Mysql复习
1. 什么是MVCC
多版本并发控制:读取数据时通过一种类似快照的方式将数据保存下来这样读锁和写锁就不冲突了,不同的事务session会看到自己特定版本的数据
mvcc 只在read committed(已提交读)和 repeatable read(可重复读) 两个隔离级别下工作。其他两个隔离级别和MVCC不兼容,因为READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行。而serializable 则会对所有读取的行都枷锁。
聚簇索引记录中有两个必要的隐藏列:
trx_id:用来存储每次对某条聚簇索引记录进行修改的时候的事务id
roll_poninter:每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中。这个roll_pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本)
已提交读和可重复读的区别就在于它们生成ReadView的策略不同。
id | name | trx_id | Roll_pointer |
---|---|---|---|
1 | 小狼2 | 100 | 上一个版本的地址 |
1 | 小狼1 | 60 | 上一个版本的地址 |
1 | 小狼 | 50 |
开始事务时创建readview readview维护当前活动的事务id,即未提交的事务id,排序生成一个数组访问数据,获取数据中的事务id(获取的是事务id最大的记录),对比readview
如果在readview的左边(比readview都小),可以访问()在左边意味着该事务已经提交
如果在readview的右边(比readview都大)或者就在readview中,不可以访问,获取roll_pointer,取上一版本重新对比(在右边意味着,该事务在readview生成以后出现,在readview中意味着该事务还未提交)
已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的readview,而可重复读隔离级别则在第一次读的时候生成一个readview,之后的读都复用之前的readview。
这就是mysql 的MVCC通过版本链,实现多版本,可并发读-写 写-读。通过readview生成策略的不同实现不同的隔离级别。
事务隔离级别
脏读:在事务进行过程中,读取了其他事务未提交的事务
不可重复读:在一个事务过程中,多次查询的结果不一致
幻读:在一个事务过程中,用同样的操作查询数据,得到的记录数不相同
加锁:
1:脏读 在修改时加排他锁,直到事务提交才释放。读取时加共享锁,读完释放锁
2:不可重复读 读数据时加共享锁,写数据时加排他锁
3:幻读 加范围锁
MVCC
Mysql中like模糊查询如何优化
- 合理使用索引,前缀匹配
- 使用反向索引,后缀匹配
- 限制扫描范围
- 使用缓存
- 使用专业工具
count(1) count(*) count(列名)的区别
count(1)
统计表中行的数量,包含所有行,不论行中的值是什么
每一行以常数1进行计算,即对于每一行都会计数一次,因此它的计算与count(*)基本相同。效率:
在某些数据库实现中,count1 和count(*) 相同,功能上是等效的count(*)
统计表中所有行的的数量,包括所有列,无论列的值是否为NULL
按行统计时,不考虑列的具体值和数据类型
效率:最常用的方法之一,特别是在InnoDB引擎中,count(*) 通常是最优的计数形式,因为它被优化为直接统计页上的数目。
count(column_name)
统计指定列中非NULL值的行数
只计数该列中有非NULL值的行。因此,如果某行在coulumn_name中的值为null,则不会计入总数。适用场景:
用于需要过滤掉null值时,例如,统计某个特定字段有值的记录数
性能对比
- 现在数据库通常对count(*) 和count(1)进行了优化,因此二者性能基本相似
- count( column_name) 可能慢一些,特别是当列中有大量的NULL值时,因为需要遍历和检查每一个值
索引失效反而会提升效率
- 小表查询
对于非常小的表,Mysql可能会选择全表扫描 - 读取大部分或者所有行(如30%或更多)
- 低选择性索引
- 频繁更新的表
- 复杂查询的优化选择
对于复杂的多表联接查询,优化器有时候可以选择执行计划中不使用某个索引 - 数据分布与优化器误判
- 小表查询
NULL值问题