zoukankan      html  css  js  c++  java
  • Mysql死锁问题解决方式 & 聚簇索引、隔离级别等知识

    参考了这篇文章:http://www.cnblogs.com/LBSer/p/5183300.html  《mysql死锁问题分析

    写的不错。

    如果Mysql死锁,会报出:

    1.1 死锁成因&&检测方法

    我们mysql用的存储引擎是innodb,从日志来看,innodb主动探知到死锁,并回滚了某一苦苦等待的事务。问题来了,innodb是怎么探知死锁的?

         直观方法是在两个事务相互等待时,当一个等待时间超过设置的某一阀值时,对其中一个事务进行回滚,另一个事务就能继续执行。这种方法简单有效,在innodb中,参数innodb_lock_wait_timeout用来设置超时时间。

         仅用上述方法来检测死锁太过被动,innodb还提供了wait-for graph算法来主动进行死锁检测,每当加锁请求无法立即满足需要并进入等待时,wait-for graph算法都会被触发。

    1.2 innodb隔离级别、索引与锁 

    1.2.1 锁与索引的关系

           假设我们有一张消息表(msg),里面有3个字段。假设id是主键,token是非唯一索引,message没有索引。

    id: bigint

    token: varchar(30)

    message: varchar(4096)

         innodb对于主键使用了聚簇索引,这是一种数据存储方式,表数据是和主键一起存储,主键索引的叶结点存储行数据。对于普通索引,其叶子节点存储的是主键值

    下面介绍了几类加锁的方式。

    关于主键的锁,只锁主键;

    非主键的索引键,锁相关的键;

    非索引,走的是全表扫描过滤。这时表上的各个记录都将添加上X锁。

     

    1.2.2 锁与隔离级别的关系

    为了保证并发操作数据的正确性,数据库都会有事务隔离级别的概念:

    1)未提交读(Read uncommitted);2)已提交读(Read committed(RC));3)可重复读(Repeatable read(RR));4)可串行化(Serializable)。

    我们较常使用的是RC和RR。(值得一提的是:大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。MySQL(其实是InnoDB,因为MyISAM不支持事务)的默认隔离级别是Repeatable read。)

    关于Mysql的隔离级别,可以参考我的这篇文章:http://www.cnblogs.com/charlesblc/p/5913914.html

    另外,补充上这篇文章写的比较清楚:http://blog.csdn.net/qq_33290787/article/details/51924963 

    Read uncommitted

    读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。(脏读)

    Read committed

    读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。

    分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读

    Repeatable read

    重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。

    分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作

    Serializable 序列化

    Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

    另外,注意,Mysql虽然默认级别是RR,但是通过并发控制(Gap锁)有效地解决了幻读的问题。(Gap锁的内容,下面介绍)

    为了解决幻读问题,innodb引入了gap锁。

          在事务A执行:update msg set message=‘订单’ where token=‘asd’;

          innodb首先会和RC级别一样,给索引上的记录添加上X锁,此外,还在非唯一索引’asd’与相邻两个索引的区间加上锁

           这样,当事务B在执行insert into msg values (null,‘asd',’hello’); commit;时,会首先检查这个区间是否被锁上,如果被锁上,则不能立即执行,需要等待该gap锁被释放。这样就能避免幻读问题。

    3 死锁成因

         了解了innodb锁的基本原理后,下面分析下死锁的成因。如前面所说,死锁一般是事务相互等待对方资源,最后形成环路造成的。下面简单讲下造成相互等待最后形成环路的例子。

    3.3不同索引锁冲突

         这种情况比较隐晦,事务A在执行时,除了在二级索引加锁外,还会在聚簇索引上加锁

    3.4 gap锁冲突

         innodb在RR级别下,如下的情况也会产生死锁,比较隐晦。不清楚的同学可以自行根据上节的gap锁原理分析下。

     就是各种环路。注意最后,还有Gap锁造成的环路。

    (具体内容看原文)

    4 如何尽可能避免死锁

    1)以固定的顺序访问表和行。比如对第2节两个job批量更新的情形,简单方法是对id列表先排序,后执行,这样就避免了交叉等待锁的情形;又比如对于3.1节的情形,将两个事务的sql顺序调整为一致,也能避免死锁。

    2)大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。

    3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。

    4)降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。

    5)为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。

    5 如何定位死锁成因

     1)通过应用业务日志定位到问题代码,找到相应的事务对应的sql;

     2)确定数据库隔离级别。

    select @@global.tx_isolation

    select @@tx_isolation

    3)找DBA执行下show InnoDB STATUS看看最近死锁的日志。

     (具体内容看原文)

    (完)

  • 相关阅读:
    在operator =中要处理“自我赋值”
    delete指针以后应赋值为NULL
    【转】C++对成员访问运算符->的重载
    【转】浅析SkipList跳跃表原理及代码实现
    【KakaJSON手册】05_JSON转Model_05_动态模型
    【KakaJSON手册】04_JSON转Model_04_值过滤
    【KakaJSON手册】03_JSON转Model_03_key处理
    【KakaJSON手册】02_JSON转Model_02_数据类型
    【KakaJSON手册】01_JSON转Model_01_基本用法
    利用eclipse调试JDK源码
  • 原文地址:https://www.cnblogs.com/charlesblc/p/6288254.html
Copyright © 2011-2022 走看看