mysql中的隐式类型转换?
mysql innodb有行锁的实现,行锁会针对当前读进行加排他锁。
1.对索引字段做函数操作会导致索引失效,破坏索引的有序性,优化器不走b+树搜索
1 | select * from trade_log where month(t_modify)=7; |
上面的语句没办法使用到索引,不可以在索引的字段上加函数操作,这里复杂的函数操作导致了破坏索引的扫描,但是即使类似where id+1=1000这种操作优化器也并不会做优化操作,需要写成where id= 1000-1。
隐式字符编码转换导致索引失效
在应用过程中通常会有字段字符集不统一的问题,如a表字段order_code为utf8mb4,关联 b表retail_order_code字段字符集为utf8。utf8mb4为utf8的超集合,也就是如果用a表取关联b表:
1 | select * from a,b where a.order_code=b.retail_order_code; |
实际上这句话在被连接的表上的逻辑==:
1 | select * from b where convert(b.retail_order_code using utf8mb4) =a.value; |
所以隐式字符编码转换索引失效的条件:连接过程中被驱动的表的索引字段加函数操作。
解决的方法:
1.改字符集,改成同样的字符集。
2.将驱动表的字符集用函数操作改为被驱动表的字符集。
1 | select * from a,b where convert(a.order_code using utf8) = b.retail_order_code; |
上面的语句在被连接表的逻辑变成:
1 | select * from b where b.retail_order_code=a.value;//字符集一致,不需要做索引函数操作 |
隐式类型转换
在mysql的数据结构中,varchar和int的类型转换级别是不一样的,参考上面的分析方法,我们通过实践语句:
1 | select "10">9;//输出 1代表将字符串转成了数字 |
那么我们可以得出结论,如果一个表a中有个varchar(32)的字段为索引字段order_code, 但是我们执行语句:
1 | select * from a where order_code=1111;//会导致全表扫描。 |
便于理解,我们将其定义为一个父集一个子集,父集可以自动convert转换为子集。上面utf8为父集,utf8mb4为子集,int为子集,varchar为父集。如果父集为索引字段,子集为查询的字段,那么会出现索引失效。
问题记录
1 | Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and (utf8_general_ci,IMPLICIT) for operation '=' |
虽然都是utf8,但是mysql在比较的时候无法用具体的字符集进行比较,需要强制指定字符集:
1 | -- convert(t1.order_code using utf8) |
值得注意的是convert(t1.order_code using uft8) COLLATE utf8_unicode_ci和convert(t1.order_code using utf8)虽然让字符集标准化了,通过explain我们发现:
1 | 1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 186 100.00 NULL |
sql查询日志:
1 | # Query_time: 0.002302 Lock_time: 0.000244 Rows_sent: 186 Rows_examined: 372 一次回表查询 |
join操作在驱动表上加父集合(convert using utf8)使用索引。
Using join buffer (Block Nested Loop)
BNL算法建立在join操作的基础上,join操作有一个内表一个外表,外表和内表两层循环。BNL算法取出100条放到join buffer中,内层循环直接从buffer中全部匹配出来100条,这样操作减少了磁盘io