定位MySQL锁和事务问题的两大利器

序言

MySQL服务器和独立的存储引擎都可以设置锁。一般来说锁分为读锁(或叫共享锁)和写锁(排它锁)。读锁允许并发线程读取加锁的数据,但禁止写数据;相反,写锁禁止其他线程读写操作。

MySQL有4种类型的锁:表锁、行锁、页锁、元数据锁:

  1. 表锁:顾名思义会锁住整张表,myisam引擎就实现的是表锁;
  2. 行锁:粒度更细,仅锁住正在被线程访问的任意一行或多行,因此同一个表中的其他行仍然可以被其他并发线程访问,innodb引擎是支持行锁的;
  3. 页锁:仅在少见的BDB引擎中存在,比较少见;
  4. 元数据锁:元数据是DDL语句的变更信息,如create、drop、alter等,MySQL 5.5版本加入的新特性。目的是为了解决线程可以在其他线程中的并发事务使用相同表的情况下修改表定义或是删除表的问题。

在并发的环境下极易引起锁竞争问题,比如获取锁等待,也容易导致死锁。在行锁级别,死锁是无法100%避免的,那么当出现锁问题时,有什么方法定位到原因?又如何解决锁问题?

定位利器一:show processlist

show processlist可以查看所有正在执行的sql线程:

mysql> show processlist\G
*************************** 1. row ***************************
     Id: 193
   User: root
   Host: localhost:51760
     db: NULL
Command: Query
   Time: 0
  State: starting
   Info: show processlist

输出字段解释如下:

  • Id:执行的线程id
  • User、Host、db: 客户端连接的选项
  • Command:线程中执行的命令
  • Time:从线程开始执行命令道现在消耗的时间,单位秒
  • State:内部状态
  • Info:表明线程当前正在进行的工作

推荐使用查询 select * from information_schema.processlist表,输出结果和show processlist完全一致,不过由于保持在表中可以根据需要排序查询结果,如果按特定User或者按执行时间排序等。

获取查询执行时间最长的10个线程
select * from information_schema.processlist order by Time desc limit 10\G

通过这个show processlist利器,可以很直观看出异常线程,及对应执行的sql,一目了然。

利器二:show engine innodb status

这个利器是专门针对Innodb引擎的,可以确定InnoDB中的请求是否阻塞。该命令在分析并发多语句事务的作用的时候尤为有用,输出内容更加详细。

show engine innodb status

输出信息比较多,对于锁和事务相关的重点关注TRANSACTIONS以下部分:

------------
TRANSACTIONS
------------
Trx id counter 10753
Purge done for trx's n:o < 10729 undo n:o < 0 state: running but idle
History list length 700
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 422098131918480, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 10752, ACTIVE 12 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 211, OS thread handle 140622868731648, query id 10380 localhost 127.0.0.1 root updating
update  t_attr2 set id=213 where id=212
------- TRX HAS BEEN WAITING 12 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 124 page no 7 n bits 96 index PRIMARY of table `wechat`.`t_attr2` trx id 10752 lock_mode X locks rec but not gap waiting
Record lock, heap no 23 PHYSICAL RECORD: n_fields 8; compact format; info bits 32
 0: len 8; hex 80000000000000d4; asc         ;;
 1: len 6; hex 0000000029fd; asc     ) ;;
 2: len 7; hex 25000001430c75; asc %   C u;;
 3: len 30; hex 687474703a2f2f777566617a7563652e636f6d2f73656c6563742e68746d; asc http://wufazuce.com/select.htm; (total 31 bytes);
 4: len 20; hex 3131352e3139372e3234362e3533202020202020; asc 115.197.246.53      ;;
 5: len 30; hex e4b8ade59bbde58d8ee4b89ce6b599e6b19fe79c81e69dade5b79ee5b882; asc                               ; (total 36 bytes);
 6: len 30; hex 4d6f7a696c6c612f352e30202857696e646f7773204e5420362e313b2057; asc Mozilla/5.0 (Windows NT 6.1; W; (total 126 bytes);
 7: len 4; hex 58ab9f58; asc X  X;;

------------------
---TRANSACTION 10749, ACTIVE 94 sec
2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 2
MySQL thread id 210, OS thread handle 140622868932352, query id 10008 localhost 127.0.0.1 root cleaning up

从上面的信息可以很容易看到事务的信息,包括执行的时长,执行的sql语句,线程id,查询id等;

相关参数和观察表

最后介绍有几个和锁、事务有关的参数及系统表:
- innodb_wait_timeout参数:innodb获取锁等待超时时间,默认50秒;
- INFORMATION_SCHEMA.innodb_locks表:保持已获得锁的信息;
- INFORMATION_SCHEMA.innodb_lock_waits表:保持等待锁的信息;
- INFORMATION_SCHEMA.innodb_trx表:保持正在执行的事务的信息

总结

在MySQL并发条件下出现锁抢占或死锁的很常见的,遇到锁问题当务之急是定位出现问题的线程,再定位出正在执行的sql,那么修复就很简单了。一般来说,为了避免锁抢占或死锁的出现,一定要保持事务短小精悍。

你可能感兴趣的:(MySQL)