乐趣区

数据库锁笔记

引言

MyISAM不支持事务。

MyISAM 与 InnoDB 关于锁方面的区别是什么

MyISAM默认使用的是表级锁,不支持行级锁。

InnoDB默认使用的是行级锁,也支持表级锁。

读锁 / 共享锁:其他 session 可以读,但不能写。

写锁 / 排他锁:其他 session 既不能读,也不能写。

MyISAM,对数据进行查询时,自动为该表加上一个表级的“读锁”;对数据进行增删改时,会为该表加上一个表级的“写锁”。当“读锁”未被释放的时候,另一个 session 对这张表进行写操作会被阻塞。

InnoDB的行锁也类似,对一行进行加锁,如果是读锁,其他事务可以读,但是不可以写;如果是写锁,其他事务既不能读,也不能写。

InnoDB会自动为增删改查语句开启事务,并自动提交或回滚,在事务提交或回滚后会自动同时释放锁。

如果没有走事务,会进行全表遍历,提升为表锁。

MyISAM适合的场景

  1. 频繁执行全表 count 语句。MyISAM会保存整个表的行数。
  2. 对数据进行增删改的频率不高,查询非常频繁。
  3. 适合没有事务的场景。

InnoDB适合的场景

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

锁的分类

按锁粒度划分

表级锁、行级锁、页级锁。

表级锁速度快,但冲突多,行级冲突少,但速度慢。页级锁介于两者之间,锁定相邻的一组记录。

按锁级别划分

共享锁、排他锁。

按加锁方式划分

自动锁、显式锁。

按操作划分

DML锁、DDL锁。

按使用方式划分

乐观锁、悲观锁。

乐观锁:在操作时很乐观,认为操作不会产生并发问题(不会有其他线程对数据进行修改),因此不会上锁。但是在更新时会判断其他线程在这之前有没有对数据进行修改。

悲观锁:总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加(悲观)锁。一旦加锁,不同线程同时执行时, 只能有一个线程执行,其他的线程在入口处等待,直到锁被释放。

数据库事务的四大特性

ACID

A:原子性,事务包含的所有操作,要么全部成功,要么全部失败。
C:一致性,事务确保数据只有操作前状态和操作后状态,访问时绝不会出现中间态。
I:隔离性,多个事务并发执行时,一个事务的执行不应该影响其他事务。
D:持久性,一个事务一旦提交,对数据的修改永久保存。

事务隔离级别以及各级别下的并发访问问题

MySQL 事务的隔离级别

  1. READ-UNCOMMITTED:在一个事务中,可以读取到其他事务未提交的数据变化。在生产环境中不建议使用。
  2. READ-COMMITTED:在一个事务中,可以读取到其他事务已经提交的数据变化。是 Oracle 默认的事务隔离级别。
  3. REPEATABLE-READ:在其中一个事务中,直到事务结束前,都可以反复读取到事务刚开始时看到的数据。是 MySQL 默认的事务隔离级别。
  4. SERIALIZABLE:在每个读的数据行上都加表级共享锁,在每次写数据时都加表级排他锁。这样会造成 InnoDB 的并发能力下降,大量的超时和锁竞争就会发生。在生产环境中不建议使用。

事务并发访问引起的问题以及如何避免

  1. 更新丢失:MySQL所有事务隔离级别在数据库层面上均可避免。
  2. 脏读:一个事务读到另一个事务没有提交的数据,READ-COMMITTED事务隔离级别以上可避免。
  3. 不可重复读:其他事务有提交,导致多次读取同一数据时结果不一致,REPEATABLE-READ事务隔离级别以上可避免。
  4. 幻读:更新 M 条数据,其他事务对数据进行了增删,却成功了 M+N/M-N 条,产生了幻觉,SERIALIZABLE事务隔离级别可避免。但是 MySQLInnoDB引擎在 REPEATABLE-READ 就避免了幻读现象。

InnoDB REPEATABLE-READ 隔离级别下如何避免幻读

表象:快照读(伪MVCC

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

当前读和快照读

当前读:即加了锁的增删改查语句,读取的是记录的最新版本,并且读取之后还需要保证其他并发事务不能修改当前记录,所以要加锁。

快照读:不加锁的非阻塞读,SELECT。为了提升并发性能,读写不冲突,基于MVCC,避免了加锁操作,开销更低。

MVCC:多版本并发控制,避免使用锁,保存某个时间点上的数据快照。

next-key 锁

行锁:对单个行上锁。

Gap锁:对间隔上锁。

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

如果 WHERE 条件全部命中,则不会用 Gap 锁,只会加记录锁。

如果 WHERE 条件部分命中或全不命中,则会加 Gap 锁。

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

非唯一索引

只会锁住操作相邻的区间。

不走索引

所有的区间都会被锁住。

RC、RR 级别下的 InnoDB 的非阻塞读如何实现

  • 数据行里的 DB_TRX_IDDB_ROLL_PTRDB_ROW_ID 字段。
  • undo日志。
  • read view,决定当前事务能看到的是哪个版本的数据。
退出移动版