设为首页 加入收藏

TOP

MySQL 死锁与日志二三事(二)
2017-11-24 10:48:58 】 浏览:827
Tags:MySQL 日志 二三
不然的话,可能导致大量的锁冲突,从而影响并发性能。由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;另外间隙锁也会锁多行,InnoDB除了通过范围条件加锁时使用间隙锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁!

话都说到这了,那就看下咱们业务表的索引情况:

show INDEX from `db_zz_flow`.`t_channel_final_datas`;
+-----------------------+--------------+-------------------------------+----------------+-------------+----------+--------+--------------+-----------+-----------------+
| Table                 |   Non_unique | Key_name                      |   Seq_in_index | Column_namt |   Packed | Null   | Index_type   | Comment   | Index_comment   |
|-----------------------+--------------+-------------------------------+----------------+-------------+----------+--------+--------------+-----------+-----------------|
| t_channel_final_datas |            0 | PRIMARY                       |              1 | id        > |   <null> |        | BTREE        |           |                 |
| t_channel_final_datas |            1 | index_countdate_type_terminal |              1 | count_date> |   <null> | YES    | BTREE        |           |                 |
| t_channel_final_datas |            1 | index_countdate_type_terminal |              2 | channel_ty> |   <null> | YES    | BTREE        |           |                 |
| t_channel_final_datas |            1 | index_countdate_type_terminal |              3 | terminal  > |   <null> | YES    | BTREE        |           |                 |
| t_channel_final_datas |            1 | index_countdate_channelid     |              1 | count_date> |   <null> | YES    | BTREE        |           |                 |
| t_channel_final_datas |            1 | index_countdate_channelid     |              2 | channel_id> |   <null> | YES    | BTREE        |           |                 |
+-----------------------+--------------+-------------------------------+----------------+-------------+----------+--------+--------------+-----------+-----------------+

可以看到这张表的索引极不合理:有3个索引,但是 update 却没有完全的用上索引,导致 update 没有精确的用上索引,需要锁定多行范围数据,从而引发死锁。

知道原理后,咱们再精心构建一个四字段的组合索引即可让 update 精准的走 innodb 索引,实际上,我们更新索引后,这个死锁问题即得到了解决。

注:innodb不仅会打印出事务和事务持有和等待的锁,而且还有记录本身,不幸的是,它可能超过innodb为输出结果预留的长度(只能打印1M的内容且只能保留最近一次的死锁信息),如果你无法看到完整的输出,此时可以在任意库下创建innodb_monitor或innodb_lock_monitor表,这样innodb status信息会完整且每15s一次被记录到错误日志中。如:create table innodb_monitor(a int)engine=innodb;,不需要记录到错误日志中时就删掉这个表即可。

(2)回滚的话,为什么只有部分 update 语句失败,而不是整个事务里的所有 update 都失败?

这是因为咱们的 innodb 默认是自动提交的:

show variables like 'autocommit';
+-----------------+---------+
| Variable_name   | Value   |
|-----------------+---------|
| autocommit      | ON      |
+-----------------+---------+

在多个 update 或 insert 语句情况下,每执行完一条 SQL,innodb 就立即 commit 一次以持久化变更,同时释放锁,这也正是本例中死锁回滚事务后只有极个别语句失败的原因。

需要注意的是,通常还有另外一种情况也可能导致部分语句回滚,需要格外留意。在 innodb 里有个参数叫:innodb_rollback_on_timeout

show VARIABLES LIKE 'innodb_rollback_on_timeout'
+----------------------------+---------+
| Variable_name              | Value   |
|----------------------------+---------|
| innodb_rollback_on_timeout | OFF     |
+----------------------------+---------+

官方手册里这样描述:

In MySQL 5.1, InnoDB rolls back only the last statement on a transaction timeout by default. If –innodb_rollback_on_timeout is specified, a transaction timeout causes InnoDB to abort and roll back the entire transaction (the same behavior as in MySQL 4.1). This variable was added in MySQL 5.1.15.

解释:这个参数关闭或不存在的话遇到超时只回滚事务最后一个Query,打开的话事务遇到超时就回滚整个事务。

(3)怎样降低 innodb 死锁几率?

死锁在行锁及事务场景下很难完全消除,但可以通过表设计和SQL调整等措施减少锁冲突和死锁,包括:

  • 尽量使用较低的隔离级别,比如如果发生了间隙锁,你可以把会话或者事务的事务隔离级别更改为 RC(read committed)级别来避免,但此时需要把 binlog_format 设置成 row 或者 mixed 格式
  • 精心设计索引,并尽量使用索引访问数据,使加锁更精确,
首页 上一页 1 2 3 4 5 6 7 下一页 尾页 2/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇记一次诡异的 ssh 互信免密码登录.. 下一篇Java Fork/Join 框架

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目