
查看MySQL数据库的死锁日志
1 使用终端或命令提示符登录到MySQL,输入命令:mysql -h xxxxxxxxxx -P 3306 -u username -p 解释:xxxxxxxxxx是数据库IP地址,username是数据库用户名,输入命令后,会让你输入username对应的密码,就可以登录了
2 如何查看MySQL数据库的死锁信息 在MySQL客户端下输入命令: show engine innodb status \G;
3 如何定位MySQL数据库的死锁信息 在打印出来的信息中找到“LATEST DETECTED DEADLOCK”一节内容,看图中红线
4 如何分析日志,定位死锁原因 看3里面的图,紫色划线部分 分析: 事务1,等待 RECORD LOCKS space id 553 page no 376 n bits 368 index `index_user_id` of table `tbj``score_user`,这个位置的X锁 事务2,持有 RECORD LOCKS space id 553 page no 376 n bits 368 index `index_user_id` of table `tbj``score_user`这个地方的S锁 事务2,等待这个地方的X锁 理论上这个事务2是可以提交的不会,死锁,但是这个事务日志只打印最后一部分死锁,信息,这里面隐含的条件是,事务1也持有 RECORD LOCKS space id 553 page no 376 n bits 368 index `index_user_id` of table `tbj``score_user`这个地方的S锁,这样,事务2不能加X锁,同时事务1也不能加X锁,产生死锁。
当你开始执行一个 ALTER ,而你遇到了可怕的“元数据锁定等待”,我敢肯定你一定遇见过。我最近遇到了一个案例,其中被更改的表要执行一个很小范围的更新(<100行)。ALTER 在负载测试期间一直等待了几个小时。在停止负载测试后,ALTER 按预期在不到一秒的时间内就完成了。那么这里发生了什么?
检查外键
每当有奇数次锁定时,我的第一直觉就是检查外键。当然这张表有一些外键引用了一个更繁忙的表。但是这种行为似乎仍然很奇怪。对表运行 ALTER 时,会针对子表请求一个 SHARED_UPGRADEABLE 元数据锁。还有针对父级的 SHARED_READ_ONLY 元数据锁。
我们来看看如何根据文档获取元数据锁定[1]:
如果给定锁定有多个服务器,则首先满足最高优先级锁定请求,并且与 max_write_lock_count系统变量有关。写锁定请求的优先级高于读取锁定请求。
[1]:>
请务必注意锁定顺序是序列化的:语句逐个获取元数据锁,而不是同时获取,并在此过程中执行死锁检测。
通常在考虑队列时考虑先进先出。如果我发出以下三个语句(按此顺序),它们将按以下顺序完成:
1 INSERT INTO parent2 ALTER TABLE child3 INSERT INTO parent
但是当子 ALTER 语句请求对父进行读取锁定时,尽管排序,但两个插入将在 ALTER 之前完成。以下是可以演示此示例的示例场景:
数据初始化:
CREATE TABLE `parent` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`val` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `child` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`val` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_parent` (`parent_id`),
CONSTRAINT `fk_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB;
INSERT INTO `parent` VALUES (1, "one"), (2, "two"), (3, "three"), (4, "four");
Session 1:
start transaction;update parent set val = "four-new" where id = 4;Session 2:
alter table child add index `idx_new` (val);Session 3:
start transaction;update parent set val = "three-new" where id = 3;此时,会话 1 具有打开的事务,并且处于休眠状态,并在父级上授予写入元数据锁定。 会话 2 具有在子级上授予的可升级(写入)锁定,并且正在等待父级的读取锁定。最后会话 3 具有针对父级的授权写入锁定:
mysql> select from performance_schemametadata_locks;+-------------+-------------+-------------------+---------------+-------------+| OBJECT_TYPE | OBJECT_NAME | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS |+-------------+-------------+-------------------+---------------+-------------+| TABLE | child | SHARED_UPGRADABLE | TRANSACTION | GRANTED | <- ALTER (S2)| TABLE | parent | SHARED_WRITE | TRANSACTION | GRANTED | <- UPDATE (S1)| TABLE | parent | SHARED_WRITE | TRANSACTION | GRANTED | <- UPDATE (S3)| TABLE | parent | SHARED_READ_ONLY | STATEMENT | PENDING | <- ALTER (S2)+-------------+-------------+-------------------+---------------+-------------+请注意,具有挂起锁定状态的唯一会话是会话 2(ALTER)。会话 1 和会话 3 (分别在 ALTER 之前和之后发布)都被授予了写锁。排序失败的地方是在会话 1 上发生提交的时候。在考虑有序队列时,人们会期望会话 2 获得锁定,事情就会继续进行。但是,由于元数据锁定系统的优先级性质,会话 3 具有锁定,会话 2 仍然等待。
如果另一个写入会话进入并启动新事务并获取针对父表的写锁定,则即使会话 3 完成,ALTER 仍将被阻止。
只要我保持一个对父表打开元数据锁定的活动事务,子表上的 ALTER 将永远不会完成。更糟糕的是,由于子表上的写锁定成功(但是完整语句正在等待获取父读锁定),所以针对子表的所有传入读取请求都将被阻止!
另外,请考虑一下您通常如何对无法完成的语句进行故障排除。您查看已经打开较长时间的事务(在进程列表和 InnoDB 状态中)。但由于阻塞线程现在比 ALTER 线程更年轻,因此您将看到的最旧的事务/线程是 ALTER 。
这正是这种情况下发生的情况。在准备发布时,我们的客户端正在运行 ALTER 语句并结合负载测试(一种非常好的做法!)以确保顺利发布。问题是负载测试保持对父表打开一个活动的写事务。这并不是说它只是一直在写,而是有多个线程,一个总是活跃的。 这阻止了 ALTER 完成并阻止对相对静态的子表的随后的读请求。
幸运的是,这个问题有一个解决方案(除了从设计模式中驱逐外键)。变量 max_write_lock_count[2] 可用于允许在写入锁定之后在读取锁定之前授予读取锁定连续写锁。默认情况下,此变量设置为 18446744073709551615,如果你对该表发出 10,000 次写入/秒,那么你的读将被锁定 5800 万年……
如何利用MySQL数据库锁定和解锁数据库表,这里有详细教程:>
MySQL死锁问题的相关知识是本文我们主要要介绍的内容,接下来我们就来一一介绍这部分内容,希望能够对您有所帮助。
1、MySQL常用存储引擎的锁机制
MyISAM和MEMORY采用表级锁(table-level locking)
BDB采用页面锁(page-level locking)或表级锁,默认为页面锁
InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁
2、各种锁特点
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
3、各种锁的适用场景
表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用
行级锁则更适合于有大量按索引条件并发更新数据,同时又有并发查询的应用,如一些在线事务处理系统
4、死锁
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
表级锁不会产生死锁。所以解决死锁主要还是针对于最常用的InnoDB。
5、死锁举例分析
在MySQL中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句 *** 作了主键索引,MySQL就会锁定这条主键索引;如果一条语句 *** 作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。
在UPDATE、DELETE *** 作时,MySQL不仅锁定WHERE条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的next-key locking。
例如,一个表db。tab_test,结构如下:
id:主键;
state:状态;
time:时间;
索引:idx_1(state,time)
出现死锁日志如下:
(1) TRANSACTION:
TRANSACTION 0 677833455, ACTIVE 0 sec, process no 11393, OSthread id 278546 starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 320
MySQL thread id 83, query id 162348740 dcnet03 dcnet Searching rows for update
update tab_test set state=1064,time=now() where state=1061 and time < date_sub(now(), INTERVAL 30 minute) (任务1的sql语句)
(1) WAITING FOR THIS LOCK TO BE GRANTED: (任务1等待的索引记录)
RECORD LOCKS space id 0 page no 849384 n bits 208 index `PRIMARY` of table `db/tab_test` trx id 0 677833455 _mode X locks rec but not gap waiting
Record lock, heap no 92 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
0: len 8; hex 800000000097629c; asc b ;; 1: len 6; hex 00002866eaee; asc (f ;; 2: len 7; hex 00000d40040110; asc @ ;; 3: len 8; hex 80000000000050b2; asc P ;; 4: len 8; hex 800000000000502a; asc P;; 5: len 8; hex 8000000000005426; asc T&;; 6: len 8; hex 800012412c66d29c; asc A,f ;; 7: len 23; hex 75706c6f6164666972652e636f6d2f6 8616e642e706870; asc xxxcom/;; 8: len 8; hex 800000000000042b; asc +;; 9: len 4; hex 474bfa2b; asc GK +;; 10: len 8; hex 8000000000004e24; asc N$;;
(2) TRANSACTION:
TRANSACTION 0 677833454, ACTIVE 0 sec, process no 11397, OS thread id 344086 updating or deleting, thread declared inside InnoDB 499
mysql tables in use 1, locked 1
3 lock struct(s), heap size 320, undo log entries 1
MySQL thread id 84, query id 162348739 dcnet03 dcnet Updating update tab_test set state=1067,time=now () where id in (9921180) (任务2的sql语句)
(2) HOLDS THE LOCK(S): (任务2已获得的锁)
RECORD LOCKS space id 0 page no 849384 n bits 208 index `PRIMARY` of table `db/tab_test` trx id 0 677833454 lock_mode X locks rec but not gap
Record lock, heap no 92 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
0: len 8; hex 800000000097629c; asc b ;; 1: len 6; hex 00002866eaee; asc (f ;; 2: len 7; hex 00000d40040110; asc @ ;; 3: len 8; hex 80000000000050b2; asc P ;; 4: len 8; hex 800000000000502a; asc P;; 5: len 8; hex 8000000000005426; asc T&;; 6: len 8; hex 800012412c66d29c; asc A,f ;; 7: len 23; hex 75706c6f6164666972652e636f6d2f6 8616e642e706870; asc uploadfirecom/handphp;; 8: len 8; hex 800000000000042b; asc +;; 9: len 4; hex 474bfa2b; asc GK +;; 10: len 8; hex 8000000000004e24; asc N$;;
(2) WAITING FOR THIS LOCK TO BE GRANTED: (任务2等待的锁)
RECORD LOCKS space id 0 page no 843102 n bits 600 index `idx_1` of table `db/tab_test` trx id 0 677833454 lock_mode X locks rec but not gap waiting
Record lock, heap no 395 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 8; hex 8000000000000425; asc %;; 1: len 8; hex 800012412c66d29c; asc A,f ;; 2: len 8; hex 800000000097629c; asc b ;;
WE ROLL BACK TRANSACTION (1)
(回滚了任务1,以解除死锁)
原因分析:
当“update tab_test set state=1064,time=now() where state=1061 and time < date_sub(now(), INTERVAL 30 minute)”执行时,MySQL会使用idx_1索引,因此首先锁定相关的索引记录,因为idx_1是非主键索引,为执行该语句,MySQL还会锁定主键索引。
假设“update tab_test set state=1067,time=now () where id in (9921180)”几乎同时执行时,本语句首先锁定主键索引,由于需要更新state的值,所以还需要锁定idx_1的某些索引记录。
这样第一条语句锁定了idx_1的记录,等待主键索引,而第二条语句则锁定了主键索引记录,而等待idx_1的记录,这样死锁就产生了。
6、解决办法
拆分第一条sql,先查出符合条件的主键值,再按照主键更新记录:
select id from tab_test where state=1061 and time < date_sub(now(), INTERVAL 30 minute);
update tab_test state=1064,time=now() where id in();
MySQL8x 中新增了一个轻量级的备份锁,它允许在 online 备份的时候进行 DML *** 作,同时可防止快照不一致。这个锁禁止的 *** 作很少,它禁止的 *** 作包括:
文件的创建、删除、改名账户的管理REPAIR TABLETRUNCATE TABLEOPTIMIZE TABLE备份锁由 lock instance for backup 和 unlock instance 语法组成。使用这些语句需要 BACKUP_ADMIN 权限。当前运行的所有事务
select from information_schemainnodb_trx
当前出现的锁
select from information_schemainnodb_locks
锁等待的对应关系
select from information_schemainnodb_lock_waits
通过找到线程id号,进行kill
通过 select from information_schemainnodb_trx 查询 trx_mysql_thread_id然后执行 kill 线程ID
以上就是关于如何查看MySQL数据库的死锁信息全部的内容,包括:如何查看MySQL数据库的死锁信息、mysql锁定了数据库表只能写,为什么还可以读、如何利用MySQL数据库锁定和解锁数据库表等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)