FPS --
3757 字
19 分钟
MySQL 进阶学习

以下尖括号表示需要替换的名称或值,方括号表示可选部分;执行前应删除占位符。

索引#

是帮助MySQL高效获取数据的数据结构,以空间换时间

优劣 优势: 提高检索数据的效率,降低数据库的IO成本,降低CPU的消耗 劣势: 索引需要占用空间;INSERT、UPDATE、DELETE 还需维护相关索引,可能增加写入成本。

MySQL的索引在存储引擎实现的,不同引擎有不同的结构 B+树:最常见的,大部分都支持B+树索引 Hash:支持等值查找,不支持范围查找或按索引排序。MEMORY 引擎支持显式 Hash 索引,InnoDB 也可能自动构建自适应 Hash 索引。 R-树:用于空间索引;InnoDB 和 MyISAM 都支持空间索引。 全文索引:用于文本关键字检索;InnoDB 和 MyISAM 支持 FULLTEXT 索引。

索引结构#

二叉树 缺点:顺序插入时会形成一个链表,查询性能会大大降低。大数据量的情况下,层级较深,检索速度慢 Hash 通过哈希函数把键映射到槽位;发生哈希冲突时,存储引擎需要进一步比较或处理冲突。

  1. Hash索引仅支持对等比较(=,IN),不支持范围查询(between,>,<,。。。)
  2. 无法通过索引完成排序操作
  3. 等值查询通常很快,但实际性能取决于数据分布、冲突和执行计划,不能一概认为比 B+ 树快。

MySQL中的InnoDB对Hash有自适应,Hash索引是存储引擎根据B+树索引在指定条件下自动构建的

索引分类#

分类含义特点关键字
主键索引针对表中主键创建的索引默认自动创建且只能有一个PRIMARY
唯一索引避免某个字段的值出现重复可以有多个UNIQUE
常规索引快速定位特点数据可以有多个-
全文索引全文索引查找的是文本中的关键字,而不是比较索引中的值可以有多个FULLTEXT

根据存储形式划分

分类含义特点
聚集索引InnoDB 聚集索引叶子节点保存行数据每张 InnoDB 表有一个
二级索引数据与索引分开存放,叶子节点为对应行的主键可以有很多个

聚集索引选取规则:如果存在主键,主键索引就是聚集索引 如果没有主键,InnoDB 会选择所有列都为 NOT NULL 的第一个 UNIQUE 索引。 如果也没有这样的唯一索引,InnoDB 会生成隐藏的聚集索引。

由二级索引找到主键值,再到聚集索引读取行数据,称为回表查询。

索引语法#

创建索引

CREATE [UNIQUE|FULLTEXT] INDEX <INDEXNAME> ON <TABLENAME>(COLUMN,COLUMN,...);

查看索引

SHOW INDEX FROM <TABLENAME>;

删除索引

DROP INDEX <INDEXNAME> ON <TABLENAME>;

SQL性能分析#

SHOW STATUS 可查看服务器状态变量。下面的 Com_% 计数是服务器级别的语句计数,不限定当前数据库;也不等同于实际读取或修改的行数。

SHOW GLOBAL STATUS LIKE 'Com\_%';

慢查询日志 慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位秒,默认10)的所有SQL语句的日志。默认不开启。需要在配置文件中配置:slow_query_log=1 和long_query_time=10。配置完成之后重启服务器

profile详情 可以帮助了解SQL语句时间消耗 可通过 have_profiling 查看当前 MySQL 是否支持该功能:

SELECT @@have_profiling;

SHOW PROFILE 已弃用;旧版本中可在当前会话启用,新的性能分析应优先使用 Performance Schema。

SET profiling=1;
-- 查看当前会话中各语句的耗时
SHOW profiles;
-- 查看指定 query_id 的各阶段耗时
SHOW PROFILE FOR QUERY <QUERY_ID>;
-- 查看指定 query_id 的 CPU 耗时
SHOW PROFILE CPU FOR QUERY <QUERY_ID>;

explain执行计划 EXPLAIN或者DESC命令获取MySQL任何执行SELECT语句的信息,包括SELECT语句执行过程中表如何连接和连接顺序。

-- 在 SELECT 语句前加 EXPLAIN
EXPLAIN SELECT <COLUMN> FROM <TABLENAME>;

各个字段含义

  • Id
    • SELECT 的标识符;不能只凭 id 大小推断实际执行顺序。
  • select_type
    • 表示select的类型,常见的取值由simple(简单表,即不使用表连接或子查询)、Primary(主查询,即外层的查询)、UNION(UNION中的第二以及以后的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等
  • type
    • 表访问类型,常见值有 system、const、eq_ref、ref、range、index、ALL;需结合行数和过滤条件判断性能。
  • possible_keys
    • 显示可能应用在这张表上的索引,一个或多个
  • key
    • 实际使用的索引。如果为NULL,就是没有使用索引
  • key_len
    • 优化器选用的索引键长度,可帮助判断联合索引用了哪些列;不是越短越好。
  • rows
    • MySQL认为必须要执行查询的行数,在InnoDB引擎的表中是一个估计值,可能并不总是准确的
  • filtered
    • 估计经过表条件筛选后保留的行占 rows 的百分比;需结合实际执行计划判断,不能单凭数值高低评价查询。

索引的使用#

最左前缀法则 联合索引通常按最左列开始使用;跳过前导列或遇到范围条件后,后续列能否用于索引查找取决于查询条件和优化器。用 EXPLAIN 确认实际使用情况。 索引列运算 对索引列直接做函数或运算,通常会妨碍普通索引用于查找;可考虑改写条件或使用函数索引,并用 EXPLAIN 验证。 字符串不加引号 字符串值应使用引号。若比较时发生隐式类型转换,可能妨碍索引使用,具体取决于列类型和执行计划。 模糊查询 LIKE 'aa%' 有机会利用 B+ 树索引做范围查找;LIKE '%aa' 通常不能使用普通 B+ 树索引定位前缀。 or连接的条件 OR 的各条件能否用索引,取决于可用索引、数据分布和优化器选择;可检查 EXPLAIN 中是否使用 Index Merge 或全表扫描。 数据分布影响 如果MySQL评估索引比全表慢,则不使用索引 SQL提示 在SQL语句中加入一些人为的提示优化操作

-- USE INDEX
SELECT * FROM <TABLENAME> USE INDEX(<INDEXNAME>) WHERE <OPTION>;
-- IGNORE INDEX
SELECT * FROM <TABLENAME> IGNORE INDEX(<INDEXNAME>) WHERE <OPTION>;
-- FORCE INDEX
SELECT * FROM <TABLENAME> FORCE INDEX(<INDEXNAME>) WHERE <OPTION>;

覆盖索引 当查询需要的列都可从索引取得时,可避免回表;按实际查询选择列,避免无必要的 SELECT *。

前缀索引 对较长的字符串列,可只索引前缀以减少索引占用;前缀过短会降低区分度,需要按数据分布选择长度。

CREATE INDEX <INDEX_NAME> ON <TABLE_NAME> (<COLUMN_NAME>(<PREFIX_LENGTH>));

可用 COUNT(DISTINCT LEFT(列名, N)) / COUNT(*) 估计前缀的区分度。区分度越高通常越利于筛选,但实际性能仍需结合索引大小和查询计划衡量。

单列索引与联合索引 单列:一个索引只包含一列 多列:一个索引包含多列 多个条件经常一起出现时,可考虑符合列顺序的联合索引;单列索引与联合索引的取舍应依据实际查询、写入成本和 EXPLAIN 结果。

索引设计原则#

  1. 针对数据量大、且查询比较频繁的表建立索引。
  2. 对常作为查询条件、分组、排序的字段建立索引。
  3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
  4. 如果是字符串类型的索引,字符串长度较长,可以根据字段的特点建立前缀索引。
  5. 联合索引可覆盖部分常用查询并减少回表,但不一定比单列索引节省空间。
  6. 要控制索引数量,索引不是越多越好,索引越多,维护索引的代价越大,影响增删改的效率。
  7. 如果索引列不能存储NULL,在建表时候用NOT NULL限制,当优化器指定每列是否包含NULL时,它可以更好得确定哪个索引最有效。

视图#

视图保存查询定义,通常不单独存储结果数据;查询视图时会访问其底层表。

语法#

创建或修改视图

CREATE [OR REPLACE] VIEW <VIEWNAME>(NEWCOLUMNNAME1,NEWCOLUMNNAME2,...) AS <SELECT语句> [WITH [CASCADED | LOCAL] CHECK OPTION];

查询视图

-- 查看创建视图语句
SHOW CREATE VIEW <VIEWNAME>;
-- 查询视图数据
SELECT * FROM <VIEWNAME>;

删除视图

DROP VIEW <VIEWNAME>;

视图的检查选项: 使用 WITH CHECK OPTION 创建可更新视图后,经该视图执行的 INSERT 和 UPDATE 必须使新行符合视图条件。 默认是 CASCADED。若视图 A 基于视图 B:CASCADED 会递归检查底层视图条件;LOCAL 检查 A 的条件,并继续按 B 自身的检查选项判断,不会强制给 B 添加检查选项。

视图的更新#

要使得视图可以更新,视图与基表的行必须为一对一关系。如果视图包含以下任何一项,则视图无法更新:

  1. distinct
  2. group by
  3. having
  4. UNION or UNION ALL
  5. 聚合函数或窗口函数

视图的作用#

  1. 视图可以封装常用查询,减少重复书写查询条件。
  2. 可以向用户授予视图权限,并通过视图定义限制其可见的数据;权限效果还取决于视图的安全上下文和底层表权限。
  3. 视图可以帮助用户屏蔽真实表结构变化带来的影响

存储过程#

存储过程是保存在数据库中的 SQL 语句集合,可封装重复的数据库操作。

语法#

创建

CREATE PROCEDURE 存储过程名称([参数列表])
BEGIN
SQL语句;
END;

调用

CALL 名称(参数);

查看数据库的存储过程

SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_SCHEMA = '数据库名称' AND ROUTINE_TYPE = 'PROCEDURE';

查看存储过程创建语句

SHOW CREATE PROCEDURE 存储过程名称;

删除存储过程

DROP PROCEDURE [IF EXISTS] 存储过程名称;

变量#

在 mysql 命令行客户端中定义多语句存储程序时,需要临时更改语句结束符;DELIMITER 是客户端命令,不是 SQL。

-- 以下示例在 mysql 命令行客户端执行
DELIMITER //
CREATE PROCEDURE hello()
BEGIN
SELECT 'hello' AS message;
END//
DELIMITER ;
系统变量#

由MySQL数据库提供,不需要用户定义(全局变量GLOBAL,会话变量SESSION)

查看系统变量

SHOW [GLOBAL | SESSION] VARIABLES;
SHOW [GLOBAL | SESSION] VARIABLES LIKE '变量名模式';
SELECT @@SESSION.系统变量名;
SELECT @@GLOBAL.系统变量名;

设置系统变量

SET [SESSION | GLOBAL] 变量名=值;
SET @@SESSION.变量名=值;
SET @@GLOBAL.变量名=值;
用户变量#

用户根据业务需求定义的。两个@是系统变量,一个@是用户变量,用户变量不需要提前声明类型。作用域为当前会话。 用户变量无需声明;读取尚未赋值的用户变量会得到 NULL。

赋值

SET @变量名=值,@变量名=值,...;
SELECT @变量名:=值,@变量名:=值,...;
-- SELECT设置变量,必须要要用:=,SET的话:=和=都可

使用

SELECT @变量名;
局部变量#

由DECLARE声明,只能在BEGIN和END之间使用

声明

DECLARE 变量名 类型 [DEFAULT ...];

字段类型有CHAR, BIGINT, INT,VARCHAR, DATE, TIME等

赋值

SET 变量=值;
SET 变量:=值;
SELECT 字段 INTO 变量 FROM 表...;

SELECT ... INTO 应只返回一行数据;局部变量名不带 @。

IF判断#

IF 条件 THEN
...
ELSEIF 条件2 THEN
...
ELSE
...
END IF;

参数(形参)#

  • IN
    • 需要调用时输入的,是默认的
  • OUT
    • 作为返回值的参数
  • INOUT
    • 既可以作为输入,也可以作为输出

用法

-- 使用SET对参数2进行赋值
CREATE PROCEDURE 存储过程(IN 参数1 类型, OUT 参数2 类型,...)
BEGIN
SQL语句;
END;

CASE#

语法一

CASE 变量
WHEN 值1 THEN SQL语句;
WHEN 值2 THEN SQL语句;
...
ELSE SQL语句;
END CASE;

语法二

CASE
WHEN 条件1 THEN SQL语句;
WHEN 条件2 THEN SQL语句;
...
ELSE SQL语句;
END CASE;

WHILE#

WHILE 条件 DO
SQL语句;
END WHILE;

REPEAT#

有条件的循环,满足条件则退出。

REPEAT
SQL语句;
UNTIL 条件
END REPEAT;

LOOP#

可以实现简单的死循环,不在SQL中增加退出循环的条件。

LEAVE: 退出循环 ITERATE: 必须在循环中,表示跳过当前循环剩下的语句。

[开始标记]:LOOP
SQL语句;
-- 根据条件选择 LEAVE 或 ITERATE,通常不会连续执行二者
LEAVE 开始标记;
END LOOP [结束标记];

游标#

用来存储查询结果集的数据类型,可以用游标进行循环处理。游标的使用包括声明、OPEN、FETCH、CLOSE。

声明

DECLARE 游标名 CURSOR FOR 查询语句;

打开游标

OPEN 游标名;

获取游标记录

FETCH 游标名 INTO 变量1,变量2,...;

关闭游标

CLOSE 游标名;

条件处理程序#

定义在流程控制执行过程中遇到条件时的处理步骤。

DECLARE handler_action HANDLER FOR condition_value,... SQL;

handler_action:

  • EXIT 满足condition_value时退出
  • CONTINUE 满足condition_value时继续

condition_value:

  • SQLSTATE sqlstate_value:状态码,如SQLSTATE ‘02000’

常用的条件类别:

  • SQLWARNING:SQLSTATE 类别 01。
  • NOT FOUND:SQLSTATE 类别 02。
  • SQLEXCEPTION:除成功、警告和未找到之外的异常类别。

存储函数#

存储函数通过 RETURN 返回值;函数参数默认都是输入参数,声明时不写 IN。

CREATE FUNCTION add_one(n INT)
RETURNS INT DETERMINISTIC NO SQL
RETURN n + 1;

characteristic特性说明:

  • DETERMINISTIC: 相同的输入参数总是产生相同的结果
  • NO SQL:不包含SQL语句
  • READS SQL DATA:包含查询数据的语句,但不包含写入数据的语句。

启用二进制日志且 log_bin_trust_function_creators=0 时,创建函数可能要求声明 DETERMINISTIC、NO SQL 或 READS SQL DATA;应按函数的实际行为声明,不能为绕过错误而标注不真实的特性。

触发器#

触发器与表关联,在插入、更新或删除每行之前或之后执行,可用于数据校验或记录变更。 使用别名OLD和NEW来引用触发器中发生变化的记录内容。支持行级触发,不支持语句级触发。

  1. INSERT型触发器
  • NEW表示将要或者已经新增的数据
  1. UPDATE型触发器
  • OLD表示修改之前的数据,NEW表示将要或者已经修改后的数据
  1. DELETE型触发器
  • OLD表示将要或者已经删除的数据

语法#

CREATE TRIGGER 触发器名
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON 表名 FOR EACH ROW --行级触发器
BEGIN
SQL;
END;

查看

SHOW TRIGGERS;

删除触发器

DROP TRIGGER [数据库名.]触发器名;
--如果没有指定数据库则默认为当前数据库

参考资料#

MySQL 进阶学习
https://www.pardofelis.top/posts/mysql-advanced/
作者
所愿Syuan
发布于
2026-10-10
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

第 2 篇,共 3 篇本文属于系列

评论

正在加载评论...

最后更新于 ,距今已过 3 天

部分内容可能已过时