一、常用命令
1.查看数据库配置的最大连接数
show VARIABLES like 'max_connections'
2.SHOW PROCESSLIST查看当前数据库运行的所有线程、命令、状态、耗时、SQL
SHOW PROCESSLIST
如果是root用户执行,能看到所有用户的当前的连接状态。
如果是普通账号,只能看到当前账号占用的数据库连接。
show proccesslist只能列出前100条,如果想看全数据的话要使用 show full processlist。
Info字段是执行的SQL内容
3.查看当前运行的所有事务
select * from information_schema.INNODB_TRX
trx_id | trx_state | trx_started | trx_requested_lock_id | trx_wait_started | trx_weight | trx_mysql_thread_id | trx_query | trx_operation_state | trx_tables_in_use | trx_tables_locked | trx_lock_structs | trx_lock_memory_bytes | trx_rows_locked | trx_rows_modified | trx_concurrency_tickets | trx_isolation_level | trx_unique_checks | trx_foreign_key_checks | trx_last_foreign_key_error | trx_adaptive_hash_latched | trx_adaptive_hash_timeout | trx_is_read_only | trx_autocommit_non_locking |
3017 | LOCK WAIT | 2020-09-23 15:26:58 | 3017:55:5:11 | 2020-09-23 15:27:15 | 4 | 10 | insert into user (mobile_phone,age) values ('1234',0) | inserting | 1 | 1 | 2 | 1136 | 1 | 2 | 0 | REPEATABLE READ | 1 | 1 | 0 | 0 | 0 | 0 | |
283923312743632 | RUNNING | 2020-09-23 15:26:52 | 3 | 11 | 0 | 1 | 3 | 1136 | 5 | 0 | 0 | REPEATABLE READ | 1 | 1 | 0 | 0 | 0 | 0 |
4.查看当前出现的锁
select * from information_schema.INNODB_LOCKS
解释:
Column name | Description |
---|---|
lock_id | 锁ID |
lock_trx_id | 事务ID, 可以连INNODB_TRX表查事务详情 |
lock_mode | 锁的模式: S, X, IS, IX, S_GAP, X_GAP, IS_GAP, IX_GAP, or AUTO_INC |
lock_type | 锁的类型:RECORD行锁 、表锁 |
lock_table | 加锁的表 |
lock_index | 如果是lock_type='RECORD' 行级锁 ,为锁住的索引,如果是表锁为null |
lock_space | 如果是lock_type='RECORD' 行级锁 ,为锁住对象的Tablespace ID,如果是表锁为null |
lock_page | 如果是lock_type='RECORD' 行级锁 ,为锁住页号,如果是表锁为null |
lock_rec | 如果是lock_type='RECORD' 行级锁 ,为锁住页号,如果是表锁为null |
lock_data | 事务锁住的主键值,若是表锁,则该值为null |
5.查看锁等待的对应关系
select * from information_schema.INNODB_LOCK_WAITS
6.SHOW ENGINE INNODB STATUS 查看Innodb状态
SHOW ENGINE INNODB STATUS
但是该命令只能获取最近一次的死锁信息。
| InnoDB | |
=====================================
2020-09-23 15:45:57 0x5a0 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 4 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 94 srv_active, 0 srv_shutdown, 87271 srv_idle
srv_master_thread log flush and writes: 87365
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 278
OS WAIT ARRAY INFO: signal count 272
RW-shared spins 0, rounds 336, OS waits 167
RW-excl spins 0, rounds 206, OS waits 3
RW-sx spins 0, rounds 0, OS waits 0
Spin rounds per wait: 336.00 RW-shared, 206.00 RW-excl, 0.00 RW-sx
------------
TRANSACTIONS
------------
Trx id counter 3039
Purge done for trx's n:o < 3038 undo n:o < 0 state: running but idle
History list length 23
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 283923312742760, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283923312741016, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283923312740144, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 3038, ACTIVE 4 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
MySQL thread id 11, OS thread handle 3532, query id 553 localhost ::1 root update
insert into user (mobile_phone,age) values ('1234',0)
------- TRX HAS BEEN WAITING 4 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 55 page no 5 n bits 80 index idx_age of table `sid`.`user` trx id 3038 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 11 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000046; asc F;;
1: len 8; hex 8000000000000003; asc ;;
------------------
--------
FILE I/O
--------
I/O thread 0 state: wait Windows aio (insert buffer thread)
I/O thread 1 state: wait Windows aio (log thread)
I/O thread 2 state: wait Windows aio (read thread)
I/O thread 3 state: wait Windows aio (read thread)
I/O thread 4 state: wait Windows aio (read thread)
I/O thread 5 state: wait Windows aio (read thread)
I/O thread 6 state: wait Windows aio (write thread)
I/O thread 7 state: wait Windows aio (write thread)
I/O thread 8 state: wait Windows aio (write thread)
I/O thread 9 state: wait Windows aio (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
572 OS file reads, 1332 OS file writes, 709 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 2.75 writes/s, 2.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 1 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
Hash table size 2267, node heap has 0 buffer(s)
0.00 hash searches/s, 0.50 non-hash searches/s
---
LOG
---
Log sequence number 10942620
Log flushed up to 10942620
Pages flushed up to 10942620
Last checkpoint at 10942611
0 pending log flushes, 0 pending chkp writes
442 log i/o's done, 1.25 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 8585216
Dictionary memory allocated 299078
Buffer pool size 512
Free buffers 254
Database pages 257
Old database pages 0
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 538, created 79, written 804
0.00 reads/s, 0.25 creates/s, 1.25 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 257, unzip_LRU len: 0
I/O sum[17]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=12432, Main thread ID=12576, state: sleeping
Number of rows inserted 1763, updated 7, deleted 34, read 2641
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
具体分析如下:
①.Header
这部分简单的打印,输出的时间,以及自从上次输出的间隔时间。
注:Background thread部分信息为统计信息,即mysql服务启动之后该部分值会一直递增,因为它显示的是自mysqld服务启动之后master线程所有的loop和log刷新操作。
通过对比active和idle的值,可以获知系统整体负载情况。Active的值越大,证明服务越繁忙。
②.BACKGROUND THREAD
参数 | 说明 |
Srv_master_thread loops | Master线程的循环次数,master线程在每次loop过程中都会sleep,sleep的时间为1秒。而在每次loop的过程中会选择active、shutdown、idle中一种状态执行。Master线程在不停循环,所以其值是随时间递增的。 |
Srv_active | Master线程选择的active状态执行。Active数量增加与数据表、数据库更新操作有关,与查询无关,例如:插入数据、更新数据、修改表等。 |
Srv_shutdown | 这个参数的值一直为0,因为srv_shutdown只有在mysql服务关闭的时候才会增加。 |
Srv_idle | 这个参数是在master线程空闲的时候增加,即没有任何数据库改动操作时。 |
Log_flush_and_write | Master线程在后台会定期刷新日志,日志刷新是由参数innodb_flush_log_at_timeout参数控制前后刷新时间差。 |
③. SEMAPHORES 信号量
OS WAIT ARRAY INFO 操作系统等待数组的信息,它是一个插槽数组,innodb使用了多少次操作系统的等待
保留统计(reservation count)显示了innodb分配插槽的频度
信号计数(signal count) 衡量的是线程通过数组得到信号的频度
RW-shared spins:#这行显示读写的共享锁的计数器
RW-excl spins:#这行显示读写的排他锁的计数器
RW-sx spins:#这行显示共享排它锁计数器
④. TRANSACTIONS
包含Innodb 事务(transactions)的统计信息,还有当前活动的事务列表。
transaction id: 这个ID是一个系统变量随时每次新的transaction产生而增加。
Purge done:正在进行清空(purge)操作的transaction ID。你可以通过查看第transaction id和第Purge done ID的区别,明白没有被purge的事务落后的情况。
History listlength:记录了undo spaces内unpurged的事务的个数。
⑤. FILE I/O
显示了I/O Helper thread的状态,包括一些统计信息
pending operations, pending的log和buffer pool thread的fsync()调用
399 OS file:行显示了reads, writes, and fsync()调用次数。
0.00 reads/s…… : 显示了每秒的统计信息
备注:“aio”表示“ 异步I/O(asynchronous I/O).”
⑥. INSERT BUFFER AND ADAPTIVE HASH INDEX
Ibuf:insertbuffer的一些信息,包括free list, segment size
Hash table:显示了hash table的一些信息最后一行显示了每秒进行了多少次hash搜索,以及非hash搜索
⑦. LOG
Log sequence number表示的是redo log buffer中的lsn
Log flushed up to表示的是redo log file中的lsn
Pages flushed up to表示的缓冲池最旧脏页的lsn
Last checkpoint at 指的就是最近一个物理页刷新到磁盘时,它的fil_page_lsn的变量值。
⑧. BUFFER POOL AND MEMORY
当前内存使用状态
Pages read ahead:显示了每秒线性预读跟随机预读的次数
备注:InnoDB 提供了两种预读的方式,一种是 Linear read ahead,由参数innodb_read_ahead_threshold控制,当你连续读取一个 extent 的 threshold 个 page 的时候,会触发下一个 extent 64个page的预读。另外一种是Random read-ahead,由参数innodb_random_read_ahead控制,当你连续读取设定的数量的page后,会触发读取这个extent的剩余page。InnoDB 的预读功能是使用后台线程异步完成。
⑨. ROW OPERATIONS
0 queries inside InnoDB, 0 queries in queue:显示了有多少线程在Innodb内核
read views open inside InnoDB:显示了有多少read view被打开了,一个read view是一致性保证的MVCC “snapshot”
备注:innodb多版本并发(MVCC)通过read view来确定一致性读时的数据库snapshot, innodb的read view确定一条记录能否看到,
在RC隔离级别下,是每个SELECT都会获取最新的read view;
在RR隔离级别下,则是当事务中的第一个SELECT请求才创建read view
7.SHOW STATUS LIKE 'innodb_row_lock_%'查询当前锁性能状态
SHOW STATUS LIKE 'innodb_row_lock_%
查看当前锁性能状态
解释如下:
Innodb_row_lock_current_waits:当前等待锁的数量
Innodb_row_lock_time:系统启动到现在、锁定的总时间长度
Innodb_row_lock_time_avg:每次平均锁定的时间
Innodb_row_lock_time_max:最长一次锁定时间
Innodb_row_lock_waits:系统启动到现在、总共锁定次数
8.查看死锁
SELECT b.trx_state, e.state, e.time, d.state AS block_state, d.time AS block_time
, a.requesting_trx_id, a.requested_lock_id, b.trx_query, b.trx_mysql_thread_id, a.blocking_trx_id
, a.blocking_lock_id, c.trx_query AS block_trx_query, c.trx_mysql_thread_id AS block_trx_mysql_tread_id
FROM information_schema.INNODB_LOCK_WAITS a
LEFT JOIN information_schema.INNODB_TRX b ON a.requesting_trx_id = b.trx_id
LEFT JOIN information_schema.INNODB_TRX c ON a.blocking_trx_id = c.trx_id
LEFT JOIN information_schema.PROCESSLIST d ON c.trx_mysql_thread_id = d.id
LEFT JOIN information_schema.PROCESSLIST e ON b.trx_mysql_thread_id = e.id
ORDER BY a.requesting_trx_id;
二、慢查询相关
查看慢SQL是否启用
show variables like 'log_slow_queries';
开启慢查询命令
set global log_slow_queries = on;
查看慢查询参数,即设置超过多少秒的查询归为了慢查询
show global variables like 'long_query_time';
设置超过1秒就会被认为慢查询
set global long_query_time =1;
查看慢查询存放日志
show variables like 'slow_query_log_file';
查看慢查询
select * from mysql.slow_log;
查询有多少条慢查询记录
show global status like '%Slow_queries%';
打开慢查询的方法
1.Windows下开启MySQL慢查询
MySQL在Windows系统中的配置文件一般是是my.ini找到[mysqld]下面加上
代码如下
log slow queries = F:/MySQL/log/mysqlslowquery.log
long_query_time = 2
2.Linux下启用MySQL慢查询
MySQL在Linux系统中的配置文件一般是是my.cnf找到[mysqld]下面加上
代码如下
log slow queries = /data/mysqldata/slowquery.log
long_query_time = 2
说明:
log slow queries = /data/mysqldata/slowquery.log
为慢查询日志存放的位置,一般这个目录要有MySQL的运行帐号的可写权限,一般都将这个目录设置为MySQL的数据存放目录;
long_query_time = 2中的2表示查询超过两秒才记录;
3.日志分析工具mysqldumpslow
MySQL提供了日志分析工具mysqldumpslow,可以直接在命令行里面输
-s, 是表示按照何种方式排序
c: 访问计数
l: 锁定时间
r: 返回记录
t: 查询时间
al:平均锁定时间
ar:平均返回记录数
at:平均查询时间
-t, 是top n的意思,即为返回前面多少条的数据;
-g, 后边可以写一个正则匹配模式,大小写不敏感的;
例子:
得到返回记录集最多的10个SQL。
mysqldumpslow -s r -t 10 /database/mysql/mysql06_slow.log
得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /database/mysql/mysql06_slow.log
得到按照时间排序的前10条里面含有左连接的查询语句。
mysqldumpslow -s t -t 10 -g “left join” /database/mysql/mysql06_slow.log
另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现刷屏的情况。
mysqldumpslow -s r -t 20 /mysqldata/mysql/mysql06-slow.log | more
三、线上问题排查思路
3.1死锁
参考:https://zhuanlan.zhihu.com/p/261314744?utm_source=wechat_session
1.线上错误日志报警发现死锁异常
2.查看错误日志的堆栈信息
根据1,2步骤可以找到死锁异常时进行回滚事务的具体业务,也就能够找到该事务执行的 SQL 语句
3.查看 MySQL 死锁相关的日志
死锁日志的获取
发生死锁异常后,我们可以直接使用 show engine innodb status
命令获取死锁信息,但是该命令只能获取最近一次的死锁信息。所以,我们可以通过开启 InnoDB 的监控机制来获取实时的死锁信息,它会周期性(每隔 15 秒)打印 InnoDb 的运行状态到 mysqld 服务的错误日志文件中。
InnoDb 的监控较为重要的有标准监控(Standard InnoDB Monitor)和 锁监控(InnoDB Lock Monitor),通过对应的系统参数可以将其开启。
-- 开启标准监控
set GLOBAL innodb_status_output=ON;
-- 关闭标准监控
set GLOBAL innodb_status_output=OFF;
-- 开启锁监控
set GLOBAL innodb_status_output_locks=ON;
-- 关闭锁监控
set GLOBAL innodb_status_output_locks=OFF;
另外,MySQL 提供了一个系统参数 innodb_print_all_deadlocks
专门用于记录死锁日志,当发生死锁时,死锁日志会记录到 MySQL 的错误日志文件中。
`set` `GLOBAL` `innodb_print_all_deadlocks=``ON``;`
出发死锁的例子
TRX1 | TRX2 |
start transaction; | start transaction; |
select * from user where age = 70 for update | select * from user where age = 90 for update; |
select * from user where age = 90 for update; | |
select * from user where age = 70 for update |
死锁日志的分析
通过上述手段,我们可以拿到死锁日志,下图是我做实验触发死锁异常时获取的日志(省略的部分信息)。
------------------------
LATEST DETECTED DEADLOCK
------------------------
2020-10-12 15:38:23 0x20a4
*** (1) TRANSACTION:
TRANSACTION 3337, ACTIVE 15 sec starting index read//说明:TRANSACTION 3337是事务ID, ACTIVE 15 sec 表示事务活动时间,starting index read 为事务当前正在运行的状态,可能的事务状态有:fetching rows,updating,deleting,inserting, starting index read 等状态。
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 4 row lock(s)//说明:
tables in use 1 表示有一个表被使用,locked 1 表示有一个表锁。LOCK WAIT 表示事务正在等待锁,5 lock struct(s) 表示该事务的锁链表的长度为 5,每个链表节点代表该事务持有的一个锁结构,包括表锁,记录锁或 autoinc 锁等。heap size 1136 为事务分配的锁堆内存大小。
4 row lock(s) 表示当前事务持有的行锁个数,通过遍历上面提到的 11 个锁结构,找出其中类型为 LOCK_REC 的记录数。undo log entries 1 表示当前事务有 1 个 undo log 记录,说明该事务已经更新了 1条记录。
MySQL thread id 2, OS thread handle 14904, query id 53 localhost ::1 root Sending data
select * from user where age = 90 for update
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 55 page no 5 n bits 80 index idx_age of table `sid`.`user` trx id 3337 lock_mode X waiting
Record lock, heap no 7 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 8000005a; asc Z;;
1: len 8; hex 8000000000000002; asc ;;*** (2) TRANSACTION:
TRANSACTION 3336, ACTIVE 17 sec starting index read, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 4 row lock(s)
MySQL thread id 3, OS thread handle 8356, query id 54 localhost ::1 root Sending data
select * from user where age = 70 for update
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 55 page no 5 n bits 80 index idx_age of table `sid`.`user` trx id 3336 lock_mode X
Record lock, heap no 7 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 8000005a; asc Z;;
1: len 8; hex 8000000000000002; asc ;;*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 55 page no 5 n bits 80 index idx_age of table `sid`.`user` trx id 3336 lock_mode X waiting
Record lock, heap no 11 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000046; asc F;;
1: len 8; hex 8000000000000003; asc ;;
*** WE ROLL BACK TRANSACTION (2) //说明:事务ID3336被回滚了
------------
TRANSACTIONS
------------
Trx id counter 3338
Purge done for trx's n:o < 0 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 284317694257816, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 3337, ACTIVE 23 sec
5 lock struct(s), heap size 1136, 6 row lock(s)
MySQL thread id 2, OS thread handle 14904, query id 53 localhost ::1 root
TABLE LOCK table `sid`.`user` trx id 3337 lock mode IX
RECORD LOCKS space id 55 page no 5 n bits 80 index idx_age of table `sid`.`user` trx id 3337 lock_mode X
Record lock, heap no 11 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000046; asc F;;
1: len 8; hex 8000000000000003; asc ;;RECORD LOCKS space id 55 page no 3 n bits 96 index PRIMARY of table `sid`.`user` trx id 3337 lock_mode X locks rec but not gap
//说明:
- 记录锁(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
- 间隙锁(LOCK_GAP): lock_mode X locks gap before rec
- Next-key 锁(LOCK_ORNIDARY): lock_mode X
- 插入意向锁(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention
Record lock, heap no 11 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 8; hex 8000000000000002; asc ;;
1: len 6; hex 000000000b1a; asc ;;
2: len 7; hex af000001230110; asc # ;;
3: len 11; hex 3034313234313233313233; asc 04124123123;;
4: len 4; hex 8000005a; asc Z;;Record lock, heap no 16 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 8; hex 8000000000000003; asc ;;
1: len 6; hex 000000000b20; asc ;;
2: len 7; hex b2000001260110; asc & ;;
3: len 2; hex 3730; asc 70;;
4: len 4; hex 80000046; asc F;;RECORD LOCKS space id 55 page no 5 n bits 80 index idx_age of table `sid`.`user` trx id 3337 lock_mode X locks gap before rec
Record lock, heap no 7 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 8000005a; asc Z;;
1: len 8; hex 8000000000000002; asc ;;Record lock, heap no 8 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000078; asc x;;
1: len 8; hex 8000000000000001; asc ;;RECORD LOCKS space id 55 page no 5 n bits 80 index idx_age of table `sid`.`user` trx id 3337 lock_mode X
Record lock, heap no 7 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 8000005a; asc Z;;
1: len 8; hex 8000000000000002; asc ;;
4.根据 binlog 查看死锁相关事务的执行内容
通过死锁日志,我们可以找到最终获得锁事务最后执行的 SQL,但是如果该事务执行了多条 SQL,这些信息就可能不够用的啦,我们需要完整的了解该事务所有执行的 SQL语句。这时,我们就需要从 binlog 日志中获取。
binlog 日志会完整记录事务执行的所有 SQL,借助它,我们就能找到最终获取锁事务所执行的全部 SQL。然后再进行具体的锁冲突分析。
开启binlog方式
1.先看binlog是否开启
show variables like 'log_%';
2.修改配置文件,开启Binlog
如果是Linux则是修改/etc/my.cnf
windonw是修改my.ini
加入一下内容:
server-id=2
log-bin=mysql-bin
binlog_format=ROW
expire_logs_days=30
重启mysql服务
binlog文件:
我们可以使用 MySQL 的命令行工具 Mysqlbinlog
远程获取线上数据库的 binlog 日志。具体命令如下所示:
Mysqlbinlog -hlocalhost -u root -p --read-from-remote-server mysql-bin.000001 --base64-output=decode-rows -v
其中 --base64-output=decode-rows
表示 row 模式 binlog日志,所以该方法只适用于 row 模式的 binlog日志,但是目前主流 MySQL 运维也都是把 binlog 日志设置为 row 模式,所以这点限制也就无伤大雅。-v
则表示将行事件重构成被注释掉的伪SQL语句。
我们可以通过死锁日志中死锁发生的具体事件和最终获取锁事务正在执行的SQL的参数信息找到 binlog 中该事务的对应信息,比如我们可以直接通过死锁日志截图中的具体的时间 10点57分和 Tom1、Teddy2 等 SQL 的具体数据信息在 binlog 找到对应的位置,具体如下图所示。
下图例子不是上面select * from XXX for update的例子,select是不会记录到binlog里面的,截图只是举个例子从binlog中能看到操作的增删改的记录
5.根据上述信息找出两个相互死锁的事务执行的 SQL 操作,根据本系列介绍的锁相关理论知识,进行分析推断死锁原因
6.修改业务代码