110人参与 • 2026-05-12 • Mysql
mysql采用分层插件式架构,核心分为4层,存储引擎是整个架构的核心,决定了数据的存储、读取、事务、锁等核心能力。
层级 | 核心组件 | 核心作用 | 和存储引擎的关系 |
连接层 | 客户端连接器、连接池 | 负责客户端连接接入、身份认证、线程复用、连接数限制、内存校验 | 所有sql请求的入口,和引擎无直接交互,只负责连接管理 |
服务层 | sql接口、解析器、查询优化器、缓存 | 核心sql处理层:负责sql语法解析、权限校验、生成执行计划、存储过程/视图/触发器等跨引擎功能 | 生成执行计划后,调用存储引擎的接口读写数据,不关心底层存储实现 |
引擎层 | 可插拔存储引擎(innodb/myisam/memory等) | 数据存储与提取的底层实现:负责数据落盘、索引管理、事务、锁、崩溃恢复等核心能力 | 是sql执行的最终执行者,不同引擎的执行逻辑、性能、特性完全不同 |
存储层 | 系统文件、数据/索引/日志文件 | 负责将数据、索引、事务日志持久化到磁盘文件系统 | 存储引擎最终将数据写入该层的磁盘文件 |
存储引擎是mysql中负责数据存储、索引建立、数据增删改查的底层技术实现,也被称为「表类型」。
和其他数据库(oracle、postgresql)最大的区别是:mysql的存储引擎是基于表生效,而非基于数据库,支持同一个库中不同的表使用不同的存储引擎,实现业务能力的灵活适配。

-- 1. 业务核心表,指定innodb引擎(mysql5.5+默认,可省略不写)
create table tb_user (
id int primary key auto_increment comment '主键id',
name varchar(50) not null comment '用户名',
age int comment '年龄',
profession varchar(50) comment '职业'
) engine = innodb default charset = utf8mb4 comment '用户核心表';
-- 2. 只读历史归档表,指定myisam引擎
create table tb_user_history_2024 (
id int primary key,
name varchar(50) not null,
create_time datetime
) engine = myisam default charset = utf8mb4 comment '2024年用户历史归档表';
-- 3. 临时缓存表,指定memory引擎
create table tb_temp_online_user (
user_id int primary key,
login_time datetime not null
) engine = memory default charset = utf8mb4 comment '用户在线状态临时表';-- 查看当前mysql支持的所有存储引擎 show engines; -- 查看当前数据库的默认存储引擎 show variables like 'default_storage_engine'; -- 查看指定表的存储引擎 show create table tb_user; -- 查看当前库所有表的引擎信息 select table_name, engine from information_schema.tables where table_schema = database();
重点讲解3个主流引擎,覆盖99%的业务场景,同时补充底层结构细节,衔接之前的索引优化知识。
兼顾高可靠性和高性能的事务型通用引擎,是企业级开发的默认选择。
核心特点
每张innodb表对应一个 xxx.ibd 独立表空间文件,存储表结构、数据、索引,由参数 innodb_file_per_table 控制(默认开启)。

表空间(tablespace) → 段(segment) → 区(extent) → 页(page) → 行(row)
非事务型读密集引擎,仅适用于极少数特殊场景。
核心特点
每张myisam表对应3个磁盘文件:
xxx.sdi:表结构信息文件xxx.myd:数据文件(mydata)xxx.myi:索引文件(myindex)数据和索引完全分离存储,无法实现覆盖索引免回表的优化。
数据全量存储在内存中的临时引擎,仅适用于临时缓存场景。
核心特点
仅对应一个 xxx.sdi 表结构文件,无数据持久化文件。
核心特性 | innodb | myisam | memory |
|---|---|---|---|
事务安全 | 支持 | 不支持 | 不支持 |
锁机制 | 行级锁 | 表级锁 | 表级锁 |
b+树索引 | 支持 | 支持 | 支持 |
hash索引 | 不支持 | 不支持 | 支持(默认) |
全文索引 | 5.6+ 支持 | 支持 | 不支持 |
外键约束 | 支持 | 不支持 | 不支持 |
数据持久化 | 磁盘持久化 | 磁盘持久化 | 不持久化,内存存储 |
崩溃数据安全 | 支持崩溃恢复 | 不支持 | 完全丢失 |
并发能力 | 极高 | 极低 | 低 |
核心原则:没有最好的引擎,只有最适合业务场景的引擎,无特殊需求优先默认innodb,避免不必要的兼容问题。
索引(index)是帮助mysql高效获取数据的有序数据结构。数据库系统在业务数据之外,额外维护了一套满足特定查找算法的数据结构,该结构通过指针指向真实数据,从而实现高级查找算法,大幅降低数据检索的成本。
核心本质:用空间换时间,通过额外的磁盘空间、数据维护成本,换取查询效率的指数级提升。
索引的核心价值,是把低效的全表扫描,转为高效的索引查找,对应示例sql:
select * from user where age = 45;
核心优势 | 核心劣势 |
|---|---|
1. 大幅提升数据检索效率,减少磁盘io次数,是索引最核心的价值 | 1. 空间成本:索引需要占用额外的磁盘空间,索引总大小甚至可能超过数据本身 |
2. 利用索引的有序性,直接避免额外排序操作,降低cpu消耗(可避免 | 2. 维护成本:insert/update/delete操作时,需要同步维护索引结构保证有序性,索引越多,写操作性能损耗越大 |
3. 将随机io转为顺序io,大幅提升磁盘读写效率 | 3. 优化成本:过多索引会增加查询优化器的选择成本,可能导致执行计划选错索引 |
mysql的索引在存储引擎层实现,而非服务层,因此不同存储引擎支持的索引类型、结构、实现逻辑完全不同,这也是索引和表类型强绑定的核心原因。
主流存储引擎的索引支持情况如下:
索引类型 | innodb(默认引擎) | myisam | memory |
b+tree索引 | 支持(默认、核心索引结构) | 支持 | 支持 |
hash索引 | 不支持(仅系统自适应hash,无法手动创建) | 不支持 | 支持(默认索引类型) |
r-tree(空间索引) | 不支持 | 支持 | 不支持 |
full-text(全文索引) | 5.6版本之后支持 | 支持 | 不支持 |
日常业务开发中,99%的场景都是基于innodb引擎的b+tree索引,这也是后续所有索引优化的核心基础。
-- 1. 创建表时同时创建索引
create table tb_user (
id int primary key auto_increment comment '主键id',
name varchar(50) not null comment '用户名',
age int comment '年龄',
profession varchar(50) comment '职业',
-- 普通单列索引
index idx_user_age (age),
-- 联合索引(对应之前学习的最左前缀原则)
index idx_user_pro_age (profession, age)
) engine = innodb default charset = utf8mb4 comment '用户表';
-- 2. 查看表的所有索引
show index from tb_user;
-- 3. 给已存在的表添加索引
create index idx_user_name on tb_user(name);
-- 4. 删除索引
drop index idx_user_name on tb_user;数据库查询的最大性能瓶颈是磁盘io(磁盘io耗时是内存操作的上万倍),因此索引结构的设计核心目标是:尽量减少磁盘io次数,也就是降低树的高度。
下面我们从演进逻辑,讲清楚为什么mysql最终选择b+tree作为默认索引结构。

在二叉树基础上,通过自旋、变色保证树的平衡,解决了链表退化问题。
核心缺陷:
依然是二叉树结构,每个节点最多2个子节点,大数据量下树的高度依然很高,无法解决磁盘io过多的核心问题,因此不适合作为mysql的索引结构。
为了解决树的高度问题,b-tree(多路平衡查找树)被设计出来,核心是多路结构:一个节点可以存储多个key和多个子节点指针,大幅降低树的高度。

以5阶b-tree为例:每个节点最多存储4个key、5个子节点指针,所有节点都存储key、数据、指针,整棵树全局有序。
核心特点

b+tree是b-tree的优化版,完美解决了b-tree的缺陷,是innodb引擎默认的索引结构。
mysql在经典b+tree的基础上,做了关键优化:将叶子节点的单向链表,升级为双向循环链表,在叶子节点上增加了向前、向后的双向指针。

这个优化的核心价值:
age between 20 and 30,只需定位到age=20的叶子节点,即可顺着双向链表向后扫描,无需回溯父节点,无额外io。using temporary的底层原因。为什么b+tree是mysql最适合的索引结构?
树高低、io少、查询性能稳定,天生适配范围、排序、分组查询,是所有sql优化的底层基础。
-- 1. 无索引查询,查看执行计划(全表扫描 type: all) explain select * from tb_user where age = 25; -- 2. 创建age字段索引 create index idx_user_age on tb_user(age); -- 3. 再次查看执行计划(走索引 type: ref) explain select * from tb_user where age = 25; -- 4. 范围查询,利用b+tree双向链表优化 explain select * from tb_user where age between 20 and 30 order by age;

hash索引底层基于哈希表(散列表)实现,核心逻辑如下:
举个例子:对name字段建立hash索引,当查询name='arm'时,mysql会对'arm'计算hash值,直接定位到对应的槽位,无需遍历树结构,一步找到数据。
核心优势 | 核心劣势 |
等值查询(=、in)性能极高,无hash冲突时仅需一次io,时间复杂度o(1),理想情况下性能优于b+tree索引 | 仅支持等值匹配,完全不支持范围查询(between、>、<、>=、<=),因为hash值是无序的,无法通过hash值判断大小范围 |
无法利用索引完成排序操作,hash值的大小和原字段值的大小无任何关联,无法利用hash索引实现order by排序 | |
不支持模糊查询(like)、最左前缀匹配原则,无法实现部分匹配查询 | |
hash冲突严重时(大量重复值),需要遍历链表比对,查询性能会大幅下降 |
mysql的索引可以从功能维度和innodb存储形式维度两大维度进行完整分类,其中innodb的聚集/二级索引分类是核心中的核心,直接决定了sql的查询性能。
这是最基础的索引分类,对应创建索引时的语法关键字,共分为4大类:
分类 | 核心含义 | 核心特点 | 语法关键字 | 实操示例 |
|---|---|---|---|---|
主键索引 | 针对表的主键字段创建的索引,也叫聚簇索引 | 1. 表创建主键时,mysql会自动创建主键索引,无需手动创建; 2. 一张表有且仅有一个主键索引;3. 索引列不允许为null,不允许重复 | primary |
|
唯一索引 | 针对需要避免重复值的字段创建的索引,保证字段值在表中唯一 | 1. 一张表可以创建多个唯一索引 2. 索引列值必须唯一,但允许为null(可以有多个null); 3. 插入/更新时会校验唯一性,重复值会报错 | unique |
|
常规索引(普通索引) | 最基础的索引,仅用于快速定位特定数据,无唯一性约束 | 1. 一张表可以创建多个常规索引,是业务中最常用的索引类型; 2. 无唯一性约束,允许重复值、null值; 3. 仅用于提升查询效率,无额外约束 | index / key |
|
全文索引 | 针对长文本字段(varchar/text)创建的索引,用于关键词全文检索 | 1. 底层是倒排索引,和es、lucene核心原理一致; 2. 解决 3. 一张表可以创建多个全文索引,仅支持文本类型字段 | fulltext |
|
在innodb存储引擎中,根据索引的存储形式和数据关联方式,索引分为聚集索引(clustered index)和二级索引(secondary index,也叫辅助索引/非聚集索引)两大类,这是innodb索引最核心的设计。
核心定义
聚集索引是将索引结构和完整的行数据存储在一起的索引,b+tree的叶子节点直接存储了整行的完整数据,一张表有且仅有一个聚集索引。
聚集索引的选取规则(优先级从高到低)
rowid,作为默认的隐藏聚集索引。核心建议:业务中所有innodb表必须显式创建主键,且优先使用自增int/bigint主键,保证聚集索引的有序性,避免页分裂带来的性能损耗。
核心特点
核心定义
二级索引是将索引和行数据分开存储的索引,b+tree的叶子节点不存储完整行数据,仅存储对应的主键值。一张表可以创建多个二级索引,我们手动创建的唯一索引、常规索引、联合索引,都属于二级索引。
核心特点

核心定义
回表查询,就是先通过二级索引定位到主键值,再拿着主键值到聚集索引中查询完整行数据的过程,需要两次b+tree查询,性能低于直接走聚集索引的查询。
执行流程:
name字段的二级索引,在b+tree中找到name='arm'对应的叶子节点,拿到主键值id=10;id=10,走聚集索引的b+tree,找到对应的叶子节点,拿到整行的完整数据;高频面试题解答
问题:以下两条sql,哪个执行效率高?为什么?
select * from user where id = 10;(id为主键)select * from user where name = 'arm';(name有二级索引)答案:第一条sql执行效率远高于第二条。
原因:
mysql索引的核心操作分为创建、查看、删除三类,所有操作均基于表级别生效,语法适配普通索引、唯一索引、全文索引、联合索引等所有索引类型,是索引优化的基础操作。
create [unique | fulltext] index index_name on table_name (index_col_name [长度], ...);
参数 | 可选/必填 | 核心说明 |
| 可选 | 声明创建唯一索引,保证索引列的值全局唯一,允许存在多个null值 |
| 可选 | 声明创建全文索引,仅支持text/varchar长文本字段,用于关键词模糊检索 |
| 必填 | 索引名称,建议遵循命名规范: |
| 必填 | 要创建索引的目标表名 |
| 必填 | 要创建索引的字段,多个字段用逗号分隔即为联合索引,字段顺序直接决定最左前缀匹配规则;字符串字段可指定索引前缀长度,减少索引体积 |
场景1:建表时同步创建索引
建表时直接定义索引,可避免数据量大后创建索引的性能损耗,对应之前的索引分类:
create table tb_user (
id bigint primary key auto_increment comment '主键id(主键索引,自动创建)',
name varchar(50) not null comment '姓名',
phone char(11) not null comment '手机号',
profession varchar(50) comment '职业',
age tinyint comment '年龄',
status tinyint default 1 comment '状态',
email varchar(100) comment '邮箱',
-- 普通索引:name字段值可重复
index idx_user_name (name),
-- 唯一索引:phone字段非空且唯一
unique index idx_user_phone (phone),
-- 联合索引:profession、age、status,遵循最左前缀原则
index idx_user_pro_age_sta (profession, age, status),
-- 普通索引:email字段提升查询效率
index idx_user_email (email)
) engine = innodb default charset = utf8mb4 comment '用户表';场景2:已存在的表创建索引
-- 需求1:name字段值可能重复,创建普通索引 create index idx_user_name on tb_user(name); -- 需求2:phone字段非空且唯一,创建唯一索引 create unique index idx_user_phone on tb_user(phone); -- 需求3:为profession、age、status创建联合索引 create index idx_user_pro_age_sta on tb_user(profession, age, status); -- 需求4:为email建立索引提升查询效率 create index idx_user_email on tb_user(email);
用于查看表中已创建的所有索引的详细信息,包括索引类型、关联字段、是否唯一、索引长度等,是索引排查的核心命令。
show index from table_name;
-- 查看tb_user表的所有索引 show index from tb_user;
执行后可获取关键信息:
key_name:索引名称,主键索引固定为primarycolumn_name:索引关联的字段名non_unique:是否允许重复值,0=唯一索引,1=普通索引seq_in_index:字段在联合索引中的顺序,从1开始,对应最左前缀原则index_type:索引类型,innodb默认为btree(b+tree)用于删除无用、冗余的索引,减少写操作的性能损耗和磁盘空间占用。
drop index index_name on table_name;
-- 删除tb_user表中的idx_user_email索引 drop index idx_user_email on tb_user; -- 特殊:删除主键索引(一张表仅有一个主键索引) alter table tb_user drop primary key;
sql优化的核心原则是先定位瓶颈,再针对性优化,mysql提供了从全局到单条sql的全链路性能分析工具,可精准定位慢sql、性能损耗点,是索引优化的前提。
用于查看数据库全局的增删改查(insert/update/delete/select)执行频次,判断数据库的读写压力模型,是宏观性能分析的第一步。
-- 查看全局sql执行频次(mysql启动以来的累计值) show global status like 'com_______'; -- 查看当前会话的sql执行频次 show session status like 'com_______';
global:查看mysql服务启动以来的全局累计执行次数,用于分析整体业务模型session:查看当前数据库连接的会话执行次数,用于测试单条/一组sql的执行情况com_select:select语句累计执行次数,反映读压力com_insert:insert语句累计执行次数com_update:update语句累计执行次数com_delete:delete语句累计执行次数com_select占比极高,说明数据库是读多写少模型,核心优化方向是索引优化、查询缓存、读写分离慢查询日志是mysql内置的日志功能,会记录所有执行时间超过指定阈值的sql语句,是定位线上慢sql的核心工具。
核心特点
需修改mysql的配置文件(linux为/etc/my.cnf,windows为my.ini),添加以下配置:
# 开启慢查询日志开关 1=开启 0=关闭 slow_query_log = 1 # 慢sql时间阈值,单位:秒,执行时间超过2秒的sql会被记录 long_query_time = 2 # 慢查询日志文件存储路径 slow_query_log_file = /var/lib/mysql/localhost-slow.log
配置完成后,重启mysql服务生效:
# linux重启命令 systemctl restart mysqld
long_query_time阈值的sql都会自动写入日志文件profile工具可以精准查看单条sql执行时,每个阶段的耗时、cpu占用等细节,可定位sql到底慢在哪个环节(如io、排序、锁等待、优化器解析等),是微观性能分析的核心工具。
1. 查看是否支持profile功能
select @@have_profiling;
结果为yes表示支持,no表示不支持,主流mysql版本均支持。
2. 开启profile功能
默认关闭,需在当前会话手动开启,仅对当前会话生效:
-- 开启profile,1=开启 0=关闭 set profiling = 1;
3. 查看sql执行耗时概览
执行业务sql后,通过以下命令查看所有sql的执行耗时:
-- 查看当前会话所有sql的执行耗时、query_id show profiles;
结果会返回每条sql的query_id、执行时长、sql语句,可快速定位耗时最高的sql。
4. 查看指定sql的全阶段耗时详情
通过query_id查看单条sql每个执行阶段的耗时,定位瓶颈:
-- 查看query_id为1的sql,每个执行阶段的耗时 show profile for query 1; -- 进阶:同时查看cpu占用情况,精准定位cpu瓶颈 show profile cpu for query 1;
通过profile可定位常见的性能瓶颈:
sending data:数据读取、传输耗时高,通常是全表扫描、无索引导致creating sort index:排序耗时高,通常是order by字段无索引,引发using filesortcreating tmp table:创建临时表耗时高,通常是group by字段无索引,引发using temporaryexplain(或desc)是sql优化最核心的工具,可获取mysql优化器生成的sql执行计划,查看sql是否走索引、走哪个索引、是否回表、是否全表扫描、表连接顺序等核心信息,是索引优化的必备工具。
直接在select查询语句前添加explain关键字即可:
explain select 字段列表 from 表名 where 条件 [group by 字段 order by 字段]; -- 简写方式,效果完全一致 desc select 字段列表 from 表名 where 条件;
-- 查看主键查询的执行计划 explain select * from tb_user where id = 1; -- 查看二级索引查询的执行计划 explain select * from tb_user where name = '张三'; -- 查看联合索引查询的执行计划 explain select * from tb_user where profession = '软件工程' and age = 25;
执行计划返回12个字段,核心高频字段如下,按重要优先级排序:
字段名 | 核心含义 | 重点分析规则 |
| select查询的序列号,标识sql中表的执行顺序 | 1. id相同,执行顺序从上到下; 2. id不同,id值越大,越先执行(子查询优先执行) |
| select查询的类型 | 常见取值:
|
| 索引访问类型,反映sql性能的核心指标,性能从好到坏排序: | 核心优化目标: 1. 至少达到 2. 避免出现
|
| 本次查询中,可能用到的索引(候选索引) | 仅为优化器的候选列表,不代表实际会使用 |
| 本次查询中,实际使用的索引 | 核心判断项: 1. 为 2. 需和 |
| 索引中使用的字节数,是索引字段的最大可能长度 | 1. 不损失精度的前提下,长度越短越好 2. 可通过该值判断联合索引中,实际用到了哪些字段(对应最左前缀原则) |
| mysql认为执行查询必须扫描的行数 | innodb中为估算值,值越小越好,全表扫描时会显示表的总行数 |
| 返回结果行数占扫描行数的百分比 | 值越大越好,100.00为最优;值越小说明扫描了大量无效行,需优化索引 |
| 额外信息,sql优化的核心判断项,可直接定位sql的问题 | 高频关键值:
|
type字段出现all(全表扫描)extra字段出现using temporary、using filesorttype为ref/const,extra为using index(覆盖索引,无回表)完整的sql优化流程如下,可直接套用:
在大表场景下,索引对查询性能的提升效果极为显著,可通过以下步骤直观验证:
-- 1. 无索引时执行查询(全表扫描) select * from tb_sku where sn = '100000003145001'; -- 2. 为sn字段创建普通索引 create index idx_sku_sn on tb_sku(sn); -- 3. 再次执行相同查询(索引扫描) select * from tb_sku where sn = '100000003145001';
现象说明:
规则定义:联合索引遵循 “最左前缀匹配” 原则,查询必须从索引的最左列开始,且不能跳过中间列;若跳过某一列,该列右侧的所有索引列将失效。
示例:假设联合索引为 idx_user_pro_age_sta(profession, age, status)
-- 场景1:完全匹配(三列都用到索引) explain select * from tb_user where profession = '软件工程' and age = 31 and status = '0'; -- 场景2:用到前两列(status列索引失效) explain select * from tb_user where profession = '软件工程' and age = 31; -- 场景3:仅用到第一列(age、status列索引失效) explain select * from tb_user where profession = '软件工程'; -- 场景4:跳过最左列(profession列未使用,索引完全失效) explain select * from tb_user where age = 31 and status = '0'; -- 场景5:完全不使用最左列(索引完全失效,全表扫描) explain select * from tb_user where status = '0';
优化建议:联合索引的字段顺序需遵循 “等值条件优先、范围条件靠后” 的原则,将高频等值查询字段放在左侧。
规则定义:联合索引中,若某一列使用了>/</between等范围查询,该列右侧的所有索引列将失效。
示例:
-- 场景1:age使用范围查询,status列索引失效 explain select * from tb_user where profession = '软件工程' and age > 30 and status = '0'; -- 场景2:age使用>=/<=(边界范围),status列索引同样失效 explain select * from tb_user where profession = '软件工程' and age >= 30 and status = '0';
优化建议:将范围查询字段放在联合索引的最后一列,避免影响后续字段的索引使用;若业务允许,优先使用>=/<=替代>/<,减少范围扫描的行数。
规则定义:对索引列进行运算(如加减乘除)或函数操作(如substring/date_format),会导致索引失效,mysql 将转为全表扫描。
示例:
-- 场景:对phone字段使用substring函数,索引失效 explain select * from tb_user where substring(phone, 10, 2) = '15';
优化建议:
规则定义:字符串类型字段(如 varchar/char)查询时,若值未加引号,mysql 会自动进行类型转换,导致索引失效。
示例:
-- 场景1:status字段为字符串类型,不加引号,索引失效 explain select * from tb_user where profession = '软件工程' and age = 31 and status = 0; -- 场景2:phone字段为字符串类型,不加引号,索引失效 explain select * from tb_user where phone = 17799990015;
原理说明:字符串与数字比较时,mysql 会将字符串转换为数字进行比较,导致索引列的类型被隐式转换,无法匹配索引中的字符串值。优化建议:字符串字段的查询值必须加单引号,即使值是纯数字。
规则定义:
like '前缀%'):索引有效;like '%后缀'/like '%中间%'):索引失效,转为全表扫描。示例:
-- 场景1:尾部模糊匹配,索引有效 explain select * from tb_user where profession like '软件%'; -- 场景2:头部模糊匹配,索引失效 explain select * from tb_user where profession like '%工程'; -- 场景3:前后都模糊匹配,索引失效 explain select * from tb_user where profession like '%工%';
优化建议:
create index idx_profession on tb_user(profession(4)));fulltext)替代普通索引;规则定义:当查询条件匹配的数据量超过表中总行数的约 20% 时,mysql 优化器会评估使用索引的随机 io 成本高于全表扫描的顺序 io 成本,因此会放弃使用索引,直接进行全表扫描。
示例:
-- 场景1:匹配数据量少,使用索引 select * from tb_user where phone >= '17799990005'; -- 场景2:匹配数据量超过20%,mysql放弃索引,全表扫描 select * from tb_user where phone >= '17799990015';
优化建议:
force index强制使用索引,但需评估性能影响;当 mysql 优化器选择的索引不符合预期时,可通过索引提示强制指定索引,适用于复杂查询或优化器误判场景。
-- 1. use index:建议mysql使用指定索引(优化器仍可能选择其他索引) explain select * from tb_user use index(idx_user_pro) where profession = '软件工程'; -- 2. ignore index:忽略指定索引(让优化器不使用该索引) explain select * from tb_user ignore index(idx_user_pro) where profession = '软件工程'; -- 3. force index:强制mysql使用指定索引(优先级最高) explain select * from tb_user force index(idx_user_pro) where profession = '软件工程'
使用场景:
覆盖索引是指查询所需的所有列,都能在索引中直接获取,无需回表查询聚集索引,避免了额外的 io 操作,是 innodb 中性能最优的索引使用方式。

以联合索引 idx_user_pro_age_sta(profession, age, status) 为例:
-- 场景1:使用覆盖索引,无需回表(extra显示using index) explain select id, profession from tb_user where profession = '软件工程' and age = 31 and status = '0'; -- 场景2:索引列+主键,同样是覆盖索引(innodb二级索引默认包含主键) explain select id, profession, age, status from tb_user where profession = '软件工程' and age = 31 and status = '0'; -- 场景3:查询包含非索引列,需要回表(extra无using index) explain select id, profession, age, status, name from tb_user where profession = '软件工程' and age = 31 and status = '0'; -- 场景4:select * 查询,必然回表,无法使用覆盖索引 explain select * from tb_user where profession = '软件工程' and age = 31 and status = '0';
关键判断(extra 字段):
using index:使用了覆盖索引,无需回表,性能最优;using index condition:使用了索引,但需要回表查询完整数据;select *,仅查询业务所需的列;一张用户表 tb_user,包含字段 id, username, password, status,数据量较大,需对以下 sql 进行优化:
select id, username, password from tb_user where username = 'silverkite';
方案 1:普通单列索引(username)
create index idx_tb_user_username on tb_user(username);
执行逻辑:
idx_tb_user_username索引定位到username='itcast'对应的主键id;id回表查询聚集索引,获取username和password字段的值;方案 2:覆盖联合索引(最优方案)
create index idx_tb_user_uname_pwd on tb_user(username, password);
执行逻辑(无回表,性能天花板):
(username, password)中,已包含查询所需的所有字段:
where条件匹配username;select需要的id(二级索引默认包含主键)、username、password全部在索引中;using index;当字段为varchar/text等长字符串类型时,直接创建全字段索引会导致索引体积过大,查询时 io 成本高,可通过前缀索引仅对字符串的前 n 个字符创建索引,大幅节省索引空间并提升查询效率。
-- 为table_name表的column字段,取前n个字符创建前缀索引 create index idx_xxx on table_name(column(n));
前缀长度的选择需保证索引的高选择性(即不重复值占比越高,索引效率越高),计算方式如下:
-- 1. 全字段索引的选择性(最优为1,即唯一索引) select count(distinct email) / count(*) from tb_user; -- 2. 计算取前5个字符的选择性,对比全字段选择性,越接近越好 select count(distinct substring(email, 1, 5)) / count(*) from tb_user; -- 3. 当选择性接近全字段时,即可确定前缀长度(如n=5) create index idx_tb_user_email on tb_user(email(5));
以select * from tb_user where email = 'lvbu666@163.com';为例:
email(5)存储的是字符串前 5 个字符,如lvbu6;email前缀,获取对应的主键id;email值是否匹配;| 优点 | 缺点 | 适用场景 |
| 大幅降低索引体积,减少 io | 无法使用覆盖索引,查询时需回表校验完整值 | 长字符串字段(如邮箱、url),无法使用全字段索引的场景 |
| 提升索引创建与查询效率 | 若前缀选择性低,仍可能导致大量回表 | 高频前缀查询场景(如邮箱前缀、手机号前缀) |
在多条件查询场景中,优先使用联合索引,而非多个单列索引,可避免 mysql 优化器误判,同时实现覆盖索引优化。
场景 1:多个单列索引
-- 单列索引1 create index idx_tb_user_phone on tb_user(phone); -- 单列索引2 create index idx_tb_user_name on tb_user(name); -- 多条件查询 explain select id, phone, name from tb_user where phone = '17799990010' and name = '韩信';
执行结果分析:
possible_keys中同时存在两个索引,但key仅选择了idx_tb_user_phone;phone索引的基数更高);name条件,效率低。场景 2:联合索引(最优方案)
-- 创建phone和name的联合索引 create unique index idx_tb_user_phone_name on tb_user(phone, name); -- 相同查询 explain select id, phone, name from tb_user where phone = '17799990010' and name = '韩信';
执行结果分析:
idx_tb_user_phone_name,同时匹配phone和name两个条件;phone、name和主键id,若查询字段仅包含这三个,可实现覆盖索引,无需回表;| 优化场景 | 错误做法 | 正确做法 |
| 多条件查询 | 创建多个单列索引 | 创建联合索引 |
| 长字符串字段 | 创建全字段索引 | 创建前缀索引,优先保证高选择性 |
| 高频查询 | 使用select * | 仅查询业务字段,使用覆盖索引 |
| 联合索引 | 跳过最左列 | 调整查询条件,匹配最左前缀 |
| 模糊查询 | like '%前缀' | 使用like '前缀%'或前缀索引 |
索引设计是sql性能优化的根源性工作,优秀的索引设计可从源头避免慢sql、全表扫描、回表查询等性能问题,而非事后补救。以下7条核心设计原则,覆盖业务开发全场景与面试高频考点,完全承接前文的索引原理、使用规则与优化实践。
核心逻辑
索引的核心价值是解决大数据量下的查询性能问题,需精准匹配场景,避免无效索引:
实操避坑
索引的核心价值是过滤数据、利用有序性避免额外排序,这三类操作是索引最核心的落地场景,直接决定sql的性能上限。
分场景拆解
1.where查询条件字段
是索引最基础的使用场景,通过索引快速过滤数据,避免全表扫描,是所有索引设计的基础。
2.order by排序字段
利用b+tree索引天生的有序性,直接通过索引获取有序数据,避免mysql生成临时文件做额外排序(执行计划extra出现using filesort),大幅降低cpu消耗。
3.group by分组字段
分组操作的底层需要先对数据排序,再做聚合计算,索引可避免分组时创建临时表(执行计划extra出现using temporary),是分组查询优化的核心手段。
实操建议
多字段组合查询场景,优先创建联合索引,字段顺序遵循:where等值查询字段 → group by分组字段 → order by排序字段,可实现索引全流程覆盖,无额外排序、无临时表、无回表。
核心定义
区分度(也叫选择性)= 字段不重复值的数量 / 表总行数,比值越接近1,区分度越高,索引的过滤效率越强。
实操规范
1.建索引前先计算字段区分度,优先为区分度≥0.3的字段建索引:
-- 计算字段区分度,越接近1越好 select count(distinct 字段名) / count(*) from 表名;
2.业务中保证唯一性的字段(如手机号、身份证号、订单号),必须创建唯一索引,既保证数据唯一性,又获得最优查询性能;
3.低基数字段禁止单独建索引,可通过联合索引(和高区分度字段组合)实现优化。
核心逻辑
当字段为varchar(255)、text等长字符串类型时,直接创建全字段索引会导致索引体积过大,磁盘io成本极高;前缀索引仅对字符串的前n个字符创建索引,可大幅节省索引空间,提升索引查询效率。
实操规范
1.前缀长度选择核心:保证前缀的选择性接近全字段的选择性,平衡索引体积与过滤效率;
-- 1. 全字段选择性 select count(distinct email) / count(*) from tb_user; -- 2. 测试前10个字符的选择性,接近全字段即可确定前缀长度 select count(distinct substring(email, 1, 10)) / count(*) from tb_user; -- 3. 创建前缀索引 create index idx_user_email on tb_user(email(10));
2.适用场景:邮箱、url、长文本标题等长字符串字段的等值查询;
3.避坑提醒:前缀索引无法实现覆盖索引,因为索引中仅存储了字段前缀,无完整值,查询时必须回表校验完整数据。
核心优势
using index,避免回表查询,是性能优化的核心手段;设计规范
(a,b,c),则无需再创建(a)、(a,b)这类前缀子集索引,避免冗余开销;核心代价
索引的本质是「空间换时间」,过量索引会带来双重成本:
insert/update/delete时,需要同步维护所有相关索引的b+tree结构,保证有序性,索引越多,写操作耗时越长,数据库并发性能越差;实操规范
核心逻辑
null值时,需要增加额外的空值判断逻辑,无法高效利用索引;明确not null的字段,优化器可更精准地生成执行计划;null值会影响索引的基数统计,导致优化器对查询成本的评估出现偏差,可能选错执行计划;null值需要额外的存储空间标记空值,not null字段可减少索引的存储体积,提升io效率。实操规范
not null约束,并搭配合理的默认值:字符串类型默认空字符串'',数字类型默认0;null作为业务有效值,比如用0/1表示状态,而非用null表示「未设置」;-1、空字符串)替代null,保证索引列的not null约束。where过滤、order by排序、group by分组字段;not null约束与默认值,优化优化器执行计划。插入性能的核心瓶颈是磁盘 io 与事务提交开销,通过以下 4 种方式可大幅提升批量插入效率:
将多条数据合并为一条insert语句执行,减少客户端与数据库的交互次数,降低网络 io 与 sql 解析开销。
-- 低效方式:单条插入,多次交互 insert into tb_test values(1, 'tom'); insert into tb_test values(2, 'cat'); insert into tb_test values(3, 'jerry'); -- 高效方式:批量插入,单次交互 insert into tb_test values(1, 'tom'), (2, 'cat'), (3, 'jerry');
innodb 默认每条 sql 都会自动提交事务(autocommit=1),频繁提交事务会产生大量 redo 日志刷盘操作,通过手动开启事务,将多条插入包裹在一个事务中,仅需一次提交刷盘,大幅减少 io 次数。
-- 开启事务 start transaction; -- 批量插入数据 insert into tb_test values(1, 'tom'), (2, 'cat'), (3, 'jerry'); insert into tb_test values(4, 'tom'), (5, 'cat'), (6, 'jerry'); insert into tb_test values(7, 'tom'), (8, 'cat'), (9, 'jerry'); -- 统一提交事务,仅一次刷盘 commit;
rollback回滚,保证数据一致性。innodb 中数据按主键顺序存储在 b+tree 索引中,顺序插入时数据会追加到当前页的末尾,不会触发页分裂;乱序插入可能导致数据插入到已写满的页中,触发页分裂操作,带来额外的 io 开销。
-- 乱序主键(低效,易触发页分裂):8, 1, 9, 21, 88, 2, 4, 15, 89, 5, 7, 3 -- 顺序主键(高效,无额外页分裂):1, 2, 3, 4, 5, 7, 8, 9, 15, 21, 88, 89
1.页分裂:当插入数据的主键值需要写入已写满的页时,innodb 会将当前页的数据分裂为两个页,移动数据并调整 b+tree 结构,是插入性能的主要瓶颈之一;



插入50,会落在1#page区域,但1#page空间不足,就会进行页分裂,先将1中一半数据放进3#page页中,再将50放入3页,然后调整页指针。
2.页合并:当删除数据后,页中剩余数据低于merge_threshold(默认 50%)时,innodb 会尝试将相邻的页合并,减少碎片空间,优化存储效率。
一次性导入百万级以上的大批量数据,使用insert语句效率极低,推荐使用 mysql 提供的load data指令,直接从本地文件加载数据,跳过 sql 解析阶段,性能提升可达数十倍。
1.客户端连接时开启本地文件加载权限:
mysql --local-infile -u root -p
2.全局开启本地文件导入开关:
set global local_infile = 1;
3.执行load data指令导入数据:
load data local infile '/root/sql1.log' into table tb_user fields terminated by ',' lines terminated by '\n';
主键是 innodb 数据存储与索引的核心,主键设计直接影响数据插入、查询与更新的性能,需遵循以下 4 大核心原则:
innodb 二级索引(辅助索引)中会包含主键值,主键越短,二级索引的体积越小,占用的磁盘空间越少,查询时 io 效率越高。
int/bigint类型的自增主键,避免使用长字符串(如 uuid、身份证号)作为主键;自增主键保证数据按顺序插入,数据会追加到当前页的末尾,不会触发页分裂操作,插入性能最优;乱序主键(如 uuid、随机 id)会频繁触发页分裂,导致插入性能下降,同时产生大量存储碎片。
innodb 中表数据是根据主键顺序组织存放的,这种存储方式称为索引组织表(index organized table, iot),数据按主键顺序存储在 b+tree 的叶子节点中,顺序插入可保持叶子节点的有序性与连续性,避免碎片产生。
auto_increment自增主键,数据类型优先选择bigint unsigned(范围更大,避免溢出);| 主键类型 | 优点 | 缺点 |
| 自增 int/bigint | 顺序插入,无页分裂;主键短,索引体积小;性能最优 | 需单独维护,无业务含义 |
| uuid | 全局唯一,无需提前生成 | 乱序插入,频繁页分裂;长度长,索引体积大 |
| 自然主键(身份证号) | 无需额外字段,直接复用业务字段 | 长度长,索引体积大;存在业务变更风险;无法保证顺序插入 |
主键是 innodb 数据存储的核心标识,修改主键值会导致:
| 场景 | 错误做法 | 正确做法 |
| 批量插入 | 逐条插入,频繁提交事务 | 批量插入 + 事务包裹 |
| 大批量导入 | 使用 insert 循环插入 | 使用 load data infile |
| 主键设计 | 使用 uuid / 身份证号作为主键 | 使用自增 bigint 主键 |
| 主键插入 | 乱序插入 | 按主键顺序插入 |
| 主键修改 | 因业务需求修改主键值 | 主键无业务含义,永不修改 |
排序操作是sql性能的高频瓶颈,核心问题是避免using filesort(文件排序),优先通过索引实现有序数据读取。
1.using filesort:
非索引排序,通过全表扫描读取数据,在sort_buffer中完成排序操作,所有不通过索引直接返回有序结果的排序都属于文件排序,性能较差。
2.using index:
利用有序索引直接扫描返回有序数据,无需额外排序,操作效率高,是排序优化的目标。
-- 无索引,触发using filesort explain select id,age,phone from tb_user order by age , phone;
-- 创建age、phone的联合索引,实现using index create index idx_user_age_phone_aa on tb_user(age,phone); -- 升序排序:索引有序,无需额外排序 explain select id,age,phone from tb_user order by age , phone; -- 同方向降序排序:索引支持,无需额外排序 explain select id,age,phone from tb_user order by age desc , phone desc;
当排序字段为一个升序、一个降序时,需创建匹配顺序的索引:
-- 创建age升序、phone降序的索引 create index idx_user_age_phone_ad on tb_user(age asc ,phone desc); -- 匹配索引顺序,实现using index explain select id,age,phone from tb_user order by age asc , phone desc;
using filesort,可适当增大排序缓冲区sort_buffer_size(默认256kb),提升排序性能。分组操作的核心瓶颈是临时表创建与排序,可通过索引优化避免using temporary。
分组操作底层依赖数据有序性,索引的有序性可直接用于分组聚合,无需额外创建临时表。
-- 删除旧索引 drop index idx_user_pro_age_sta on tb_user; -- 无索引分组,触发using temporary explain select profession , count(*) from tb_user group by profession ; -- 创建(profession, age, status)联合索引 create index idx_user_pro_age_sta on tb_user(profession , age , status); -- 匹配最左前缀,实现using index,无临时表 explain select profession , count(*) from tb_user group by profession ; explain select profession , count(*) from tb_user group by profession, age; -- 带过滤条件的分组:where profession='软件工程' group by age explain select age,count(*) from tb_user where profession = '软件工程' group by age;
where)字段需在分组字段之前,保证索引有序性;大数据量下的分页查询(如limit 2000000,10)性能极差,需通过覆盖索引+子查询优化。
limit m,n的执行逻辑是先读取前m+n条记录,再丢弃前m条,返回后n条,当m很大时,会产生大量无效io。
-- 低效方式:直接分页查询,全表扫描排序 select * from tb_sku limit 2000000,10; -- 高效方式:先通过覆盖索引获取id,再关联查询完整数据 explain select * from tb_sku t , (select id from tb_sku order by id limit 2000000,10) a where t.id = a.id;
select *,仅在子查询中获取主键id,减少数据读取量;where id > ? limit n的方式优化,前提是主键连续有序。count操作的性能差异主要由存储引擎与使用方式决定,需根据业务场景选择最优方案。
count(*)可直接返回结果,效率极高;count(*)时需遍历数据行进行计数,性能受数据量影响较大。用法 | 执行逻辑 | 效率排序 |
|---|---|---|
| 遍历表,读取字段值并判断是否为null,不为null则计数 | 最低 |
| 遍历表,读取主键值(非null)并计数 | 较低 |
| 遍历表,不读取字段值,直接按行计数 | 高 |
| 优化处理,不读取字段值,直接按行计数 | 最高 |
结论:优先使用count(*),效率最优。
count(*),避免使用count(字段);update操作的核心风险是行锁升级为表锁,导致并发性能下降,需通过索引保证行锁的有效性。
innodb的行锁是针对索引加锁,而非针对记录加锁。若更新条件字段没有索引或索引失效,行锁会升级为表锁,严重影响并发性能。
-- 高效更新:主键索引条件,行锁,仅锁定id=1的记录 update student set no = '2000100100' where id = 1; -- 低效更新:无索引的name字段,索引失效,行锁升级为表锁 update student set no = '2000100105' where name = '韦一笑';
场景 | 错误做法 | 正确做法 |
| 混合升降序排序,无索引 | 创建匹配顺序的联合索引,使用覆盖索引 |
| 无索引分组,触发临时表 | 创建包含分组字段的联合索引,遵循最左前缀 |
| 直接使用 | 覆盖索引+子查询定位id,再关联查询 |
| 使用 | 使用 |
| 无索引条件更新,行锁升级为表锁 | 使用主键/唯一索引作为更新条件,保证索引有效 |
视图是虚拟存在的表,它本身不存储真实数据,只保存了查询的sql逻辑,数据在使用视图时动态从基表中生成。
select查询的封装,相当于给复杂查询起了个“别名”。-- 语法格式 create [or replace] view 视图名称[(列名列表)] as select语句 [with [cascaded | local] check option]; -- 示例:创建视图,查询学生表中id<=20的数据 create view v_student_20 as select id, name from student where id <= 20;
or replace:如果视图已存在,则替换原有定义;with check option:视图更新/插入数据时,必须满足视图的查询条件,否则会报错。-- 查看视图的创建语句 show create view v_student_20; -- 查询视图数据(和普通表用法一致) select * from v_student_20 where name like '张%';
-- 方式一:create or replace view(推荐) create or replace view v_student_20 as select id, name, age from student where id <= 20; -- 方式二:alter view alter view v_student_20 as select id, name, age from student where id <= 20;
-- 语法格式 drop view [if exists] 视图名称 [,视图名称] ...; -- 示例 drop view if exists v_student_20;
当使用with check option创建视图时,mysql会在视图的insert/update/delete操作中,检查数据是否符合视图的定义条件,不符合则拒绝执行。
对于多层嵌套视图,mysql提供两种检查规则:
规则:检查当前视图和所有上层依赖视图的条件,只要有一层视图条件不满足,就会报错。
示例:
-- 基表student -- 视图v1:id <= 20,带cascaded检查 create view v1 as select id,name from student where id <= 20 with cascaded check option; -- 视图v2:基于v1,id >= 10,带cascaded检查 create view v2 as select id,name from v1 where id >= 10 with cascaded check option; -- 视图v3:基于v2,id <= 15,无检查 create view v3 as select id,name from v2 where id <= 15; -- 尝试向v3插入id=21的数据:会触发检查,不符合v1的id<=20条件,插入失败 insert into v3(id,name) values(21,'test');
规则:只检查当前视图的条件,不检查上层依赖视图的条件(但上层视图带cascaded检查时,仍会触发级联检查)。
示例:
-- 基表student -- 视图v1:id <= 15,无检查 create view v1 as select id,name from student where id <= 15; -- 视图v2:基于v1,id >= 10,带local检查 create view v2 as select id,name from v1 where id >= 10 with local check option; -- 尝试向v2插入id=16的数据:满足v2的id>=10条件,但不满足v1的id<=15条件,插入失败(因为数据无法被v1查询到,视图数据不生效) insert into v2(id,name) values(16,'test');
视图的更新(insert/update/delete)并非都能执行,核心前提是:视图中的行与基表中的行存在一对一的映射关系。
如果视图定义中包含以下任意一项,则该视图无法更新:
sum()、min()、max()、count()、row_number()等;distinct去重;group by分组;having过滤;union或union all;age+1)。with check option,更新/插入的数据必须满足视图的查询条件。join + where + group by的复杂报表查询封装为视图,业务人员直接查询视图即可获取数据。student的name字段重命名为stu_name,只需修改视图的select stu_name as name,上层查询视图的业务代码无需改动。存储过程是一组预编译并存储在数据库中的sql语句集合,相当于数据库层面的“函数”。调用时只需传入参数即可执行封装好的逻辑,无需重复编写sql。
语法格式
-- 命令行中需先修改结束符(避免和默认;冲突)
delimiter //
create procedure 存储过程名称([参数列表])
begin
-- 存储过程内的sql逻辑
end //
delimiter ; -- 恢复默认结束符示例:无参数存储过程
-- 示例:查询用户表的总数
delimiter //
create procedure sp_get_user_count()
begin
select count(*) as user_total from tb_user;
end //
delimiter ;语法格式
call 存储过程名称([参数]);
示例:调用上面创建的存储过程
call sp_get_user_count();
-- 1. 查看指定数据库的所有存储过程信息 select * from information_schema.routines where routine_schema = 'test_db'; -- 2. 查看单个存储过程的创建语句 show create procedure sp_get_user_count;
语法格式
drop procedure [if exists] 存储过程名称;
示例
drop procedure if exists sp_get_user_count;
存储过程支持3种参数类型,满足输入、输出、双向交互需求:
参数类型 | 含义 | 备注 |
| 输入参数,调用时传入值 | 默认类型 |
| 输出参数,用于返回结果 | 需用变量接收 |
| 既可以作为输入,也可以作为输出 | 双向参数 |
示例1:带in输入参数的存储过程
-- 示例:根据用户id查询用户信息
delimiter //
create procedure sp_get_user_by_id(in p_user_id int)
begin
select * from tb_user where id = p_user_id;
end //
delimiter ;
-- 调用
call sp_get_user_by_id(1);示例2:带out输出参数的存储过程
-- 示例:查询用户总数,并通过输出参数返回
delimiter //
create procedure sp_get_user_count_out(out p_total int)
begin
select count(*) into p_total from tb_user;
end //
delimiter ;
-- 调用:用用户变量接收返回值
call sp_get_user_count_out(@user_count);
select @user_count as user_total;示例3:带inout双向参数的存储过程
-- 示例:传入一个数字,将其乘以2后返回
delimiter //
create procedure sp_double_num(inout p_num int)
begin
set p_num = p_num * 2;
end //
delimiter ;
-- 调用
set @num = 10;
call sp_double_num(@num);
select @num as doubled_num; -- 结果为20mysql存储过程中包含三类变量:系统变量、用户定义变量、局部变量。
mysql服务器提供的内置变量,分为全局变量(global)和会话变量(session)。
查看系统变量
-- 查看所有会话变量 show session variables; -- 模糊查找变量(如查看排序缓冲区大小) show variables like 'sort_buffer_size'; -- 查看指定变量的值 select @@global.sort_buffer_size; -- 全局变量 select @@session.sort_buffer_size; -- 会话变量
设置系统变量
-- 设置会话级变量(仅当前会话生效) set session sort_buffer_size = 1024*1024; -- 1mb -- 设置全局变量(重启后失效,需修改配置文件永久生效) set global sort_buffer_size = 2*1024*1024; -- 2mb
用户自定义的会话级变量,无需提前声明,直接用@变量名使用,作用域为当前会话。
赋值与使用
-- 方式1:set赋值 set @user_name = 'zhangsan'; set @age := 18; -- := 也可赋值 -- 方式2:select into赋值 select name, age into @user_name, @user_age from tb_user where id = 1; -- 使用变量 select * from tb_user where name = @user_name;
存储过程内的局部变量,需用declare声明,作用域为begin...end块内。
声明与赋值
delimiter //
create procedure sp_local_var_demo()
begin
-- 声明局部变量,指定类型和默认值
declare v_total int default 0;
declare v_name varchar(20);
-- 赋值
select count(*) into v_total from tb_user;
set v_name = 'local_test';
-- 使用变量
select v_total, v_name;
end //
delimiter ;
call sp_local_var_demo();语法格式
if 条件1 then
-- 条件1成立时执行
elseif 条件2 then
-- 条件2成立时执行(可选)
else
-- 所有条件不成立时执行(可选)
end if;示例:根据用户数量判断用户规模
delimiter //
create procedure sp_user_scale()
begin
declare v_count int default 0;
declare v_scale varchar(20);
select count(*) into v_count from tb_user;
if v_count > 1000 then
set v_scale = '大规模用户';
elseif v_count > 100 then
set v_scale = '中规模用户';
else
set v_scale = '小规模用户';
end if;
select v_count, v_scale;
end //
delimiter ;
call sp_user_scale();支持两种语法格式,适合多分支条件判断。
语法格式1:匹配固定值
case case_value
when when_value1 then statement_list1
when when_value2 then statement_list2
else statement_list
end case;语法格式2:匹配条件表达式
case
when search_condition1 then statement_list1
when search_condition2 then statement_list2
else statement_list
end case;示例:根据用户等级返回描述
delimiter //
create procedure sp_user_level_desc(in p_level int, out p_desc varchar(20))
begin
case p_level
when 1 then set p_desc = '普通用户';
when 2 then set p_desc = 'vip用户';
when 3 then set p_desc = 'svip用户';
else set p_desc = '未知等级';
end case;
end //
delimiter ;
-- 调用
call sp_user_level_desc(2, @level_desc);
select @level_desc; -- 结果为vip用户mysql 存储过程支持 3 种循环:while、repeat、loop,适用于不同场景的批量数据处理。
语法格式
while 条件 do
-- 循环体sql逻辑
end while;示例:批量插入 10 条测试用户数据
delimiter //
create procedure sp_batch_insert_user()
begin
declare i int default 1;
while i <= 10 do
insert into tb_user (name, age) values (concat('test_', i), 18 + i);
set i = i + 1; -- 必须更新计数器,否则会死循环
end while;
end //
delimiter ;
-- 调用执行
call sp_batch_insert_user();语法格式
repeat
-- 循环体sql逻辑
until 条件 -- 条件满足时退出循环
end repeat;示例:批量插入 10 条测试用户数据(repeat 实现)
delimiter //
create procedure sp_repeat_insert_user()
begin
declare i int default 1;
repeat
insert into tb_user (name, age) values (concat('repeat_', i), 20 + i);
set i = i + 1;
until i > 10 -- i>10时退出循环
end repeat;
end //
delimiter ;
-- 调用执行
call sp_repeat_insert_user();语法格式
[begin_label:] loop
-- 循环体sql逻辑
-- 需用leave手动退出循环,否则会死循环
end loop [end_label];leave label:退出指定标签的循环;iterate label:跳过当前循环,直接进入下一次循环。示例:loop 循环实现批量插入,含跳过逻辑
delimiter //
create procedure sp_loop_insert_user()
begin
declare i int default 1;
loop_label: loop -- 定义循环标签
if i > 10 then
leave loop_label; -- 条件满足时退出循环
end if;
-- 跳过偶数次插入,只插入奇数
if i % 2 = 0 then
set i = i + 1;
iterate loop_label; -- 跳过当前循环,直接下一次
end if;
insert into tb_user (name, age) values (concat('loop_', i), 25 + i);
set i = i + 1;
end loop loop_label;
end //
delimiter ;
-- 调用执行
call sp_loop_insert_user();游标用于存储查询结果集,可在存储过程中逐行处理数据,适合批量数据处理场景。
语法格式
-- 1. 声明游标 declare 游标名称 cursor for 查询语句; -- 2. 打开游标 open 游标名称; -- 3. 获取数据 fetch 游标名称 into 变量1, 变量2...; -- 4. 关闭游标 close 游标名称;
条件处理程序(handler)用于捕获存储过程执行中的异常,并定义处理逻辑,避免程序因异常中断。
declare handler_action handler for condition_value [, condition_value] ... statement;
handler_action:
continue:捕获异常后继续执行后续代码;exit:捕获异常后终止当前存储过程;condition_value:异常条件,支持:sqlstate 'xxxx':指定 sql 状态码(如02000表示 not found);sqlwarning:捕获所有以01开头的警告;not found:捕获所有以02开头的未找到数据异常;sqlexception:捕获所有其他 sql 异常。示例:捕获异常并记录日志
-- 先创建日志表
create table if not exists proc_log (
id int auto_increment primary key,
error_msg varchar(200),
create_time datetime default current_timestamp
);
delimiter //
create procedure sp_exception_demo()
begin
-- 声明异常处理:捕获异常后继续执行,并记录日志
declare continue handler for sqlexception
insert into proc_log (error_msg) values ('执行存储过程时发生异常');
-- 可能出错的sql:插入重复主键数据
insert into tb_user (id, name) values (1, 'test_user');
select '执行完成' as result;
end //
delimiter ;
-- 调用执行(若id=1已存在,会触发异常,但程序会继续执行并记录日志)
call sp_exception_demo();
-- 查看日志
select * from proc_log;综合使用变量、循环、游标、异常处理,实现一个完整的批量数据处理存储过程:
delimiter //
create procedure sp_user_batch_process(in p_start_id int, in p_end_id int)
begin
-- 1. 声明变量
declare v_id int;
declare v_age int;
declare done int default false;
declare update_count int default 0;
-- 2. 声明游标和异常处理
declare user_cursor cursor for
select id, age from tb_user where id between p_start_id and p_end_id;
declare continue handler for not found set done = true;
declare exit handler for sqlexception
insert into proc_log (error_msg) values (concat('批量处理用户数据异常,范围:', p_start_id, '-', p_end_id));
-- 3. 打开游标,遍历处理
open user_cursor;
user_loop: loop
fetch user_cursor into v_id, v_age;
if done then
leave user_loop;
end if;
-- 业务逻辑:年龄>30的用户,标记为vip
if v_age > 30 then
update tb_user set is_vip = 1 where id = v_id;
set update_count = update_count + 1;
end if;
end loop;
close user_cursor;
-- 4. 返回处理结果
select concat('批量处理完成,共更新', update_count, '条数据') as result;
end //
delimiter ;
-- 调用:处理id 1-100的用户数据
call sp_user_batch_process(1, 100);while/repeat/loop循环中必须有计数器更新或退出条件,否则会导致死循环;close,否则会占用数据库资源;存储函数是有返回值的存储过程,它的参数只能是 in 类型,且必须通过 return 语句返回一个结果。
它可以像普通内置函数一样,直接在 select 语句中调用。
create function 存储函数名称([参数列表])
returns type [characteristic ...]
begin
-- sql语句
return ...; -- 必须有return语句
end;returns type:指定函数的返回值类型(如 int, varchar, decimal 等)。characteristic:特性说明,用于优化和约束函数行为:deterministic:相同输入参数总是产生相同结果(纯函数)。no sql:函数体内不包含任何sql语句。reads sql data:函数体内只包含读数据的语句,不包含写数据的语句。-- 示例:获取当前系统日期
delimiter //
create function fn_get_current_date()
returns date
no sql
begin
return curdate();
end //
delimiter ;
-- 调用
select fn_get_current_date();-- 示例:根据用户id查询用户年龄
delimiter //
create function fn_get_user_age(p_user_id int)
returns int
reads sql data
begin
declare v_age int;
select age into v_age from tb_user where id = p_user_id;
return v_age;
end //
delimiter ;
-- 调用
select fn_get_user_age(1);对比项 | 存储过程(procedure) | 存储函数(function) |
返回值 | 可以无返回值,也可通过 | 必须有且仅有一个返回值 |
参数类型 | 支持 | 仅支持 |
调用方式 | 使用 | 可直接在 |
适用场景 | 批量数据处理、复杂事务、多步操作 | 计算、查询单个值、可复用的业务规则 |
return 语句,否则会报错。in 类型,不能显式指定 out 或 inout。select 中安全调用,存储函数内不建议执行 insert/update/delete 等写操作,否则可能导致数据不一致。create routine 权限,调用时需要 execute 权限。触发器是与表关联的数据库对象,它会在 insert/update/delete 操作执行之前或之后,自动触发并执行预定义的sql语句集合。
old 和 new 关键字引用数据:触发器类型 | new(新数据) | old(旧数据) |
| 表示将要/已新增的数据 | 无 |
| 表示将要/已修改后的数据 | 表示修改前的数据 |
| 无 | 表示将要/已删除的数据 |
create trigger trigger_name
before/after insert/update/delete
on tbl_name for each row -- 行级触发器
begin
trigger_stmt; -- 触发时执行的sql
end;show triggers;
drop trigger [if exists] [schema_name.]trigger_name;
我们通过触发器实现 tb_user 表的增/改/删操作日志记录,将日志写入 user_logs 表。
create table user_logs(
id int(11) not null auto_increment,
operation varchar(20) not null comment '操作类型, insert/update/delete',
operate_time datetime not null comment '操作时间',
operate_id int(11) not null comment '操作的id',
operate_params varchar(500) comment '操作参数',
primary key(`id`)
) engine=innodb default charset=utf8;delimiter //
create trigger tb_user_insert_trigger
after insert on tb_user for each row
begin
insert into user_logs(operation, operate_time, operate_id, operate_params)
values(
'insert',
now(),
new.id,
concat('新增数据:id=', new.id, ', name=', new.name, ', phone=', new.phone)
);
end //
delimiter ;delimiter //
create trigger tb_user_update_trigger
after update on tb_user for each row
begin
insert into user_logs(operation, operate_time, operate_id, operate_params)
values(
'update',
now(),
new.id,
concat('修改前:id=', old.id, ', name=', old.name, ' | 修改后:id=', new.id, ', name=', new.name)
);
end //
delimiter ;delimiter //
create trigger tb_user_delete_trigger
after delete on tb_user for each row
begin
insert into user_logs(operation, operate_time, operate_id, operate_params)
values(
'delete',
now(),
old.id,
concat('删除数据:id=', old.id, ', name=', old.name, ', phone=', old.phone)
);
end //
delimiter ;-- 1. 新增用户(触发insert触发器) insert into tb_user(id, name, phone) values(26, '张三', '18809091212'); -- 2. 修改用户(触发update触发器) update tb_user set name = '张三三' where id = 26; -- 3. 删除用户(触发delete触发器) delete from tb_user where id = 26; -- 查看日志 select * from user_logs;
before 触发器可以修改 new 数据,也可以用于数据校验;after 触发器不能修改数据,适合做日志记录、级联操作。old/new 关键字:insert 触发器中,只有 new 可用;delete 触发器中,只有 old 可用;update 触发器中,old 和 new 都可用。before insert/update 触发器中校验数据合法性(如年龄不能为负、手机号格式);存储过程、存储函数、触发器尽量不要用,阿里范式不允许
1. 业务逻辑下沉到数据库
业务逻辑本该写在 java/php/go 后端,存储过程把大量逻辑写死在db里,业务分散、逻辑混乱,新人接手看不懂。
2. 调试极其困难
没有断点调试、没有日志跟踪,出问题只能靠猜、靠打印sql,复杂流程排错成本极高。
3. 版本控制难
存储过程存在数据库里,不能git管理,无法做版本迭代、快速回滚,上线、灰度都很麻烦。
4. 可移植性差
mysql、oracle、sql server 存储过程语法完全不一样,一旦换数据库,全部重写。
5. 加重数据库压力
复杂循环、计算、业务判断都在db执行,db本应只做存储和简单查询,不适合承载业务计算逻辑,容易造成cpu、连接数打满。
6. 微服务分布式不兼容
微服务提倡业务在服务层、数据只在db,存储过程无法跨服务调用,也不方便做分布式事务、限流、熔断。
7. 并发与锁风险
存储过程里多sql默认在一个事务,容易长事务、锁等待、死锁,线上隐患大。
一句话:能用后端代码实现的计算,绝不写存储函数。
1. 隐式执行,逻辑“隐身”
触发器是自动偷偷执行,开发者写 insert/update/delete 时完全感知不到还有额外逻辑在跑,出了问题根本想不到是触发器导致的。
2. 排错极难
数据莫名其妙变了、莫名多了日志、莫名被修改,排查半天最后发现是触发器偷偷触发,隐蔽性太强,坑很多。
3. 性能损耗大
触发器是行级触发,批量插入1万条,就触发1万次,严重拖慢批量操作性能。
4. 容易触发循环嵌套死循环
a表触发器改b表,b表触发器又改a表,连环触发、死循环、锁表,线上事故高危。
5. 主从同步容易出问题
触发器在主库执行,从库复制可能重复触发,导致数据重复、不一致。
6. 不利于数据迁移和分库分表
分表、分库、数据迁移时,触发器逻辑容易被遗漏,导致数据行为不一致。
只有以下极少数场景可容忍:
新项目、微服务、分布式项目:一律禁止使用。
锁是计算机协调多个进程/线程并发访问同一资源的机制。
在数据库中,除了cpu、内存、i/o等计算资源的争用,数据本身也是一种多用户共享资源。锁的核心目标是:
mysql的锁按粒度从大到小分为三类:
锁类型 | 作用范围 | 特点 |
全局锁 | 锁定整个数据库实例 | 粒度最大,影响范围最广 |
表级锁 | 锁定整张表 | 粒度中等,不区分行,影响整张表 |
行级锁 | 锁定单条数据行 | 粒度最小,仅影响被操作的行 |
全局锁会对整个数据库实例加锁,加锁后实例进入只读状态,以下操作都会被阻塞:
insert/update/delete)create/alter/drop table)
当你执行 mysqldump 全库备份时,如果不加特殊参数,就会触发全局锁。
-- 加全局读锁(只读锁) flush tables with read lock; -- 释放全局锁 unlock tables;
# 备份命令(传统方式,会加全局锁) mysqldump -uroot -p1234 itcast > itcast.sql
select 查询可以正常执行,insert/update/delete 会被阻塞;
在 innodb 引擎中,使用 --single-transaction 参数实现不加锁的一致性备份:
mysqldump --single-transaction -uroot -p123456 itcast > itcast.sql
原理:利用 innodb 的事务隔离级别,在一个事务内完成一致性快照备份,全程不影响业务写入。
表级锁是mysql中粒度最大的锁,每次操作直接锁定整张表:
表级锁主要分为三类:
类型 | 加锁方式 | 兼容性说明 |
表共享读锁(read lock) |
| 不阻塞其他客户端的读,但阻塞写 |
表独占写锁(write lock) |
| 既阻塞其他客户端的读,也阻塞写 |
-- 1. 加表锁 lock tables 表名 read; -- 加共享读锁 lock tables 表名 write; -- 加独占写锁 -- 2. 释放锁 unlock tables; -- 手动释放 -- 客户端断开连接时,也会自动释放锁
select,但insert/update/delete会被阻塞;select和写操作都会被阻塞,只有持有锁的会话能读写。元数据锁(meta data lock,mdl)是mysql 5.5+自动维护的锁,无需显式使用,在访问表时自动加锁:
对应sql | 锁类型 | 说明 |
|
| 显式表锁 |
|
| 与读/写兼容,与 |
|
| 与读/写兼容,与 |
|
| 与所有其他mdl锁互斥 |
select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks;

为了解决行锁与表锁的冲突检查效率问题,innodb引入了意向锁:

锁类型 | 触发场景 | 兼容性说明 |
意向共享锁(is) |
| 与表共享读锁兼容,与表独占写锁互斥 |
意向排他锁(ix) |
| 与表共享读锁、写锁都互斥;意向锁之间不互斥 |
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
行级锁是 innodb 引擎独有的锁机制,每次操作仅锁定对应的行数据:
行锁分为共享锁(s锁)和排他锁(x锁),兼容性如下:
当前锁类型 | 请求s锁 | 请求x锁 |
s(共享锁) | 兼容 | 冲突 |
x(排他锁) | 冲突 | 冲突 |
sql语句 | 锁类型 | 说明 |
| 排他锁(x) | 自动加锁 |
| 共享锁(s) | 手动加锁 |
| 排他锁(x) | 手动加锁 |
普通 | 不加锁 | mvcc快照读,无锁 |
-- 事务a begin; select * from tb_user where id = 1 lock in share mode; -- 加s锁 -- 事务b begin; select * from tb_user where id = 1 lock in share mode; -- 可以加s锁(兼容) update tb_user set name = 'test' where id = 1; -- 加x锁,被阻塞(冲突)
-- 事务a begin; update tb_user set name = 'test' where id = 1; -- 自动加x锁 -- 事务b begin; select * from tb_user where id = 1 for update; -- 加x锁,被阻塞(冲突) select * from tb_user where id = 1 lock in share mode; -- 加s锁,被阻塞(冲突)
间隙锁锁定索引记录的间隙(不含记录本身),目的是防止其他事务在该间隙插入数据,从而避免幻读,仅在rr隔离级别下生效。
示例1:不存在记录的等值查询(唯一索引)
-- 表中id为10、20、30,无id=15的记录 begin; select * from tb_user where id = 15 for update; -- 触发间隙锁,锁定(10,20)之间的间隙,防止插入id=15的数据
示例2:普通索引等值查询的边界场景
-- 表中age为18、20、22,查询age=25(不存在) begin; select * from tb_user where age = 25 for update; -- 触发间隙锁,锁定(22, +∞)之间的间隙
临键锁是行锁 + 间隙锁的组合,同时锁定数据记录及其前面的间隙,是innodb在rr隔离级别下默认的锁机制,用于彻底防止幻读。
示例1:范围查询(唯一索引)
-- 表中id为10、20、30、40 begin; select * from tb_user where id > 20 and id < 30 for update; -- 触发临键锁,锁定: -- 行锁:id=30(不满足条件的第一个值) -- 间隙锁:(20,30)和(30,40)之间的间隙
示例2:普通索引的范围查询
-- 表中age为18、20、22、25 begin; select * from tb_user where age >= 20 for update; -- 触发临键锁,锁定: -- 行锁:age=20、22、25 -- 间隙锁:(18,20)、(20,22)、(22,25)、(25, +∞)
通过以下sql可以查看意向锁、行锁、间隙锁的情况:
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
锁类型 | 作用 | 隔离级别 |
行锁(record lock) | 锁定单条记录,防止其他事务修改/删除 | rc/rr |
间隙锁(gap lock) | 锁定索引间隙,防止插入新数据 | rr |
临键锁(next-key lock) | 行锁+间隙锁组合,彻底防止幻读 | rr(默认) |
死锁:两个或多个事务,互相持有对方需要的锁,又都不释放自己的锁,无限等待,谁也执行不下去。
innodb 会自动检测死锁,主动回滚代价更小的一个事务,让另一个正常执行。
四个条件同时满足,必然死锁。
表 tb_user 有主键 id。
事务a
begin; update tb_user set name='a' where id=1; -- 持有id=1行锁 update tb_user set name='b' where id=2; -- 等待事务b的id=2锁
事务b
begin; update tb_user set name='b' where id=2; -- 持有id=2行锁 update tb_user set name='a' where id=1; -- 等待事务a的id=1锁
形成循环等待 → 直接死锁。
字段无索引,更新变成表级排他锁,互相阻塞产生死锁。
事务a
begin; update tb_user set age=20 where name='张三'; -- name无索引,锁整张表
事务b
begin; update tb_user set age=25 where name='李四'; -- 同样无索引,也锁整张表
互相等待对方表锁,形成死锁。
普通索引范围查询产生临键锁/间隙锁,间隙之间互相占用,引发死锁。
-- 查看最近一次死锁详情 show engine innodb status;
在输出信息中找到 latest detected deadlock,可看到:
所有业务事务,必须按相同顺序访问表、访问行。
示例:
永远先操作 id=1,再操作 id=2,所有服务都遵守,从根源打破循环等待。
更新/删除条件一定要走索引,避免行锁升级为表锁,大幅减少锁范围和冲突。
捕获死锁异常后,间隔短暂时间自动重试,线上常用方案。
范围查询容易触发临键锁、间隙锁,锁范围放大,极易诱发死锁;
能用等值查询就不用范围。
把隔离级别从 rr 降到 rc:
不要在一个事务内多次更新同一张表不同行,减少锁竞争。
innodb 的数据是按表空间(tablespace)→ 段(segment)→ 区(extent)→ 页(page)→ 行(row)的层级组织。

trx_id 和 roll_pointer,配合回滚段的 undo log,实现了事务的一致性视图和多版本读取。trx_id(事务id)、roll_pointer(旧版本指针),用于mvcc;
innodb 架构分为三大核心部分:

free page:空闲未使用的页clean page:已使用且数据未修改的页dirty page:已使用且数据已修改,与磁盘数据不一致的页

adaptive_hash_index(默认开启)


类型 | 作用 | 关键文件/参数 |
系统表空间(system tablespace) | 存储change buffer、数据字典、undo log等 |
|
独立表空间(file-per-table) | 每个表单独存储数据和索引,默认开启 |
|
通用表空间(general tablespaces) | 自定义表空间,可指定多个表 | 需用 |
撤销表空间(undo tablespaces) | 存储undo log,支持事务回滚和mvcc | 默认两个16mb的文件 |
临时表空间(temporary tablespaces) | 存储临时表数据 | 全局 |

ib_logfile0、ib_logfile1,以循环方式写入线程类型 | 核心职责 | 说明 |
master thread | 核心调度,异步刷新脏页、合并change buffer、回收undo页 | innodb主线程 |
io thread | 处理aio请求的回调,包括read/write/log/insert buffer线程 | 默认配置:读4个、写4个、日志1个、插入缓冲1个 |
purge thread | 回收已提交事务的undo log,释放空间 | 提升事务回滚和mvcc性能 |
page cleaner thread | 协助master thread刷新脏页,减轻主线程压力 | 减少主线程阻塞,提升并发性能 |
事务是一组不可分割的操作集合,作为一个整体向系统提交或撤销请求:
特性 | 含义 | 实现机制 |
原子性(atomicity) | 事务是最小操作单元,不可分割,要么全成、要么全败 |
|
一致性(consistency) | 事务执行前后,数据必须保持一致状态(如转账前后总额不变) | 业务规则 + acid共同保障 |
隔离性(isolation) | 事务在不受外部并发操作影响的独立环境中运行 | 锁 + mvcc |
持久性(durability) | 事务提交后,对数据的修改永久生效,不丢失 |
|

记录事务提交时数据页的物理修改,用于崩溃恢复,保障事务持久性。
redo log buffer(日志缓冲区)ib_logfile0/ib_logfile1(重做日志文件,循环写入)记录数据修改前的状态,提供事务回滚和mvcc多版本并发控制。
delete对应insert,update对应反向update);rollback segment)中,每个回滚段包含1024个undo log段。mvcc(multi-version concurrency control,多版本并发控制),是 innodb 实现读写不阻塞的核心机制:
读取记录的最新版本,并对记录加锁,保证其他事务无法修改。
select ... lock in share mode(共享锁)select ... for update(排他锁)insert / update / delete(自动加排他锁)读取记录的可见版本(可能是历史数据),不加锁,是非阻塞读。
select(无锁);read committed:每次select都生成新快照;repeatable read:事务中第一次select生成快照,后续复用;serializable:快照读退化为当前读。每个innodb记录都包含三个隐藏字段:
字段名 | 作用 |
| 最近修改该记录的事务id |
| 回滚指针,指向该记录的上一个版本(undo log) |
| 隐藏主键,无主键时自动生成 |

db_roll_ptr将多个版本串联成一条版本链,链表头是最新版本,尾部是最早版本;readview是快照读判断版本可见性的依据,记录当前系统中活跃的事务(未提交)id集合,包含四个核心字段:
字段 | 含义 |
| 当前活跃事务id集合 |
| 最小活跃事务id |
| 预分配事务id(当前最大事务id+1) |
| 创建该readview的事务id |
读取记录时,需遍历版本链,直到找到符合以下条件的版本:
trx_id == creator_trx_id:数据由当前事务修改,可见;trx_id < min_trx_id:事务已提交,可见;trx_id > max_trx_id:事务在readview生成后开启,不可见;min_trx_id <= trx_id <= max_trx_id:需判断trx_id是否在m_ids中:隔离级别 | readview生成时机 | 效果 |
| 每次快照读都生成新的readview | 每次查询都能看到其他事务已提交的修改,解决不可重复读 |
| 事务中第一次快照读生成readview,后续复用 | 整个事务期间复用同一个readview,保证可重复读,同时避免幻读 |
安装mysql后,会自动创建4个系统数据库,作用如下:
数据库 | 核心作用 |
| 存储mysql服务器运行的关键信息:用户账号、权限配置、时区设置、主从复制状态等 |
| 提供访问数据库元数据的接口,包含数据库、表、字段类型、索引、权限等信息 |
| 底层性能监控数据库,收集服务器运行状态参数,用于性能分析与调优 |
| 基于 |
用于连接mysql服务器、执行sql语句。
mysql [options] [database]-u / --user=name:指定登录用户名-p / --password[=name]:指定登录密码(交互输入更安全)-h / --host=name:指定服务器ip或域名-p / --port=port:指定连接端口(默认3306)-e / --execute=name:执行sql语句并直接退出(适合批处理脚本)# 连接数据库并执行查询后退出 mysql -uroot -p123456 db01 -e "select * from stu;"
用于执行服务器配置检查、状态监控、数据库管理等操作。
mysqladmin [options] command# 删除test01数据库 mysqladmin -uroot -p123456 drop 'test01'; # 查看mysql版本信息 mysqladmin -uroot -p123456 version;
用于解析二进制日志文件,查看数据修改记录,支持按时间、位置过滤日志。
mysqlbinlog [options] log-files1 log-files2 ...-d / --database=name:仅显示指定数据库的操作日志-o / --offset=#:忽略日志开头的前n条命令-r / --result-file=name:将解析后的日志输出到指定文件--start-datetime / --stop-datetime:按时间范围过滤日志--start-position / --stop-position:按日志位置过滤日志快速查询数据库、表、字段、索引等元数据信息。
mysqlshow [options] [db_name [table_name [col_name]]]--count:显示数据库/表的统计信息(表数量、记录数等)-i:显示指定数据库或表的状态信息# 查询test库中每个表的字段数和行数 mysqlshow -uroot -p2143 test --count;
用于备份数据库,生成包含建表语句和数据插入语句的sql文件,支持跨数据库迁移。
基础语法:
# 备份单个数据库 mysqldump [options] db_name [tables] # 备份多个数据库 mysqldump [options] --database/-b db1 [db2 db3...] # 备份所有数据库 mysqldump [options] --all-databases/-a
常用选项:
--add-drop-database:在创建数据库前添加drop database语句--add-drop-table:在创建表前添加drop table语句(默认开启)-n / --no-create-db:不包含数据库创建语句-t / --no-create-info:不包含表创建语句-d / --no-data:仅备份表结构,不包含数据-t / --tab=name:分别生成.sql(表结构)和.txt(数据)文件mysqlimport:用于导入mysqldump -t导出的文本数据文件。
mysqlimport [options] db_name textfile1 [textfile2...]mysqlimport -uroot -p2143 test /tmp/city.txtsource:mysql客户端内的命令,用于导入sql文件。source /root/xxxx.sql工具 | 核心场景 |
| 连接数据库、执行sql脚本、批处理查询 |
| 服务器状态监控、权限刷新、数据库管理 |
| 日志解析、数据恢复、主从复制排查 |
| 数据库备份、跨环境数据迁移 |
| 数据批量导入、sql脚本执行 |
到此这篇关于深度解析mysql存储引擎与索引的文章就介绍到这了,更多相关mysql存储引擎与索引内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论