一次由于字符集不统一导致的存储过程不使用索引的cook

1、业务反馈登陆失败,初步查询数据库登陆不进去,赶紧进去数据库查看,发现果然线程已经卡住了不少了,并且是同一条sql

show processlist;

state出现的状态为 searching rows for update
2、查看慢日志,出现大量同一访问存储过程卡住的情况
执行时间到40s左右
3、利用information_schema下面和锁有关的三个表查询:

SELECT r.trx_state wating_trx_state,r.trx_id waiting_trx_id,r.trx_mysql_thread_Id waiting_thread,r.trx_query waiting_query,b.trx_state blocking_trx_state,b.trx_id blocking_trx_id,b.trx_mysql_thread_id blocking_thread,b.trx_query blocking_query FROM information_schema.innodb_lock_waits w INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id\G

出现200多组锁等待,而且都是同类型的等待
4、表的大小为80w行数据左右,检查表的索引情况,存在检索字段的索引,如果走索引了,不应该存在这种情况
explain select * from test1 where column_a = 'a' and column_b = 'b'; 结果走索引了已经;这就纳闷了
由于之前遇到过存储过程里面和外面执行语句效率不一样,怀疑是否是存储过程里面的原因
提取出存储语句,进行稍微的更改,利用存储过程返回的结果,查看sql执行效率

CREATE DEFINER=`root`@`localhost` PROCEDURE `ptest1`(
i_usermobile VARCHAR (11),
OUT o_return INT)
label_pro : 
BEGIN

DECLARE p_curym INT ;
declare p_curymd int;
DECLARE p_count INT;
DECLARE CONTINUE HANDLER FOR 1062 SET o_return = -2 ;

SET p_curym = DATE_FORMAT(NOW(), '%Y%m') ;
SET p_curymd = DATE_FORMAT(NOW(), '%Y%m%d') ;
SET p_count = 0;
SET o_return = 0;

explain select * from test1 
where mobile = i_usermobile AND curmonth = p_curym; 
commit;
end

查看到返回的结果

mysql> CALL ptest2('13654390105',@com_mysql_jdbc_outparam_o_return);
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | test2 | NULL | ALL | NULL | NULL | NULL | NULL | 99847 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
1 row in set (0.02 sec)

Query OK, 0 rows affected (0.02 sec)

根本没有走索引,所以
###问题的根本还是单条sql引起的性能问题,需要从存储过程下手,开始没能想到字符集的问题,后面没办法向老大求救
5、查看建表语句,字符集为gbk,

***
ENGINE=InnoDB AUTO_INCREMENT=118297 DEFAULT CHARSET=gbk COMMENT='抽奖机会记录表'

查看存储过程字符集

character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8_general_ci

然后尝试使用,将索引使用的行改成utf8字符集

ALTER TABLE test1 mobile VARCHAR(13) CHARACTER SET utf8;

再次进行flag存储过程的调用

mysql> CALL ptest1('13654390105',@com_mysql_jdbc_outparam_o_return); 
+----+-------------+-------+------------+-------+------------------------+------------------------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+------------------------+------------------------+---------+-------------+------+----------+-------+
| 1 | SIMPLE | test1 | NULL | const | PK_Y_CN201612A1_DRAWCE | PK_Y_CN201612A1_DRAWCE | 46 | const,const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+------------------------+------------------------+---------+-------------+------+----------+-------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

如上图所示,开始使用索引了
6、在现网进行更改行的属性之后,主机负载下降到正常水平。由于处理故障的时候没想到比较特殊的一次经历,也没有保留好现场,靠着回忆和重现完成该笔记了。
7、扩展:
猜想1、如果表字符集和存储过程的字符集统一,是否能够正常使用索引

mysql> show create procedure ptest2\G
*************************** 1. row ***************************
Procedure: ptest2
sql_mode: STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `ptest2`(
i_usermobile VARCHAR (11),
OUT o_return INT)
label_pro : 
BEGIN

DECLARE p_curym INT ;
declare p_curymd int;
DECLARE p_count INT;
DECLARE CONTINUE HANDLER FOR 1062 SET o_return = -2 ;

SET p_curym = DATE_FORMAT(NOW(), '%Y%m') ;
SET p_curymd = DATE_FORMAT(NOW(), '%Y%m%d') ;
SET p_count = 0;
SET o_return = 0;

explain select * from test2 
where mobile = i_usermobile AND curmonth = p_curym; 
commit;
end
character_set_client: gbk
collation_connection: gbk_chinese_ci
Database Collation: utf8_general_ci
1 row in set (0.00 sec)

mysql> show create table test2\G
*************************** 1. row ***************************
Table: test2
Create Table: CREATE TABLE `test2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`mobile` varchar(13) DEFAULT NULL,
`total` int(1) NOT NULL DEFAULT '0' COMMENT '抽奖机会总数',
`used` int(1) NOT NULL DEFAULT '0' COMMENT '使用的抽奖机会',
`curmonth` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `PK_Y_CN201612A1_DRAWCE` (`mobile`,`curmonth`)
) ENGINE=InnoDB AUTO_INCREMENT=118297 DEFAULT CHARSET=gbk COMMENT='抽奖机会记录表'
1 row in set (0.00 sec)

 

除却所在库的字符集为utf8之外,其余皆是gbk,进行实验

mysql> CALL ptest2('13654390105',@com_mysql_jdbc_outparam_o_return);
-> $$
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | test2 | NULL | ALL | NULL | NULL | NULL | NULL | 99847 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

 

 

结果让人失望,存储过程并没有使用索引
猜想2、进行更加规范的统一,将存储过程所在库的字符集也改为gbk

mysql> CALL ptest2('13654390105',@com_mysql_jdbc_outparam_o_return);
-> $$
+----+-------------+-------+------------+-------+------------------------+------------------------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+------------------------+------------------------+---------+-------------+------+----------+-------+
| 1 | SIMPLE | test2 | NULL | const | PK_Y_CN201612A1_DRAWCE | PK_Y_CN201612A1_DRAWCE | 46 | const,const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+------------------------+------------------------+---------+-------------+------+----------+-------+
1 row in set (0.01 sec)

Query OK, 0 rows affected (0.01 sec)

 


成功了,看来存储过程内部使用索引还和数据库的字符集有一定关联
猜想3、在猜想2的基础上将索引字段的字符集改为utf8

mysql> show full columns from test2;
-> $$
+----------+-------------+-----------------+------+-----+---------+----------------+---------------------------------+----------------+
| Field | Type | Collation | Null | Key | Default | Extra | Privileges | Comment |
+----------+-------------+-----------------+------+-----+---------+----------------+---------------------------------+----------------+
| id | int(11) | NULL | NO | PRI | NULL | auto_increment | select,insert,update,references | |
| mobile | varchar(13) | utf8_general_ci | YES | MUL | NULL | | select,insert,update,references | |
| total | int(1) | NULL | NO | | 0 | | select,insert,update,references | 抽奖机会总数 |
| used | int(1) | NULL | NO | | 0 | | select,insert,update,references | 使用的抽奖机会 |
| curmonth | int(11) | NULL | NO | | NULL | | select,insert,update,references | |
+----------+-------------+-----------------+------+-----+---------+----------------+---------------------------------+----------------+
5 rows in set (0.01 sec)

mysql> CALL ptest2('13654390105',@com_mysql_jdbc_outparam_o_return);$$
+----+-------------+-------+------------+-------+------------------------+------------------------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+------------------------+------------------------+---------+-------------+------+----------+-------+
| 1 | SIMPLE | test2 | NULL | const | PK_Y_CN201612A1_DRAWCE | PK_Y_CN201612A1_DRAWCE | 46 | const,const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+------------------------+------------------------+---------+-------------+------+----------+-------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

 

gbk能够兼容utf8的字符集,反过来utf8不能兼容gbk字符集,所以建议建表的ddl还是放在utf8字符集下面处理。

posted @ 2017-07-17 16:37  一头猪的奇妙旅行  阅读(329)  评论(0编辑  收藏  举报