文章目录
- 第一讲:一条SQL查询语句是如何执行的?
- 第二讲:一条SQL更新语句是如何执行的?
- 第三讲:事务隔离,为什么你改了我还看不见
- 第四讲:深入浅出索引(上)
- 第五讲:深入浅出索引(下)
- 第六讲:全局锁和表锁,给表加个字段怎么有这么多阻碍?
- 第七讲:行锁功过,怎么减少行锁对性能的影响?
- 第八讲:事务到底是隔离的还是不隔离的?
- 第九讲:普通索引和唯一索引,应该怎么选择?
- 第十讲:MySQL为什么有时候会选错索引?
- 第十一讲:怎么给字符串字段加索引?
- 第十二讲:为什么我的MySQL会“抖”一下?
- 第十三讲:为什么表数据删掉一半,表文件大小不变?
- 第十四讲:count(*)这么慢,我该怎么办?
- 第十六讲:order by是怎么工作的?
- 第十七讲:为什么我只查一行的语句,也执行这么慢?
第一讲:一条SQL查询语句是如何执行的?
- 连接器:在完成了经典的TCP握手后,开始认证身份,以及校验权限
- 分析器:词法分析和语法分析
- 优化器:使用哪个索引、各个表的连接顺序
- 执行器:调用引擎接口取数据,判断是否符合条件,符合条件的放在结果集
- 存储引擎:与磁盘交互,获取数据,返回给执行器
第二讲:一条SQL更新语句是如何执行的?
两个重要日志
- bin log:server层、追加写、记录更新SQL语句
- redo log:存储引擎、覆盖写、记录更新数据
更新过程
- redo log - prepare:更新数据记录到内存
- bin log:更新语句记录到磁盘
- redo log - commit:更新数据记录到磁盘
第三讲:事务隔离,为什么你改了我还看不见
事务执行语句
- 事务启动语句:begin、start transaction
- 事务提交语句:commit
- 事务回滚语句:rollback
- 自动提交关闭:set autocommit = 0
第四讲:深入浅出索引(上)
创建索引语句
create table T(
id int primary key,
k int not null,
name varchar(16),
index(k)
)engine = InnoDB;
主键索引和普通索引的区别
- 主键索引不需要回表。普通索引如果没有覆盖索引就需要回表
第五讲:深入浅出索引(下)
几个重要的概念
- 覆盖索引:普通索引如果是覆盖索引,那就不用回表
- 最左前缀原则:索引内的多个字段从左到右,字段的内容也是从做到右
- 索引下推:可以减少回表次数
第六讲:全局锁和表锁,给表加个字段怎么有这么多阻碍?
全局锁
- 全局读锁命令:Flush tables with read lock
- 典型使用场景:全库逻辑备份
表级锁
- 表锁(数据锁):lock tables t1 read, t2 write;(加锁)、unlock table;(解锁)
- 元数据锁MDL(表结构锁):不需要显式使用,在访问一个表的时候就会被自动加上
行级锁
- MySQL的行级锁是在引擎层由各个引擎自己实现的,但并不是所有的引擎都支持行锁
第七讲:行锁功过,怎么减少行锁对性能的影响?
重要概念
- 两阶段锁:上锁阶段和解锁阶段
- 死锁:两阶段锁会导致死锁
死锁解决
- 设置死锁超时:innodb_lock_wait_timeout = 50
- 设置死锁检测:innodb_deadlock_detect = on
- 控制并发度:控制操作同一行数据的线程数
- 将一行改成逻辑上的多行来减少锁冲突
第八讲:事务到底是隔离的还是不隔离的?
两种读
- 当前读:读最新提交的数据
- 一致性读:遵循隔离机制来读取数据
不同操作的隔离机制
- 更新操作:读已提交(当前读)
- 查询操作:可重复读(一致性读)
思考一下为什么?
事务B查到K的值是3,而事务A查到K的值是1
第九讲:普通索引和唯一索引,应该怎么选择?
因为唯一索引的更新操作可能无法使用change buffer,所以一般推荐选择普通索引。
第十讲:MySQL为什么有时候会选错索引?
查询索引信息
- 命令:show index from table_name;
影响索引选择的因素
- 扫描行数:优先选择扫描行数小的索引
- 区分度:区分度越大,扫描行数越小
- 回表:如果普通索引+回表消耗的性能大于全表查询,那么就会导致索引失效
- 字段排序:
索引选择错误的解决办法
- 执行修正命令:analyze table t;
- 强制使用索引:select * from t force(a) where …
- 引导使用索引:order by b变成order by b,a
- 删除误用的索引
第十一讲:怎么给字符串字段加索引?
- 命令:alter table user add index index_email(email6));
- 原则:在区分度和索引长度之间权衡利弊
第十二讲:为什么我的MySQL会“抖”一下?
主要分为两种场景,导致MySQL突然抖一下
- redo log写满了,要刷新redo log内存(日志写回磁盘)
- 缓冲池buffer pool不够用了,要先将脏页写到磁盘(数据页写回磁盘)
InnoDB刷脏页的控制策略
- 影响InnoDB刷脏页能力的因素:脏页比例、redo log写盘速度
- 通过命令可以获取磁盘的IOPS,然后将InnoDB的innodb_io_capacity设置为IOPS
- InnoDB的实际刷盘速度,内部计算出一个R%,然后乘以innode_io_capacity,就是刷盘速度
第十三讲:为什么表数据删掉一半,表文件大小不变?
- delete命令其实只是把记录的位置,或者数据页标记为了“可复用”,但磁盘文件的大小是不会变的。也就是说,通过delete命令是不能回收表空间的。这些可以复用,而没有被使用的空间,看起来就像是“空洞”。
第十四讲:count(*)这么慢,我该怎么办?
- 随着系统中记录数越来越多,select count(*) from table_name也会执行得越来越慢
- 可以使用缓存、数据表来存储count(*)的值
- 不同count的性能:count(字段)< count(主键id)< count(1)≈ count(*)
第十六讲:order by是怎么工作的?
select city, name, age from T where city = '杭州' order by name limit 1000;
- 全字段排序:取出整行数据,然后取city、name、age放入sort_buffer排序,不需要回表
- rowid排序:取出整行数据,部分字段放入sort_buffer排序,需要回表
- 如果(city, name)是索引,那么上面的order by就不需要排序
第十七讲:为什么我只查一行的语句,也执行这么慢?
select * from table where id = 1
- 等锁释(MDL锁、行锁)
- 等flush刷盘
select * from table where c = 50000 limit 1
- 查询慢:全表扫描、
- 事务:一致性读导致的undo很多次,当前读直接读最新值