首页 热点资讯 义务教育 高等教育 出国留学 考研考公
您的当前位置:首页正文

MySQL数据库中把int转化varchar引发的慢查询

2022-03-04 来源:华拓网
MySQL数据库中把int转化varchar引发的慢查询

最近⼀周接连处理了2个由于int向varchar转换⽆法使⽤索引,从⽽引发的慢查询。

CREATE TABLE `appstat_day_prototype_201305` (`day_key` date NOT NULL DEFAULT '1900-01-01',`appkey` varchar(20) NOT NULL DEFAULT '',`user_total` bigint(20) NOT NULL DEFAULT '0',`user_activity` bigint(20) NOT NULL DEFAULT '0',`times_total` bigint(20) NOT NULL DEFAULT '0',`times_activity` bigint(20) NOT NULL DEFAULT '0',`incr_login_daily` bigint(20) NOT NULL DEFAULT '0',`unbind_total` bigint(20) NOT NULL DEFAULT '0',`unbind_activitys` bigint(20) NOT NULL DEFAULT '0',PRIMARY KEY (`appkey`,`day_key`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8

mysql> explain SELECT * from appstat_day_prototype_201305 where appkey = xxxxx and day_key between '2013-05-23' and '2013-05-30';+----+-------------+------------------------------+------+---------------+------+---------+------+----------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+------------------------------+------+---------------+------+---------+------+----------+-------------+

| 1 | SIMPLE | appstat_day_prototype_201305 | ALL | PRIMARY | NULL | NULL | NULL | 19285787 | Using where |+----+-------------+------------------------------+------+---------------+------+---------+------+----------+-------------+1 row in set (0.00 sec)

mysql> explain SELECT * from appstat_day_prototype_201305 where appkey = 'xxxxx' and day_key between '2013-05-23' and '2013-05-30';+----+-------------+------------------------------+-------+---------------+---------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+------------------------------+-------+---------------+---------+---------+------+------+-------------+

| 1 | SIMPLE | appstat_day_prototype_201305 | range | PRIMARY | PRIMARY | 65 | NULL | 1 | Using where |+----+-------------+------------------------------+-------+---------------+---------+---------+------+------+-------------+1 row in set (0.00 sec)

从上⾯可以很明显的看到由于appkey是varchar,⽽在where条件中不加'',会引发全表查询,加了就可以⽤到索引,这扫描的⾏数可是天差地别,对于服务器的压⼒和响应时间⾃然也是天差地别的。我们再看另外⼀个例⼦:

*************************** 1. row ***************************Table: poll_joined_151

Create Table: CREATE TABLE `poll_joined_151` (`poll_id` bigint(11) NOT NULL,`uid` bigint(11) NOT NULL,

`item_id` varchar(60) NOT NULL,

`add_time` int(11) NOT NULL DEFAULT '0',

`anonymous` tinyint(1) NOT NULL DEFAULT '0',`sub_item` varchar(1200) NOT NULL DEFAULT '',

KEY `idx_poll_id_uid_add_time` (`poll_id`,`uid`,`add_time`),

KEY `idx_anonymous_id_addtime` (`anonymous`,`poll_id`,`add_time`)) ENGINE=InnoDB DEFAULT CHARSET=utf8

SELECT * FROM poll_joined_151 WHERE poll_id = '2348993' AND anonymous =0 ORDER BY add_time DESC LIMIT 0 , 3*************************** 1. row ***************************id: 1

select_type: SIMPLEtable: poll_joined_151type: ref

possible_keys: idx_poll_id_uid_add_time,idx_anonymous_id_addtimekey: idx_anonymous_id_addtimekey_len: 9

ref: const,constrows: 30240

Extra: Using where

从上⾯的例⼦看,虽然poll_id的类型为bigint,但是SQL中添加了'',但是这个语句仍然⽤到了索引,虽然扫描⾏数也不少,但是能⽤到索引就是好SQL。那么⼀个⼩⼩的''为什么会有这么⼤的影响呢?根本原因是因为MySQL在对⽂本类型和数字类型进⾏⽐较的时候会进⾏隐式的类型转换。以下是5.5官⽅⼿册的说明:

If both arguments in a comparison operation are strings, they are compared as strings.两个参数都是字符串,会按照字符串来⽐较,不做类型转换。If both arguments are integers, they are compared as integers.两个参数都是整数,按照整数来⽐较,不做类型转换。

Hexadecimal values are treated as binary strings if not compared to a number.⼗六进制的值和⾮数字做⽐较时,会被当做⼆进制串。

If one of the arguments is a TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed. This is done to be more ODBC-friendly. Note that this is not done for the argum有⼀个参数是 TIMESTAMP 或 DATETIME,并且另外⼀个参数是常量,常量会被转换为 timestamp

If one of the arguments is a decimal value, comparison depends on the other argument. The arguments are compared as decimal values if the other argument is a decimal or integer value, or as floating-point values if the other argument is a floating-point value.有⼀个参数是 decimal 类型,如果另外⼀个参数是 decimal 或者整数,会将整数转换为 decimal 后进⾏⽐较,如果另外⼀个参数是浮点数,则会把 decimal 转换为浮点数进⾏⽐较In all other cases, the arguments are compared as floating-point (real) numbers.所有其他情况下,两个参数都会被转换为浮点数再进⾏⽐较

根据以上的说明,当where条件之后的值的类型和表结构不⼀致的时候,MySQL会做隐式的类型转换,都将其转换为浮点数在⽐较。对于第⼀种情况:⽐如where string = 1;

需要将索引中的字符串转换成浮点数,但是由于'1',' 1','1a'都会⽐转化成1,故MySQL⽆法使⽤索引只能进⾏全表扫描,故造成了慢查询的产⽣。

mysql> SELECT CAST(' 1' AS SIGNED)=1;+-------------------------+

| CAST(' 1' AS SIGNED)=1 |+-------------------------+| 1 |

+-------------------------+1 row in set (0.00 sec)

mysql> SELECT CAST(' 1a' AS SIGNED)=1;+--------------------------+

| CAST(' 1a' AS SIGNED)=1 |+--------------------------+| 1 |

+--------------------------+

1 row in set, 1 warning (0.00 sec)

mysql> SELECT CAST('1' AS SIGNED)=1;+-----------------------+

| CAST('1' AS SIGNED)=1 |+-----------------------+| 1 |

+-----------------------+1 row in set (0.00 sec)

同时需要注意⼀点,由于都会转换成浮点数进⾏⽐较,⽽浮点数只有53bit,故当超过最⼤值的时候,⽐较会出现问题。对于第⼆种情况:

由于索引建⽴在int的基础上,⽽将纯数字的字符串可以百分百转换成数字,故可以使⽤到索引,虽然也会进⾏⼀定的转换,消耗⼀定的资源,但是最终仍然使⽤了索引,不会产⽣慢查询。

mysql> select CAST( '30' as SIGNED) = 30;+----------------------------+

| CAST( '30' as SIGNED) = 30 |+----------------------------+| 1 |

+----------------------------+1 row in set (0.00 sec)

以上所述是⼩编给⼤家介绍的MySQL数据库中把int转化varchar引发的慢查询 ,希望对⼤家有所帮助,如果⼤家有任何疑问请给我留⾔,⼩编会及时回复⼤家的。在此也⾮常感谢⼤家对⽹站的⽀持!

因篇幅问题不能全部显示,请点此查看更多更全内容