MySQL查询语句(续)
第4章:MySQL查询语句(续)
4.3 高级功能
MySQL提供了许多高级功能,可以增强数据库的性能、安全性和功能性。本节将详细介绍索引、视图、存储过程、触发器、事务等高级功能。
4.3.1 索引
索引是提高数据库查询性能的关键工具。它们类似于书籍的目录,允许数据库系统快速定位所需的数据,而无需扫描整个表。
索引的类型
MySQL支持多种类型的索引:
- B-Tree索引:最常用的索引类型,适用于全键值、键值范围和键前缀查询
- 哈希索引:只适用于等值比较,主要用于MEMORY存储引擎
- 全文索引:用于全文搜索
- 空间索引:用于地理空间数据类型
- 前缀索引:对字符串列的前几个字符建立索引
- 复合索引:包含多个列的索引
创建索引
可以在创建表时或之后创建索引:
-- 在创建表时创建索引
CREATE TABLE employees (employee_id INT PRIMARY KEY,first_name VARCHAR(50),last_name VARCHAR(50),email VARCHAR(100),hire_date DATE,INDEX idx_name (first_name, last_name),UNIQUE INDEX idx_email (email)
);-- 在现有表上创建索引
CREATE INDEX idx_hire_date ON employees (hire_date);
CREATE UNIQUE INDEX idx_emp_email ON employees (email);-- 使用ALTER TABLE创建索引
ALTER TABLE employees ADD INDEX idx_last_name (last_name);
创建前缀索引
对于长字符串列,可以只索引前几个字符:
CREATE INDEX idx_description ON products (description(50));
这将为description
列的前50个字符创建索引。
创建全文索引
全文索引用于全文搜索:
-- 在创建表时创建全文索引
CREATE TABLE articles (id INT PRIMARY KEY,title VARCHAR(200),content TEXT,FULLTEXT INDEX idx_content (content)
);-- 在现有表上创建全文索引
CREATE FULLTEXT INDEX idx_title_content ON articles (title, content);
使用全文索引进行搜索:
-- 自然语言模式
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('database optimization');-- 布尔模式
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+database -optimization' IN BOOLEAN MODE);
查看索引
可以使用以下命令查看表的索引:
-- 方法1
SHOW INDEX FROM employees;-- 方法2
SHOW INDEXES FROM employees;-- 方法3
SHOW KEYS FROM employees;
也可以从INFORMATION_SCHEMA查询索引信息:
SELECT index_name, column_name, non_unique
FROM INFORMATION_SCHEMA.STATISTICS
WHERE table_schema = 'your_database' AND table_name = 'employees';
删除索引
可以使用以下命令删除索引:
-- 方法1
DROP INDEX idx_name ON employees;-- 方法2
ALTER TABLE employees DROP INDEX idx_name;
索引的使用场景
索引适用于以下场景:
- WHERE子句中频繁使用的列
- JOIN操作中的连接列
- ORDER BY或GROUP BY子句中的列
- 包含大量唯一值的列
索引的注意事项
虽然索引可以提高查询性能,但也有一些注意事项:
- 索引会占用额外的存储空间
- 索引会降低写操作(INSERT、UPDATE、DELETE)的性能
- 不是所有查询都能使用索引
- 过多的索引可能导致优化器选择次优的执行计划
索引最佳实践
以下是一些索引使用的最佳实践:
- 为经常在WHERE子句中使用的列创建索引
- 为经常在ORDER BY和GROUP BY子句中使用的列创建索引
- 为外键列创建索引
- 考虑列的基数(唯一值的数量)
- 避免对经常更新的列创建过多索引
- 使用EXPLAIN分析查询,确保索引被正确使用
- 定期检查和优化索引
4.3.2 视图
视图是基于SQL查询的虚拟表,可以像表一样使用。视图不存储数据,而是在查询时动态生成结果。
创建视图
使用CREATE VIEW语句创建视图:
CREATE [OR REPLACE] VIEW view_name AS
SELECT column1, column2, ...
FROM table_name
WHERE condition;
示例:
-- 创建一个简单的视图
CREATE VIEW employee_details AS
SELECT e.employee_id, e.first_name, e.last_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;-- 创建带列名的视图
CREATE VIEW employee_salaries (id, name, salary, department) AS
SELECT e.employee_id, CONCAT(e.first_name, ' ', e.last_name), e.salary, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
查询视图
视图可以像表一样查询:
SELECT * FROM employee_details;SELECT id, name, department
FROM employee_salaries
WHERE salary > 10000;
修改视图
可以使用ALTER VIEW或CREATE OR REPLACE VIEW修改现有视图:
-- 方法1
ALTER VIEW employee_details AS
SELECT e.employee_id, e.first_name, e.last_name, e.email, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;-- 方法2
CREATE OR REPLACE VIEW employee_details AS
SELECT e.employee_id, e.first_name, e.last_name, e.email, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
删除视图
使用DROP VIEW语句删除视图:
DROP VIEW [IF EXISTS] employee_details;
可更新视图
在某些情况下,视图是可更新的,这意味着可以通过视图执行INSERT、UPDATE和DELETE操作:
-- 创建可更新视图
CREATE VIEW active_employees AS
SELECT employee_id, first_name, last_name, email, department_id
FROM employees
WHERE active = 1;-- 通过视图更新数据
UPDATE active_employees
SET department_id = 80
WHERE employee_id = 207;
视图可更新的条件:
- 视图必须只基于一个表
- 视图不能包含以下元素:
- 聚合函数(SUM、COUNT等)
- DISTINCT
- GROUP BY
- HAVING
- UNION
- 子查询
- 某些连接操作
- 视图必须包含基表的所有NOT NULL且没有默认值的列
带检查选项的视图
可以使用WITH CHECK OPTION子句确保通过视图进行的修改符合视图的定义条件:
CREATE VIEW active_employees AS
SELECT employee_id, first_name, last_name, email, active
FROM employees
WHERE active = 1
WITH CHECK OPTION;
使用此选项后,以下更新将失败,因为它违反了视图的WHERE条件:
UPDATE active_employees
SET active = 0
WHERE employee_id = 207;
视图的优势
视图提供以下优势:
- 简化复杂查询:将复杂查询封装为简单视图
- 提供数据抽象:隐藏底层表结构的复杂性
- 实现行级和列级安全:限制用户只能看到特定的行或列
- 提供向后兼容性:当底层表结构变化时,保持应用程序接口稳定
4.3.3 存储过程
存储过程是存储在数据库中的一组SQL语句,可以像函数一样调用。它们可以接受参数、执行操作并返回结果。
创建存储过程
使用CREATE PROCEDURE语句创建存储过程:
DELIMITER //
CREATE PROCEDURE procedure_name(parameter_list)
BEGIN-- SQL语句
END //
DELIMITER ;
参数可以是IN(输入)、OUT(输出)或INOUT(输入输出)类型:
DELIMITER //
CREATE PROCEDURE get_employee_details(IN emp_id INT,OUT emp_name VARCHAR(100),OUT emp_salary DECIMAL(10,2)
)
BEGINSELECT CONCAT(first_name, ' ', last_name), salaryINTO emp_name, emp_salaryFROM employeesWHERE employee_id = emp_id;
END //
DELIMITER ;
调用存储过程
使用CALL语句调用存储过程:
-- 调用不带参数的存储过程
CALL update_statistics();-- 调用带IN参数的存储过程
CALL increase_salaries(60, 10);-- 调用带OUT参数的存储过程
SET @name = '';
SET @salary = 0;
CALL get_employee_details(207, @name, @salary);
SELECT @name, @salary;
存储过程中的变量
可以在存储过程中声明和使用变量:
DELIMITER //
CREATE PROCEDURE calculate_bonus(IN emp_id INT)
BEGINDECLARE salary DECIMAL(10,2);DECLARE years_of_service INT;DECLARE bonus DECIMAL(10,2);-- 获取员工工资和服务年限SELECT salary, FLOOR(DATEDIFF(CURDATE(), hire_date) / 365)INTO salary, years_of_serviceFROM employeesWHERE employee_id = emp_id;-- 计算奖金IF years_of_service < 5 THENSET bonus = salary * 0.05;ELSEIF years_of_service < 10 THENSET bonus = salary * 0.10;ELSESET bonus = salary * 0.15;END IF;-- 返回结果SELECT emp_id, salary, years_of_service, bonus;
END //
DELIMITER ;
存储过程中的流程控制
MySQL存储过程支持多种流程控制语句:
- IF语句:
IF condition THEN-- statements
ELSEIF another_condition THEN-- statements
ELSE-- statements
END IF;
- CASE语句:
CASE expressionWHEN value1 THEN-- statementsWHEN value2 THEN-- statementsELSE-- statements
END CASE;-- 或CASEWHEN condition1 THEN-- statementsWHEN condition2 THEN-- statementsELSE-- statements
END CASE;
- 循环语句:
-- WHILE循环
WHILE condition DO-- statements
END WHILE;-- REPEAT循环(至少执行一次)
REPEAT-- statements
UNTIL condition
END REPEAT;-- LOOP循环(需要显式退出)
loop_label: LOOP-- statementsIF condition THENLEAVE loop_label;END IF;
END LOOP;
存储过程中的错误处理
可以使用DECLARE HANDLER语句处理存储过程中的错误:
DELIMITER //
CREATE PROCEDURE safe_update_salary(IN emp_id INT, IN new_salary DECIMAL(10,2))
BEGIN-- 声明错误处理器DECLARE EXIT HANDLER FOR SQLEXCEPTIONBEGINROLLBACK;SELECT 'Error occurred. Transaction rolled back.' AS message;END;-- 开始事务START TRANSACTION;-- 更新工资UPDATE employeesSET salary = new_salaryWHERE employee_id = emp_id;-- 检查是否找到员工IF ROW_COUNT() = 0 THENSIGNAL SQLSTATE '45000'SET MESSAGE_TEXT = 'Employee not found';END IF;-- 提交事务COMMIT;SELECT 'Salary updated successfully.' AS message;
END //
DELIMITER ;
查看存储过程
可以使用以下命令查看存储过程:
-- 查看所有存储过程
SHOW PROCEDURE STATUS WHERE db = 'your_database';-- 查看特定存储过程的定义
SHOW CREATE PROCEDURE procedure_name;
也可以从INFORMATION_SCHEMA查询存储过程信息:
SELECT routine_name, routine_definition
FROM INFORMATION_SCHEMA.ROUTINES
WHERE routine_schema = 'your_database' AND routine_type = 'PROCEDURE';
修改和删除存储过程
使用ALTER PROCEDURE修改存储过程的特性(但不能修改其内容):
ALTER PROCEDURE procedure_name
COMMENT 'New comment'
SQL SECURITY INVOKER;
要修改存储过程的内容,需要先删除再重新创建。
使用DROP PROCEDURE删除存储过程:
DROP PROCEDURE [IF EXISTS] procedure_name;
4.3.4 函数
MySQL函数类似于存储过程,但它们返回单个值,可以在SQL语句中使用。
创建函数
使用CREATE FUNCTION语句创建函数:
DELIMITER //
CREATE FUNCTION function_name(parameter_list)
RETURNS data_type
[characteristic ...]
BEGIN-- SQL语句RETURN value;
END //
DELIMITER ;
示例:
DELIMITER //
CREATE FUNCTION calculate_age(birth_date DATE)
RETURNS INT
DETERMINISTIC
BEGINRETURN FLOOR(DATEDIFF(CURDATE(), birth_date) / 365);
END //
DELIMITER ;
函数特性(characteristic)包括:
- DETERMINISTIC:函数对于相同的输入总是返回相同的结果
- NOT DETERMINISTIC:函数结果可能因外部因素而变化(默认)
- CONTAINS SQL:函数包含SQL语句,但不读取或修改数据
- NO SQL:函数不包含SQL语句
- READS SQL DATA:函数包含读取数据的语句,但不修改数据
- MODIFIES SQL DATA:函数包含修改数据的语句
使用函数
函数可以在SQL语句中使用:
-- 在SELECT语句中使用
SELECT employee_id, first_name, last_name, birth_date, calculate_age(birth_date) AS age
FROM employees;-- 在WHERE子句中使用
SELECT * FROM employees
WHERE calculate_age(birth_date) > 40;-- 直接调用函数
SELECT calculate_age('1980-01-15');
查看、修改和删除函数
查看函数:
-- 查看所有函数
SHOW FUNCTION STATUS WHERE db = 'your_database';-- 查看特定函数的定义
SHOW CREATE FUNCTION function_name;
修改函数特性(不能修改内容):
ALTER FUNCTION function_name
COMMENT 'New comment'
SQL SECURITY INVOKER;
删除函数:
DROP FUNCTION [IF EXISTS] function_name;
4.3.5 触发器
触发器是在表上执行特定操作(INSERT、UPDATE或DELETE)时自动执行的特殊存储过程。
创建触发器
使用CREATE TRIGGER语句创建触发器:
DELIMITER //
CREATE TRIGGER trigger_name
{BEFORE | AFTER} {INSERT | UPDATE | DELETE} ON table_name
FOR EACH ROW
BEGIN-- SQL语句
END //
DELIMITER ;
示例:
-- 创建BEFORE INSERT触发器
DELIMITER //
CREATE TRIGGER before_employee_insert
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN-- 设置默认值IF NEW.hire_date IS NULL THENSET NEW.hire_date = CURDATE();END IF;
END //
DELIMITER ;-- 创建AFTER UPDATE触发器
DELIMITER //
CREATE TRIGGER after_salary_update
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN-- 记录工资变更IF OLD.salary <> NEW.salary THENINSERT INTO salary_changes (employee_id, old_salary, new_salary, change_date)VALUES (NEW.employee_id, OLD.salary, NEW.salary, NOW());END IF;
END //
DELIMITER ;
在触发器中,可以使用OLD和NEW关键字引用行的旧值和新值:
- INSERT触发器只能使用NEW
- DELETE触发器只能使用OLD
- UPDATE触发器可以使用OLD和NEW
查看触发器
可以使用以下命令查看触发器:
-- 查看所有触发器
SHOW TRIGGERS;-- 查看特定表的触发器
SHOW TRIGGERS WHERE `table` = 'employees';
也可以从INFORMATION_SCHEMA查询触发器信息:
SELECT trigger_name, event_manipulation, action_statement
FROM INFORMATION_SCHEMA.TRIGGERS
WHERE event_object_table = 'employees';
删除触发器
使用DROP TRIGGER语句删除触发器:
DROP TRIGGER [IF EXISTS] trigger_name;
触发器的使用场景
触发器适用于以下场景:
- 自动更新时间戳:记录行的最后修改时间
- 数据验证:确保插入或更新的数据符合业务规则
- 维护冗余数据:自动更新相关表中的冗余数据
- 审计日志:记录数据变更
- 实现复杂的业务规则:如级联更新或删除
触发器的注意事项
使用触发器时应注意以下几点:
- 触发器可能影响性能:特别是在大量数据操作时
- 触发器可能导致复杂的调试问题:因为它们是自动执行的
- 触发器不应包含复杂的业务逻辑:复杂逻辑应放在应用程序或存储过程中
- 触发器不能调用存储过程:但可以调用函数
- 触发器不能使用显式或隐式的事务控制语句:如COMMIT或ROLLBACK
4.3.6 事件调度器
MySQL事件调度器允许自动执行计划任务,类似于Unix的cron作业。
启用事件调度器
首先,确保事件调度器已启用:
-- 检查事件调度器状态
SHOW VARIABLES LIKE 'event_scheduler';-- 启用事件调度器
SET GLOBAL event_scheduler = ON;
也可以在MySQL配置文件中启用:
[mysqld]
event_scheduler = ON
创建事件
使用CREATE EVENT语句创建事件:
DELIMITER //
CREATE EVENT event_name
ON SCHEDULE schedule
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT 'comment']
DO
BEGIN-- SQL语句
END //
DELIMITER ;
schedule可以是以下之一:
- AT timestamp:在指定时间执行一次
- EVERY interval:按指定间隔重复执行
示例:
-- 创建一次性事件
DELIMITER //
CREATE EVENT one_time_event
ON SCHEDULE AT '2023-12-31 23:59:59'
DO
BEGININSERT INTO year_end_summary (year, total_sales)SELECT YEAR(order_date), SUM(amount)FROM ordersWHERE YEAR(order_date) = 2023;
END //
DELIMITER ;-- 创建重复事件
DELIMITER //
CREATE EVENT daily_cleanup
ON SCHEDULE EVERY 1 DAY
STARTS '2023-01-01 01:00:00'
DO
BEGINDELETE FROM temp_logsWHERE created_at < DATE_SUB(NOW(), INTERVAL 7 DAY);
END //
DELIMITER ;
查看事件
可以使用以下命令查看事件:
-- 查看所有事件
SHOW EVENTS;-- 查看特定数据库的事件
SHOW EVENTS FROM your_database;
也可以从INFORMATION_SCHEMA查询事件信息:
SELECT event_name, event_definition, interval_value, interval_field, status
FROM INFORMATION_SCHEMA.EVENTS
WHERE event_schema = 'your_database';
修改事件
使用ALTER EVENT语句修改事件:
-- 修改事件执行计划
ALTER EVENT daily_cleanup
ON SCHEDULE EVERY 12 HOUR;-- 禁用事件
ALTER EVENT daily_cleanup
DISABLE;-- 启用事件
ALTER EVENT daily_cleanup
ENABLE;
删除事件
使用DROP EVENT语句删除事件:
DROP EVENT [IF EXISTS] event_name;
事件的使用场景
事件调度器适用于以下场景:
- 定期数据清理:删除旧日志或临时数据
- 定期数据聚合:计算和存储统计信息
- 定期维护任务:优化表、更新统计信息
- 生成定期报告:每日、每周或每月报告
- 数据归档:将旧数据移动到归档表
4.3.7 分区
分区是将大表分成更小、更易管理的部分的技术。MySQL支持多种分区类型。
分区类型
MySQL支持以下分区类型:
- RANGE分区:基于连续范围的分区
- LIST分区:基于离散值列表的分区
- HASH分区:基于哈希函数的分区
- KEY分区:类似于HASH,但使用MySQL的内部哈希函数
创建分区表
使用PARTITION BY子句创建分区表:
-- RANGE分区
CREATE TABLE sales (id INT NOT NULL,sale_date DATE NOT NULL,amount DECIMAL(10,2),PRIMARY KEY (id, sale_date) -- 包含分区键
)
PARTITION BY RANGE (YEAR(sale_date)) (PARTITION p0 VALUES LESS THAN (2020),PARTITION p1 VALUES LESS THAN (2021),PARTITION p2 VALUES LESS THAN (2022),PARTITION p3 VALUES LESS THAN (2023),PARTITION p4 VALUES LESS THAN MAXVALUE
);-- LIST分区
CREATE TABLE employees (id INT NOT NULL,name VARCHAR(50),department VARCHAR(20),PRIMARY KEY (id, department) -- 包含分区键
)
PARTITION BY LIST (department) (PARTITION p_sales VALUES IN ('Sales', 'Marketing'),PARTITION p_tech VALUES IN ('IT', 'Engineering'),PARTITION p_admin VALUES IN ('HR', 'Finance', 'Admin')
);-- HASH分区
CREATE TABLE orders (id INT NOT NULL,customer_id INT,order_date DATE,PRIMARY KEY (id)
)
PARTITION BY HASH (id)
PARTITIONS 4;-- KEY分区
CREATE TABLE customers (id INT NOT NULL,name VARCHAR(50),region VARCHAR(50),PRIMARY KEY (id)
)
PARTITION BY KEY (id)
PARTITIONS 4;
子分区
可以进一步将分区分成子分区:
CREATE TABLE sales (id INT NOT NULL,sale_date DATE NOT NULL,amount DECIMAL(10,2),region VARCHAR(10),PRIMARY KEY (id, sale_date, region)
)
PARTITION BY RANGE (YEAR(sale_date))
SUBPARTITION BY KEY (region)
SUBPARTITIONS 2 (PARTITION p0 VALUES LESS THAN (2020),PARTITION p1 VALUES LESS THAN (2021),PARTITION p2 VALUES LESS THAN (2022)
);
管理分区
可以使用ALTER TABLE语句管理分区:
-- 添加分区
ALTER TABLE sales
ADD PARTITION (PARTITION p5 VALUES LESS THAN (2024));-- 删除分区
ALTER TABLE sales
DROP PARTITION p0;-- 重组分区
ALTER TABLE sales
REORGANIZE PARTITION p1, p2 INTO (PARTITION p12 VALUES LESS THAN (2022)
);-- 拆分分区
ALTER TABLE sales
REORGANIZE PARTITION p4 INTO (PARTITION p4 VALUES LESS THAN (2023),PARTITION p5 VALUES LESS THAN MAXVALUE
);
分区修剪
分区修剪是MySQL优化器的一个功能,它只扫描包含查询数据的分区,而不是整个表:
-- 这个查询只会扫描p1分区
SELECT * FROM sales
WHERE sale_date BETWEEN '2020-01-01' AND '2020-12-31';
查看分区信息
可以使用以下命令查看分区信息:
-- 查看表的分区信息
SHOW CREATE TABLE sales;-- 查看分区详细信息
SELECT partition_name, partition_method, partition_expression
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE table_name = 'sales';-- 查看每个分区的行数
SELECT partition_name, table_rows
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE table_name = 'sales';
分区的优势
分区提供以下优势:
- 提高查询性能:通过分区修剪减少扫描的数据量
- 简化数据管理:可以单独管理每个分区
- 优化维护操作:可以对单个分区执行维护操作
- 提高可用性:一个分区的问题不会影响其他分区
分区的限制
分区也有一些限制:
- 分区键必须是主键的一部分
- 不支持外键约束
- 某些存储引擎不支持分区
- 分区数量有限制(最多1024个分区)
4.3.8 全文搜索
MySQL支持全文搜索,允许基于文本内容的复杂搜索。
创建全文索引
可以在创建表时或之后创建全文索引:
-- 在创建表时创建全文索引
CREATE TABLE articles (id INT PRIMARY KEY,title VARCHAR(200),content TEXT,FULLTEXT INDEX idx_content (content)
);-- 在现有表上创建全文索引
CREATE FULLTEXT INDEX idx_title_content ON articles (title, content);
全文搜索模式
MySQL支持三种全文搜索模式:
- 自然语言模式:搜索包含任何指定词的文档
- 布尔模式:使用特殊运算符进行更复杂的搜索
- 查询扩展模式:基于初始搜索结果扩展搜索
自然语言模式
自然语言模式是默认模式,它搜索包含任何指定词的文档:
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('database optimization');
这将返回包含"database"或"optimization"的文章,按相关性排序。
布尔模式
布尔模式允许使用特殊运算符进行更复杂的搜索:
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('database optimization' IN BOOLEAN MODE);
布尔模式运算符:
+
:词必须存在-
:词不能存在*
:通配符"
:精确短语()
:分组>
:增加相关性<
:减少相关性
示例:
-- 必须包含"database",可能包含"optimization"
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+database optimization' IN BOOLEAN MODE);-- 必须包含"database",不能包含"mysql"
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+database -mysql' IN BOOLEAN MODE);-- 必须包含精确短语"database optimization"
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('"database optimization"' IN BOOLEAN MODE);-- 包含以"data"开头的词
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('data*' IN BOOLEAN MODE);
查询扩展模式
查询扩展模式执行两次搜索:第一次使用原始搜索词,第二次使用第一次搜索中找到的相关词:
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('database' WITH QUERY EXPANSION);
这可能会返回不包含"database"但包含相关词(如"MySQL"、"SQL"等)的文章。
全文搜索配置
可以配置全文搜索的行为:
-- 最小词长度(默认为3)
SET GLOBAL innodb_ft_min_token_size = 2;-- 停用词(不索引的常见词)
SET GLOBAL innodb_ft_server_stopword_table = 'database/stopwords';
注意:修改这些设置后,需要重建全文索引才能生效。
4.3.9 JSON支持
MySQL 5.7.8及以上版本支持原生JSON数据类型,提供了存储和操作JSON数据的功能。
创建JSON列
可以在创建表时定义JSON列:
CREATE TABLE products (id INT PRIMARY KEY,name VARCHAR(100),attributes JSON
);
插入JSON数据
可以使用JSON字面量或JSON_OBJECT()函数插入JSON数据:
-- 使用JSON字面量
INSERT INTO products VALUES (1, 'Laptop', '{"color": "black", "weight": 1.5, "features": ["backlit keyboard", "touch screen"]}');-- 使用JSON_OBJECT()函数
INSERT INTO products VALUES (2, 'Phone', JSON_OBJECT('color', 'white', 'weight', 0.3, 'features', JSON_ARRAY('water resistant', 'dual camera')));
查询JSON数据
可以使用JSON函数和运算符查询JSON数据:
-- 使用->运算符(返回JSON值)
SELECT id, name, attributes->'$.color' AS color FROM products;-- 使用->>运算符(返回字符串值)
SELECT id, name, attributes->>'$.color' AS color FROM products;-- 访问数组元素
SELECT id, name, attributes->'$.features[0]' AS first_feature FROM products;-- 使用JSON_EXTRACT()函数
SELECT id, name, JSON_EXTRACT(attributes, '$.color') AS color FROM products;
修改JSON数据
可以使用JSON函数修改JSON数据:
-- 更新整个JSON值
UPDATE products
SET attributes = '{"color": "silver", "weight": 1.5, "features": ["backlit keyboard", "touch screen", "fingerprint reader"]}'
WHERE id = 1;-- 更新特定路径
UPDATE products
SET attributes = JSON_SET(attributes, '$.color', 'silver', '$.features[2]', 'fingerprint reader')
WHERE id = 1;-- 插入新属性
UPDATE products
SET attributes = JSON_INSERT(attributes, '$.price', 999.99)
WHERE id = 1;-- 替换属性
UPDATE products
SET attributes = JSON_REPLACE(attributes, '$.weight', 1.6)
WHERE id = 1;-- 删除属性
UPDATE products
SET attributes = JSON_REMOVE(attributes, '$.weight')
WHERE id = 1;
JSON函数
MySQL提供了许多JSON函数:
-
构造函数:
JSON_ARRAY()
:创建JSON数组JSON_OBJECT()
:创建JSON对象JSON_QUOTE()
:将字符串引用为JSON字符串
-
查询函数:
JSON_EXTRACT()
:提取JSON值JSON_CONTAINS()
:检查JSON文档是否包含特定值JSON_CONTAINS_PATH()
:检查JSON文档是否包含特定路径JSON_KEYS()
:返回JSON对象的键JSON_SEARCH()
:查找JSON值中的字符串
-
修改函数:
JSON_INSERT()
:插入值(如果路径不存在)JSON_SET()
:插入或更新值JSON_REPLACE()
:更新值(如果路径存在)JSON_REMOVE()
:删除值JSON_ARRAY_APPEND()
:将值追加到JSON数组JSON_ARRAY_INSERT()
:将值插入JSON数组
-
实用函数:
JSON_TYPE()
:返回JSON值的类型JSON_LENGTH()
:返回JSON文档的长度JSON_VALID()
:检查值是否为有效的JSONJSON_PRETTY()
:格式化JSON文档以提高可读性
JSON索引
虽然不能直接索引JSON列,但可以创建虚拟列并索引它们:
-- 添加虚拟列
ALTER TABLE products
ADD COLUMN color VARCHAR(50) GENERATED ALWAYS AS (attributes->>'$.color') VIRTUAL,
ADD COLUMN weight DECIMAL(5,2) GENERATED ALWAYS AS (attributes->>'$.weight') VIRTUAL;-- 创建索引
CREATE INDEX idx_color ON products(color);
CREATE INDEX idx_weight ON products(weight);
然后可以使用这些索引进行高效查询:
SELECT * FROM products
WHERE color = 'black' AND weight < 1.0;
4.3.10 性能优化
MySQL性能优化是一个广泛的主题,涉及多个方面。以下是一些关键的优化技术:
查询优化
- 使用EXPLAIN分析查询:
EXPLAIN SELECT * FROM employees
WHERE department_id = 60 AND salary > 10000;
EXPLAIN输出包含以下重要信息:
select_type
:查询类型(SIMPLE、PRIMARY、SUBQUERY等)table
:表名type
:连接类型(system、const、eq_ref、ref、range、index、ALL等)possible_keys
:可能使用的索引key
:实际使用的索引key_len
:索引键的长度ref
:与索引比较的列rows
:估计要检查的行数filtered
:按表条件过滤的行百分比Extra
:附加信息
-
优化索引使用:
- 为WHERE、JOIN、ORDER BY和GROUP BY子句中的列创建索引
- 使用复合索引时,考虑列的顺序
- 避免在索引列上使用函数
-
优化JOIN操作:
- 确保JOIN条件有索引
- 使用适当的JOIN类型(INNER、LEFT、RIGHT)
- 考虑表的连接顺序
-
优化子查询:
- 考虑使用JOIN替代某些子查询
- 使用EXISTS而不是IN(对于大型子查询)
-
使用LIMIT限制结果集大小
服务器配置优化
-
内存配置:
innodb_buffer_pool_size
:InnoDB缓冲池大小(通常设置为系统内存的50-80%)key_buffer_size
:MyISAM键缓存大小query_cache_size
:查询缓存大小(MySQL 8.0已移除)sort_buffer_size
:排序缓冲区大小join_buffer_size
:连接缓冲区大小
-
并发配置:
max_connections
:最大连接数thread_cache_size
:线程缓存大小innodb_thread_concurrency
:InnoDB线程并发数
-
日志配置:
innodb_log_file_size
:InnoDB日志文件大小innodb_log_buffer_size
:InnoDB日志缓冲区大小innodb_flush_log_at_trx_commit
:事务提交时的日志刷新策略
表优化
-
选择适当的存储引擎:
- InnoDB:支持事务、外键、行级锁定
- MyISAM:简单、快速、不支持事务
- MEMORY:内存表,速度快但不持久
-
表结构优化:
- 使用适当的数据类型
- 避免使用NULL(除非必要)
- 考虑表分区
- 使用适当的字符集和排序规则
-
表维护:
- 定期优化表:
OPTIMIZE TABLE
- 分析表:
ANALYZE TABLE
- 检查表:
CHECK TABLE
- 修复表:
REPAIR TABLE
- 定期优化表:
监控和诊断
-
性能模式(Performance Schema):
- 启用性能模式:
SET GLOBAL performance_schema = ON;
- 查询性能模式表获取性能信息
- 启用性能模式:
-
慢查询日志:
- 启用慢查询日志:
SET GLOBAL slow_query_log = 1;
- 设置慢查询阈值:
SET GLOBAL long_query_time = 1;
- 分析慢查询日志:使用
mysqldumpslow
工具
- 启用慢查询日志:
-
状态变量:
- 查看全局状态:
SHOW GLOBAL STATUS;
- 查看会话状态:
SHOW SESSION STATUS;
- 查看特定变量:
SHOW STATUS LIKE 'Com_%';
- 查看全局状态:
-
系统变量:
- 查看全局变量:
SHOW GLOBAL VARIABLES;
- 查看会话变量:
SHOW SESSION VARIABLES;
- 查看特定变量:
SHOW VARIABLES LIKE 'innodb_%';
- 查看全局变量:
缓存优化
-
查询缓存(MySQL 8.0已移除):
- 启用查询缓存:
SET GLOBAL query_cache_type = 1;
- 设置查询缓存大小:
SET GLOBAL query_cache_size = 67108864;
- 启用查询缓存:
-
InnoDB缓冲池:
- 设置缓冲池大小:
SET GLOBAL innodb_buffer_pool_size = 1073741824;
- 设置缓冲池实例数:
SET GLOBAL innodb_buffer_pool_instances = 8;
- 设置缓冲池大小:
-
表缓存:
- 设置表打开缓存:
SET GLOBAL table_open_cache = 2000;
- 设置表定义缓存:
SET GLOBAL table_definition_cache = 1400;
- 设置表打开缓存:
通过掌握这些高级功能和优化技术,您可以充分利用MySQL的强大功能,构建高性能、可靠的数据库应用程序。这些功能不仅可以提高数据库的性能和可靠性,还可以简化开发和维护工作。