由于加了 month() 函数操作,MySQL 无法再使用索引快速定位功能,而只能使用全索引扫描。
如下:
select count(*) from tradelog where month(t_modified)=7; //查询表中所有年份的7月份纪录个数。
其中的 month()是函数,假设表中只有 21,22年和23年的数据,那么改进的SQL如下:
mysql> select count(*) from tradelog where
(t_modified >= ‘2021-7-1’ and t_modified<‘2021-8-1’) or
(t_modified >= ‘2022-7-1’ and t_modified<‘2022-8-1’) or
(t_modified >= ‘2023-7-1’ and t_modified<‘2023-8-1’);
select * from tradelog t where t.tradid = 110717; //其中的 tradid 类型为 varchar2(32);
MySQL 规定当 ‘字符串’和 ‘整型’比较的时候,会把 ‘字符串’类型先转化为‘整型’。
所以上面的语句就相当于:
mysql> select * from tradelog where CAST(tradid AS signed int) = 110717;
也就是说,这条语句触发了我们上面说到的规则:对索引字段做函数操作,优化器会放弃走树搜索功能。
下面这条查询语句会导致走全局扫描吗?
select * from tradelog where id = “83126”; // 其中 id 是 整型的
答案是不会的,为啥呢?因为上面说过了,当 ‘字符串’和 ‘整型’比较的时候,会把 ‘字符串’类型先转化为‘整型’,
所以上面的语句相当于:
mysql> select * from tradelog where id = CAST(‘110717’ AS signed int);
没有对索引字段 id 进行函数操作,所以不会走全局扫描。
-------------------------------------------------- MySQL 行锁 -------------------------------------------------------------
在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。
因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。
间隙锁和行锁合称 next-key lock,每个 next-key lock 是前开后闭区间。
如果没有特别说明,我们把间隙锁记为开区间,把 next-key lock 记为前开后闭区间。
注意,间隙锁是在可重复读隔离级别下才会生效的。所以,你如果把隔离级别设置为读提交的话,就没有间隙锁了。
我总结的加锁规则里面,包含了两个“原则”、两个“优化”和一个“bug”。
原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区间。
原则 2:查找过程中访问到的对象才会加锁。
优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。