数据库-锁

MySQL中的锁机制

锁的分类
  1. 按锁的粒度划分:表级锁,行级锁,页级锁
  2. 按锁级别划分:共享锁,排它锁
  3. 按加锁方式划分:自动锁,显式锁
  4. 按操作划分:DML锁,DDL锁
  5. 按使用方式划分:乐观锁,悲观锁
MyISAM与InnoDB关于锁方面的区别
  1. MyISAM

MyISAM

  • 不支持事务
  • 默认用的是表级锁(跟索引无关),不支持行级锁
  • 不会出现死锁(MyISAM总是一次获得SQL语句所需要的全部锁。这也正是MyISAM表不会出现死锁(Deadlock Free)的原因)
  • 在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表;同时,如果加的是读锁,那么只能执行查询操作,而不能执行更新操作。
  • 不同的操作间是串行
  • 并发插入(可在上述链接中查找):允许一个进程读表的同时,另一个进程从表尾插入
  • 锁调度(可在上述链接中查找):MySQL认为写请求一般比读请求要重要(这也正是MyISAM表不太适合于有大量更新操作和查询操作应用的原因)

适用场景:

  • 频繁执行全表count语句(MyISAM用一个变量存储了数据行数)
  • 对数据进行增删改的频率不高(锁住整张表,冲突多),查询非常频繁
  • 没有事务

锁模式:

  • 表共享读锁(Table Read Lock)
  • 表独占写锁(Table Write Lock)

加锁方式:

  • 排它锁:select …for update
  • 共享锁:select … lock in share mode
  • update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型
  1. InnoDB

InnoDB

  • 支持事务
  • 默认用的是行级锁(InnoDB行锁是通过给索引上的索引项加锁来实现的,当不走索引时,采用的是表级锁,用到索引的时候使用行级锁),也支持表级锁
  • 会出现死锁

适用场景:

  • 数据增删改查都相当频繁
  • 可靠性要求比较高,要求支持事务

锁模式:

  • 共享锁(S):又称读锁
  • 排他锁(X):又称写锁,允许获取排他锁的事务更新数据,阻止其他事务取得相同的数据集共享读锁和排他写锁

为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁

  • 意向锁的意义
  • 意向共享锁(IS):事务打算给数据行共享锁,事务在给一个数据行加共享锁必须先取得该表的IS锁
  • 意向排他锁(IX):事务打算给数据行加排他锁,事务在给一个数据行加排他锁必须先取得该表的IX锁

加锁方式:

  • 共享锁(S):select … lock in share mode
  • 排他锁(X):select …for update
  • 意向锁是InnoDB自动加的,不需用户干预,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。
  • 对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁
数据库事务的四大特性

可在上述链接中查找:

  • 原子性,一致性,隔离性,持久性
事务隔离级别以及各级别下的并发访问问题
  1. 并发事务带来的问题
  • 更新丢失:mysql所有事务隔离级别在数据库层面上均可避免
  • 脏读(一个事务读到另一个事务未提交的内容,如果事务发生回滚,会产生错误):read-committed事务隔离级别以上可避免
  • 不可重复读(多次读取数据不一致,一个事务读取数据中,另一个事务对其读取数据发生修改并提交,导致多次读取不一致):repeatable-read(默认)事务隔离级别以上可避免(多次读取数据一直,但更新操作仍是建立在其他事务修改之后的结果上进行操作)
  • 幻读(增/删):serializable事务隔离级别可避免
  • 可重复读理论上不可避免幻读,mysql采用一些技巧避免了
  • 脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。
  • 数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上 “串行化”进行,这显然与“并发”是矛盾的。
  • select @@tx_isolation:查看事务的隔离级别
  • set session transaction isolation level …:设置隔离级别
InnoDB(密集索引)可重复读隔离级别下如何避免幻读
  1. 表象:快照读(非阻塞读)–伪MVCC(数据多版本并发控制 MultiVersion Concurrency Control)

虽然不会出现幻读,但其读到的仅仅是之前版本的快照,掩耳盗铃

  1. 内在:next-key锁(行锁+gap锁)

Gap锁(间隙锁)

  • 仅存在与 rr 级别以及之上

区间(前开后闭):(10,11,13,20)

  • (negative infinity, 10]
  • (10, 11]
  • (11, 13]
  • (13, 20]
  • (20, positive infinity)

对主键索引或者唯一索引会用Gap锁吗

  • 如果where条件全部命中,则不会用Gap锁,只会用记录所
  • 如果where条件部分命中或者全不命中,则会加Gap锁
    – 如果是部分命中的话,只会部分加锁
    – 对于非主键索引,会对主键一起添加Gap锁(InnoDB采用的是密集索引)

Gap锁会用在非唯一索引或者不走索引的当前读中

  • 非唯一索引
    – 只需在修改的周边上Gap锁,区间如上所述
    – 通过比较主键可以更精确范围
  • 不走索引
    – 所有Gap都上锁,类似于表锁
RC,RR级别下的InnoDB的非阻塞读(快照读)如何实现

快照读

  1. 当前读和快照读

数据库实现事务隔离的方式,基本可以分为以下两种:

  1. 当前读:读取的记录是最新版本,读取之后还需保证其他并发事务不能修改当前记录,对当前记录加锁
    – select…lock in share mode , select…for update
    – update , delete , insert

  2. 快照读:不加锁(在事务隔离不为serializable条件下才成立,在serializable下,由于是串行读,此时的快照读退化成当前读)的非阻塞读
    select(默认)

不同隔离级别下的情况:

  1. 在rc级别下,当前读和快照读读到的数据一致(均是修改后的)
  2. 在rr级别下,当前读是更新后的,快照读如果在其他事务修改之前读取该数据,即之后读到的也是之前的,反之也是更新后的

造成原因(快照生成时机的不同):

  • READ-COMMITTED:一个事务下,RC 每条 dml 语句(增删改查)读数据都会创建一个新的 read view (快照),其DB_TRX_ID 每次读都 +1
  • REPEATABLE-READ:一个事务下,RR 第一条 dml 会对已存在的 undo log 按read view的规则创建一个快照。同一个事务下连续的多个select,也是只能读到同一个快照,即第一个快照的内容。(快照的构建时机很重要,在其它事务修改前 / 后)
  1. 快照读

读写不冲突,读取数据不需要加锁

  1. InnoDB存储引擎在每行记录上存有三个字段 DB_TRX_ID , DB_ROLL_PTR , DB_ROW_ID字段,用于实现快照读
    (1) DB_TRX_ID:最新一次修改本行记录的操作ID
    (2) DB_ROLL_PTR:回滚指针,指向当前记录项的undo log信息
    (3) DB_ROW_ID:标识插入的新的数据行的id,无论有没有主键,都会通过该字段唯一标识一条记录
  1. undo日志
    为了回滚而记录的消息称之为撤销日志–undo log
  1. read view
    – 决定当前事务能看到哪个版本的数据
    – 可见性算法:将要修改的数据的DB_TRX_ID取出来,与当前活跃事务ID做对比,如果大于或者等于,就通过DB_ROLL_PTR取出undo日志中上一层的DB_TRX_ID,直到小于。保证当前可见数据是最稳定的版本。当前事务ID>所要修改记录里的DB_TRX_ID
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值