请原谅一些新手问题 - 我是一名试图担任DBA角色的开发人员,此刻还有点夸张。
我目前正在尝试解决的主要问题是MySQL向我展示了很多死锁错误。我们已经运行了几个月没有任何东西,在添加了我们认为相当无害的变化之后,我们突然间会看到一个小时十几个小时。
我们看到的具体错误(表/列名称被擦除,我们正在使用您在那里看到的一些JDBI):
------------------------
LATEST DETECTED DEADLOCK
------------------------
2014-05-21 19:11:13 2b4c3602a700
*** (1) TRANSACTION:
TRANSACTION 1327203423, ACTIVE 0 sec starting index read
mysql tables in use 3, locked 3
LOCK WAIT 4 lock struct(s), heap size 1248, 3 row lock(s)
MySQL thread id 6182129, OS thread handle 0x2b4c36683700, query id 2061701269 [host] [ip] [user] updating
/* WriteDAO.update */ UPDATE A SET t = 0, createdDate = '2014-05-21 19:11:13' WHERE uid = 1 AND tid = 100
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 109 page no 84 n bits 584 index `idx_A_tid` of table `core`.`A` trx id 1327203423 lock_mode X waiting
Record lock, heap no 167 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 8; hex 8000000000002740; asc '@;;
1: len 8; hex 8000000000002747; asc 'G;;
*** (2) TRANSACTION:
TRANSACTION 1327203438, ACTIVE 0 sec fetching rows
mysql tables in use 3, locked 3
6 lock struct(s), heap size 1248, 6 row lock(s)
MySQL thread id 6182028, OS thread handle 0x2b4c3602a700, query id 2061701279 [host] [ip] [user] updating
/* WriteDAO.update */ UPDATE A SET t = 0, createdDate = '2014-05-21 19:11:13' WHERE uid = 2 AND tid = 100
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 109 page no 84 n bits 584 index `idx_A_tid` of table `core`.`A` trx id 1327203438 lock_mode X
Record lock, heap no 167 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 8; hex 8000000000002740; asc '@;;
1: len 8; hex 8000000000002747; asc 'G;;
Record lock, heap no 168 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 8; hex 8000000000002740; asc '@;;
1: len 8; hex 800000000000274e; asc 'N;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 109 page no 6 n bits 344 index `PRIMARY` of table `core`.`A` trx id 1327203438 lock_mode X locks rec but not gap waiting
Record lock, heap no 63 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
0: len 8; hex 800000000000274e; asc 'N;;
1: len 6; hex 00004f13d872; asc O r;;
2: len 7; hex 42000001e02f43; asc B /C;;
3: len 8; hex 8000000000002771; asc 'q;;
4: len 8; hex 8000000000002740; asc '@;;
5: len 8; hex 8000000000000000; asc ;;
6: len 5; hex 9992eb085c; asc \;;
*** WE ROLL BACK TRANSACTION (1)
表A非常简单,最后有两个索引:
CREATE TABLE A (
id BIGINT NOT NULL AUTO_INCREMENT,
uid BIGINT NOT NULL,
tid BIGINT NOT NULL,
t BIGINT NOT NULL,
createdDate DATETIME NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARACTER SET=utf8 COLLATE=utf8_unicode_ci;
CREATE INDEX idx_A_uid ON A (uid);
CREATE INDEX idx_A_tid ON A (tid);
我们没有在事务中针对这些表运行任何东西,除了JDBI事务通过@SqlUpdate / @SqlQuery / etc注释自动执行的操作,例如:
@SqlUpdate("UPDATE A "+
" SET t = :t, createdDate = :createdDate "+
" WHERE uid = :uid AND tid = :tid")
public int update (@BindBean T t);
所有这些查询都运行得非常快(慢查询日志不会显示任何超过一秒的内容,而且几乎所有查询都比这少得多)。我们还没有那么重要的负载。
第一个问题:
mysql tables in use 3, locked 3
为什么MySQL锁定3个表来对其进行更新?是否在此输出中将索引视为表?
第二个问题:
RECORD LOCKS space id 109 page no 6 n bits 344 index `PRIMARY` of table `core`.`A` trx id 1327203438 lock_mode X locks rec but not gap waiting
为什么A的主键被锁定?除了初始行创建之外,我们不会触及该列> at all<请注意,update语句都没有触及该列,它们甚至应该针对不同的行进行操作。我们还使用分布式hazelcast来获取分布式锁,因此一次只有一个线程可以尝试访问给定行以便在该表中写入。一行读取锁定会导致写入死锁吗?即使它可以,行级锁定不应该解决这个问题吗?
第三个问题:如果MySQL需要更新索引中的一行,它是否像表锁一样锁定整个索引?或者它是否像表索引那样进行行级锁定?
有关调试的建议吗?
由于
答案 0 :(得分:1)
我总是建议您不要在where子句中使用UPDATE
运行PRIMARY KEY
语句。
您有2笔交易: -
TX1:
UPDATE A
SET t = 0, createdDate = '2014-05-21 19:11:13'
WHERE uid = 1 AND tid = 100
TX2:
UPDATE A
SET t = 0, createdDate = '2014-05-21 19:11:13'
WHERE uid = 2 AND tid = 100
两个TX都在不使用Primary Key
进行更新。
所以让我们说例如TX2中的查询必须更新20行。所以它的作用是在整个A表上放置一个S锁(因为它不知道id' s)然后它会一直一个地在20行中放置一个X锁,因为它会不断更新它们。
说TX2正在更新第13行,此时TX1会尝试更新它,因此需要进行X锁定。所以它放入了对X锁的请求并进入队列。 MySQL中的队列是FIFO(先进先出)。因此,在TX1发出X锁定请求后,TX2完成更新第13行,现在请求X锁定以更新第14行,但它无法做到这一点,因为TX2无法获得其等待的X锁定TX1获得X锁定。并且TX1无法获得其X锁定,直到TX2释放其S锁定。 这就是你遇到僵局的原因。 我希望所有这些都是有道理的......我强烈建议你阅读这篇文章 - http://dev.mysql.com/doc/refman/5.0/en/innodb-lock-modes.html
需要注意的重点是: -
共享(S)锁允许事务读取行。
独占(X)锁允许事务更新或删除行。
如果事务T1在行r上持有共享(S)锁,则来自某个不同事务T2的对行r上的锁的请求按如下方式处理:
可以立即授予T2对S锁的请求。结果,T1和T2都在r上保持S锁定。
不能立即授予T2关于X锁的请求。
解决方案:
因此,解决方案是使用主键进行更新,以便将锁定放在您想要更新的行而不是表格上。
TX2应该是(在PHP中) - 您可以将逻辑转换为您想要的任何语言:
$db = Zend_Db_Table::getDefaultAdapter();
$select = "SELECT id From A where uid = 2 AND tid = 100";
$rows = $db->fetchAll($select);
foreach ($rows as $row) {
$id = $row['id'];
$update = "UPDATE A SET t=0,createdDate = '2014-05-21 19:11:13' Where id = ".$id;
$db->query($update);
}
因此,您应该选择ID并使用这些ID进行更新,因此只会锁定行而不是整个表。 你应该对TX1做同样的事情。对于流量较高的表格,这种方法很重要。