测试数据
CREATE TABLE IF NOT EXISTS `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`num` int(11) NOT NULL,
`num_char` varchar(50) NOT NULL,
PRIMARY KEY (`id`),
KEY `num` (`num`),
KEY `num_char` (`num_char`),
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
插入随机数400000条记录,num和num_char内容一样,只是类型不同。分别建立索引。
测试过程
数值字段类型匹配查询
EXPLAIN SELECT * FROM `test` WHERE num = 837159
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE test ref num num 4 const 12
状态 时间
starting 0.000079
Opening tables 0.000014
System lock 0.000005
Table lock 0.000008
init 0.000025
optimizing 0.000008
statistics 0.000055
preparing 0.000013
executing 0.000002
Sending data 0.000371
end 0.000005
query end 0.000002
freeing items 0.000074
logging slow query 0.000002
cleaning up 0.000003
显示行 0 – 12 (13 总计, 查询花费 0.0016 秒)
查询结果正常,正确使用了索引。
数值字段类型不匹配查询
EXPLAIN SELECT * FROM `test` WHERE num = ’837159′
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE test ref num num 4 const 12
状态 时间
starting 0.000069
Opening tables 0.000015
System lock 0.000004
Table lock 0.000007
init 0.000024
optimizing 0.000009
statistics 0.000148
preparing 0.000014
executing 0.000002
Sending data 0.000321
end 0.000004
query end 0.000002
freeing items 0.000063
logging slow query 0.000001
cleaning up 0.000003
显示行 0 – 12 (13 总计, 查询花费 0.0011 秒)
查询结果正常,正确的使用了索引,看来MySQL把查询关键字’837159′隐式转换为了数值类型进行比较。
字符字段类型匹配查询
EXPLAIN SELECT * FROM `test` WHERE num_char = ’837159′
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE test ref num_char num_char 52 const 13 Using where
查询结果正常,正确的使用了索引,但是key长度比数值型要长,整体性能肯定比数值型要差。
状态 时间
starting 0.000197
Opening tables 0.000031
System lock 0.000013
Table lock 0.000017
init 0.000088
optimizing 0.000022
statistics 0.000284
preparing 0.000039
executing 0.000005
Sending data 0.001232
end 0.000017
query end 0.000005
freeing items 0.001041
logging slow query 0.000004
cleaning up 0.000007
显示行 0 – 12 (13 总计, 查询花费 0.0065 秒)
字符字段类型不匹配查询
EXPLAIN SELECT * FROM `test` WHERE num_char = 837159
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE test ALL num_char NULL NULL NULL 400348 Using where
状态 时间
starting 0.000072
Opening tables 0.000013
System lock 0.000011
Table lock 0.000008
init 0.000025
optimizing 0.000008
statistics 0.000036
preparing 0.000012
executing 0.000002
Sending data 0.244489
end 0.000008
query end 0.000003
freeing items 0.000095
logging slow query 0.000002
cleaning up 0.000004
显示行 0 – 12 (13 总计, 查询花费 0.2451 秒)
查询结果异常,没有正确使用索引,进行了全表扫描。关于这个问题,没有找到合理的解释,分析有可能MySQL把字段的每个值转换为数字然后和关键字进行比较,造成的和索引类型不一致,导致索引失效。并且由于转换带来的开销,使整个查询效率更加低效。
特殊情况
从测试情况来看,如果字段为数值型,关键字加不加引号似乎影响并不大,但是在一种特殊情况下会有比较大的影响。
例:
UPDATE `test` SET num_char = ‘aaa’ WHERE num = ’837159′
13 row(s) affected. ( 查询花费 0.0044 秒 )
update `test` set num_char=’aaa’ WHERE num = ’83715999999′
0 row(s) affected. ( 查询花费 4.2821 秒 )
UPDATE `test` SET num_char = ‘aaa’ WHERE num = 83715999999
0 row(s) affected. ( 查询花费 0.0032 秒 )
当进行更新操作时正常情况下没有异常,但是如果数值超出了int的范围加引号的时候就会有相当大的问题。具体为什么出现这种情况还不是很清楚,需要进一步深入分析。
总结
- 查询关键字要保证和查询字段类型一致,在类型不一致时MySQL需要进行隐式转换,转换成相同的类型才能进行比较。隐式转换可能会使索引失效,严重影响系统性能。尤其是字段为字符型但是查询关键字没有加引号的情况下,开销相当的巨大。
- 如果类型不一致,有一方为数值行,MySQL会优先转换成数值型。
- 数值类型的查询效率要比字符型高。
- 如果字段为字符型,查询关键字一定要加上引号。
- 如果字段为数值型,查询关键字最好不要加引号,虽然只是很小的可能会出现问题,但是性能影响巨大,至于安全隐患可以使用intval函数强制转换来避免。
- 总之要避免隐式转换,隐式转换本身会有系统开销,而且会造成不可预知的影响。