mysql 的锁

http://www.javashuo.com/article/p-msbhlrah-hv.htmlhtml

  • 好好看看

MySQL大体可概括为如下3种锁:数据库

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的几率最高,并发度最低。
  • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的几率最低,并发度也最高。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度通常

MySQL表级锁的锁模式(MyISAM)

  • MySQL表级锁有两种模式:表共享锁(Table Read Lock)和表独占写锁(Table Write Lock)。
  • LOCK tables orders read local,order_detail read local;并发

    SELECT SUM(total) FROM orders;spa

    SELECT SUM(subtotal) FROM order_detail;线程

    Unlock tables;code

 MyISAM存储引擎有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别能够为0、1或2。htm

  • 当concurrent_insert设置为0时,不容许并发插入。
  • 当concurrent_insert设置为1时,若是MyISAM容许在一个读表的同时,另外一个进程从表尾插入记录。这也是MySQL的默认设置。
  • 当concurrent_insert设置为2时,不管MyISAM表中有没有空洞,都容许在表尾插入记录,都容许在表尾并发插入记录。

MyISAM的锁调度blog

  • 写请求后到,写锁也会插到读请求以前!
    • 这是由于MySQL认为写请求通常比读请求重要。

InnoDB锁问题

  •  InnoDB与MyISAM的最大不一样有两点:
    • 一是支持事务(TRANSACTION);
    • 二是采用了行级锁。

事务(Transaction)及其ACID属性

并发事务带来的问题排序

  • 更新丢失(Lost Update):
  • 脏读(Dirty Reads):
  • 不可重复读(Non-Repeatable Reads):
    • 一个事务在读取某些数据已经发生了改变、或某些记录已经被删除了!这种现象叫作“不可重复读”。
  • 幻读(Phantom Reads):
    • 一个事务按相同的查询条件从新读取之前检索过的数据,
    • 却发现其余事务插入了知足其查询条件的新数据,这种现象就称为“幻读”。

数据库实现事务隔离的方式,基本能够分为如下两种。索引

  • 一种是在读取数据前,对其加锁,阻止其余事务对数据进行修改。

  • 另外一种是不用加任何锁,

    • 经过必定机制生成一个数据请求时间点的一致性数据快照(Snapshot),

    • 并用这个快照来提供必定级别(语句级或事务级)的一致性读取。

隔离级别/读数据一致性及容许的并发反作用 读数据一致性 脏读 不可重复读 幻读

未提交读(Read uncommitted)

最低级别,只能保证不读取物理上损坏的数据
已提交度(Read committed) 语句级
可重复读(Repeatable read) 事务级
可序列化(Serializable) 最高级别,事务级

InnoDB实现了如下两种类型的行锁。

  • 共享锁(s):容许一个事务去读一行,
    • 阻止其余事务得到相同数据集的排他锁。
  • 排他锁(X):容许获取排他锁的事务更新数据,
    • 阻止其余事务取得相同的数据集共享读锁和排他写锁。
  • 另外,为了容许行锁和表锁共存,实现多粒度锁机制,

    • InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。

      •  

        意向共享锁(IS):事务打算给数据行共享锁,

        • 事务在给一个数据行加共享锁前必须先取得该表的IS锁。

      • 意向排他锁(IX):事务打算给数据行加排他锁,

        • 事务在给一个数据行加排他锁前必须先取得该表的IX锁。

InnoDB行锁实现方式

  •  InnoDB行锁是经过索引上的索引项来实现的
  • 这一点MySQL与Oracle不一样,
    • 后者是经过在数据中对相应数据行加锁来实现的。
  • InnoDB这种行锁实现特色意味者:
    • 只有经过索引条件检索数据,InnoDB才会使用行级锁
    • 不然,InnoDB将使用表锁!

间隙锁(Next-Key锁)

  • 当咱们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,
    • InnoDB会给符合条件的已有数据的索引项加锁;
  • 对于键值在条件范围内但并不存在的记录,叫作“间隙(GAP)”,
    • InnoDB也会对这个“间隙”加锁,这种锁机制不是所谓的间隙锁(Next-Key锁)。

何时使用表锁

 

  •  对于InnoDB表,在绝大部分状况下都应该使用行级锁,
    • 由于事务和行锁每每是咱们之因此选择InnoDB表的理由。
    • 但在个另特殊事务中,也能够考虑使用表级锁。
      • 第一种状况是:事务须要更新大部分或所有数据,表又比较大,
        • 若是使用默认的行锁,不只这个事务执行效率低,
        • 并且可能形成其余事务长时间锁等待和锁冲突,
        • 这种状况下能够考虑使用表锁来提升该事务的执行速度。
      • 第二种状况是:事务涉及多个表,比较复杂,极可能引发死锁,形成大量事务回滚。
        • 这种状况也能够考虑一次性锁定事务涉及的表,
        • 从而避免死锁、减小数据库因事务回滚带来的开销。
  • 固然,应用中这两种事务不能太多,不然,就应该考虑使用MyISAM表。
  • 在InnoDB下 ,使用表锁要注意如下两点。
    • (1)使用LOCK TALBES虽然能够给InnoDB加表级锁,
      • 但必须说明的是,表锁不是由InnoDB存储引擎层管理的,
      • 而是由其上一层MySQL Server负责的
      • 仅当autocommit=0、innodb_table_lock=1(默认设置)时,
        • InnoDB层才能知道MySQL加的表锁,MySQL Server才能感知InnoDB加的行锁,
        • 这种状况下,InnoDB才能自动识别涉及表级锁的死锁;
        • 不然,InnoDB将没法自动检测并处理这种死锁。
    •  (2)在用LOCAK TABLES对InnoDB锁时要注意,要将AUTOCOMMIT设为0,不然MySQL不会给表加锁;
      • 事务结束前,不要用UNLOCAK TABLES释放表锁,由于UNLOCK TABLES会隐含地提交事务;
      • COMMIT或ROLLBACK产不能释放用LOCAK TABLES加的表级锁,必须用UNLOCK TABLES释放表锁。
        • SET AUTOCOMMIT=0;

          LOCAK TABLES t1 WRITE, t2 READ, ...;

          [do something with tables t1 and here];

          COMMIT;

          UNLOCK TABLES;

关于死锁

  • MyISAM表锁是deadlock free的
  • 可是在InnoDB中,除单个SQL组成的事务外,锁是逐步得到的,这就决定了InnoDB发生死锁是可能的。
    •  发生死锁后,InnoDB通常都能自动检测到,并使一个事务释放锁并退回,另外一个事务得到锁,继续完成事务。
    • 但在涉及外部锁,或涉及锁的状况下,InnoDB并不能彻底自动检测到死锁,
      • 这须要经过设置锁等待超时参数innodb_lock_wait_timeout来解决。

下面就经过实例来介绍几种死锁的经常使用方法。

  • (1)在应用中,若是不一样的程序会并发存取多个表,
    • 应尽可能约定以相同的顺序为访问表,这样能够大大下降产生死锁的机会。
  • (2)在程序以批量方式处理数据的时候,
    • 若是事先对数据排序,保证每一个线程按固定的顺序来处理记录,也能够大大下降死锁的可能。
  • (3)在事务中,若是要更新记录,应该直接申请足够级别的锁,即排他锁,
    • 而不该该先申请共享锁,更新时再申请排他锁,甚至死锁。
  •  (4)在REPEATEABLE-READ隔离级别下,
    • 若是两个线程同时对相同条件记录用SELECT...ROR UPDATE加排他锁,
      • 在没有符合该记录状况下,两个线程都会加锁成功。
    • 程序发现记录尚不存在,就试图插入一条新记录,若是两个线程都这么作,就会出现死锁。
    • 这种状况下,将隔离级别改为READ COMMITTED,就能够避免问题。
  • (5)当隔离级别为READ COMMITED时,
    • 若是两个线程都先执行SELECT...FOR UPDATE,判断是否存在符合条件的记录,若是没有,就插入记录。
    • 此时,只有一个线程能插入成功,另外一个线程会出现锁等待,
      • 当第1个线程提交后,第2个线程会因主键重出错,但虽然这个线程出错了,却会得到一个排他锁!
      • 这时若是有第3个线程又来申请排他锁,也会出现死锁。
      • 对于这种状况,能够直接作插入操做,而后再捕获主键重异常,或者在遇到主键重错误时,老是执行ROLLBACK释放得到的排他锁。
相关文章
相关标签/搜索