欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

数据库锁机制

程序员文章站 2022-04-13 15:55:32
并发控制 数据库管理系统中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。 *,时间戳,乐观并发控制和悲观并发控制是并发控制主要采用的技术手段 锁 当并发事务同时访问一个资源时,有可能导致数据不一致,因此需要一种机制来将数据访问顺序化,以保证 ......

并发控制

  • 数据库管理系统中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。
  • *,时间戳,乐观并发控制和悲观并发控制是并发控制主要采用的技术手段

当并发事务同时访问一个资源时,有可能导致数据不一致,因此需要一种机制来将数据访问顺序化,以保证数据库数据的一致性。锁就是其中的一种机制

锁的分类

  • 按照操作划分,可分为dml锁,ddl锁
  • 按锁粒度划分,可分为表级锁,行级锁,页级锁(mysql)
  • 按锁级别划分,可分为共享锁,排他锁
  • 按加锁方式划分,可分为自动锁,显示锁
  • 按使用方式划分,可分为乐观锁,悲观锁

dml锁用于保护数据的完整性,其中包括行级锁(tx锁),表级锁(tm锁)。ddl锁用于保护数据库对象的结构,如表,索引等的结构定义,其中包括排他ddl锁,共享ddl锁,可中断解析锁

行级锁

  • 行级锁是mysql中锁定力度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但是加锁的开销也最大。行级锁分为共享锁排他锁
  • 特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高

表级锁

  • 表级锁是mysql中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分mysql引擎支持。最常见使用的myisam与innodb都支持表级锁定。表级锁定分为表分享读锁(共享锁)表独占写锁(排他锁)
  • 特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发性最低

页级锁

  • 页级锁是mysql中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但是冲突多,行级冲突少,但是速度慢。页级锁折中,以此锁定相邻的一组纪录。bdb支持页级锁

mysql常用存储引擎的锁机制

  • myisam和memory采用表级锁
  • bdb采用页级锁或表级锁,默认是页级锁
  • innodb支持行级锁和表级锁,默认为行级锁

innodb的行锁和表锁

innodb引擎中既支持行锁也支持表锁,那么什么时候会锁住整张表,什么时候锁住一行??

  • innodb行锁是通过给索引上的索引项加锁实现的,这点mysql跟oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。innodb这种行锁实现特点意味着:只有通过索引条件检索数据,innodb才使用行级锁,否则innodb将使用表锁
  • 实际应用中,要注意innodb行锁的这一特性,否则容易导致大量的锁冲突,从而影响并发性能
    • 在不通过索引条件查询的时候,innodb使用的是表锁,而不是行锁
    • 由于mysql的行锁是针对索引加的锁,不是针对纪录加的锁,所以虽然访问不同行的纪录,但是如果使用相同索引的键,是会出现锁冲突的。
    • 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外不论是使用主键索引,唯一索引还是普通索引,innodb都会使用行锁来对数据加锁
    • 即便在条件中使用了索引字段,但是否使用索引来检索数据是由mysql通过判断不同之行计划的代价来决定的,如果mysql认为全表扫效率更高,比如对一些很小的表,它就不会使用索引,这种情况innodb将使用表锁而不是行锁。因此分析锁冲突的时候,别忘记检查sql的执行计划

行级锁和死锁

  • myisam是不会产生死锁的,因为myisam总是一次性获得所需的全部锁,要么全部满足,要么全部等待。而在innodb中,锁是逐步获得的,就造成死锁的可能
  • 在mysql中,行级锁并不是直接锁纪录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,mysql就会锁住这条主键索引;如果一条语句操作了非主键索引,mysql就会先锁定该非主键索引,再锁定相关的主键索引。在update,delete操作时,mysql不仅锁定where条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的next-key locking
  • 死锁:当两个事务同时执行,一个锁住了主键索引,在等待其他相关索引。另一个锁定了非主键索引,在等待主键索引。这样就发生死锁。
  • 发生死锁后,innodb一般都可以检测到,并使一个事务释放锁回退,另一个获取锁完成事务

避免死锁

  • 如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会
  • 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率
  • 对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁

共享锁和排他锁

  • 行级锁是mysql中锁定粒度最细的一种锁,行级锁能够大大减少数据库操作的冲突。行级锁分为共享锁和排他锁
1. 共享锁
  • 共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改,直到已释放所有共享锁。
  • 如果事务t对数据a加上共享锁后,那么其他事务只能对a再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据
  • 如果事务t对数据a加上共享锁后,又对数据进行修改,那么其他事务将不能获取共享锁;同样的,若多个事务对同一数据获取共享锁,则任何事务不能对该数据进行修改
  • 用法: sselect ... lock in share mode

    在查询语句后面增加lock in share mode,mysql会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取到的是同一个版本的数据

2. 排他锁
  • 排他锁又称写锁,如果事务t对数据a加上排他锁后,则其他事务不能再对a加任何类型的*。获取排他锁的事务既能读数据,又能修改数据
  • 用法:select ... for update 。在查询语句后面增加for update,mysql会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞
3. 意向锁
  • 意向锁是表级锁,其设计目的是为了在一个事务中揭示下一行将要被请求锁的类型。innodb中的两个表锁:
    • 意向共享锁(is):表示事务准备给数据行加入共享锁,也即是说一个数据行加共享锁前必须先取得该表的is锁
    • 意向排他锁(ix):表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先去的该表的ix锁
  • 意向锁是innodb自动加的,不需要用户干预

总结

对于insert,update,delete,innodb会自动给涉及到的数据加排他锁;对于一般的select语句,innodb不会加任何锁,事务可以通过以下语句给显式加上共享锁或排他锁

  • 共享锁:select ... lock in share mode
  • 排他锁:select ... for update