|
| 1 | + |
| 2 | + |
| 3 | +>无论何时,只要有多个查询需要在同一个时刻修改数据时,就会有并发问题。MySql主要在`服务器层`与`存储引擎层`进行并发控制。 |
| 4 | +
|
| 5 | +假设数据库中国一张邮箱表,每个邮件都是一条记录。如果某个客户正在读取邮箱,同时其他客户试图在删除邮箱表中的某一条数据。这个时候,读取的结构就是不确定的了。在MySql中会通过锁定防止其它用户读取同一数据。大多数时候,MySQL锁的内部管理都是透明的。 |
| 6 | + |
| 7 | +## MySQL锁的粒度 |
| 8 | +* 每种MySql引擎都可以实现自己的锁策略和锁粒度,将锁粒度固定在某个级别,可以为某些特定的场景提供更好的性能。 |
| 9 | + |
| 10 | +**表锁(table lock)** |
| 11 | +* 表锁是mysql中最基本的锁略,并且是开销最小的策略。它会锁定整个表,一个用户在对表进行写操作(插入、删除、更新等)前,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他读取的用户才能获得读锁,读锁之间是不相互阻塞的。 |
| 12 | +* 在特定的场景中,表锁也可能有良好的性能。例如,`READ L0CAL` 表锁支持某些类型的并发写操作。另外,写锁也比读锁有更高的优先级,因此-一个写锁请求可能会被插入到读 |
| 13 | +锁队列的前面(写锁可以插入到锁队列中读锁的前面,反之读锁则不能插入到写锁的前 |
| 14 | +面)。 |
| 15 | +* 尽管存储引擎可以管理自己的锁,MySQL本身还是会使用各种有效的表锁来实现不同 |
| 16 | +的目的。例如,服务器会为诸如`ALTER TABLE` 之类的语句使用表锁,而忽略存储引擎的 |
| 17 | +锁机制。 |
| 18 | + |
| 19 | +**行级锁(row lock)** |
| 20 | +* 行级锁可以最大程度地支持并发处理(同时也带来了最大的锁开销)。 |
| 21 | +* 在`InnoDB`和`XtraDB`,以及其他一些存储引擎中实现了行级锁。 |
| 22 | +* 行级锁只在存储引擎层实现,而MySQL服务器层没有实现。服务器层完全不了解存储引擎中的锁实现。 |
| 23 | + |
| 24 | +## MySQL的事务 |
| 25 | +**事务特性** |
| 26 | +* A(原子性)事务的各步操作是不可分的,保证一系列的操作要么都完成,要么都不完成; |
| 27 | +* C(一致性)事务完成,数据必须处于一致的状态; |
| 28 | +* I(隔离性)对数据进行修改的所有并发事务彼此之间是相互隔离,这表明事务必须是独立的,不应以任何方式依赖或影响其他事务; |
| 29 | +* D(持久性)表示事务对数据处理结束后,对数据更改必须持久化,不管是事务成功还是回滚。事务日志都能够保持事务的永久性。 |
| 30 | + |
| 31 | +**事务的隔离级别** |
| 32 | +* SQL标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable ) |
| 33 | +* 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。 |
| 34 | +* 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。 |
| 35 | +* 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。未提交的更改对其他事务是不可见的 |
| 36 | +* 串行化:对应一个记录会加读写锁,出现冲突的时候,后访问的事务必须等前一个事务执行完成才能继续执行。最高的隔离级别 |
| 37 | + |
| 38 | +**MySQL中的事务** |
| 39 | +* MySQL提供了两种事务型的存储引擎: `InnoDB`和`NDB Cluster`。另外还有一些第三方 |
| 40 | +存储引擎也支持事 |
| 41 | +* MySQL默认采用自动提交(AUTOCOMIT) 模式。如果不是显式地开始-一个个事务,则每个查询都被当作一事务执行提交操作。在当前连接中,可以通过设置AUTOCOMMIT变量来启用或者禁用自动提交模式: |
| 42 | +* InnoDB采用的是两阶段锁定协议(two-phase locking protocol)。在事务执行过程中,随 |
| 43 | +时都可以执行锁定,锁只有在执行`COMMIT`或者`ROLLBACK`的时候才会释放,并且所有的 |
| 44 | +锁是在同一时刻被释放。 |
| 45 | +* InnoDB也支持通过特定的语句进行显式锁定`SELECT ... LOCK IN SHARE MODE`和`SELECT FOR UPDATE` 些语句不属于SQL规范 |
| 46 | + |
| 47 | +## 多版本并发控制MVCC |
| 48 | +* `MVCC`是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。 |
| 49 | +* MVCC的实现,是通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对同张表,同一时刻看到的数据可能是不一样的。 |
| 50 | +* `InnoDB`的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。 |
| 51 | +* MVCC只在可重复读和读提交的隔离级别生效。其它两个级别都不兼容 |
| 52 | + |
| 53 | +> 在可重复读(`REPEATABLE READ`) 隔离级别下,MVCC具体是如何操作的。 |
| 54 | +
|
| 55 | +**SELECT查询操作时** |
| 56 | + |
| 57 | +InnoDB会根据以下两个条件检查每行记录: |
| 58 | +* InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。 |
| 59 | +* 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。 |
| 60 | + |
| 61 | +**INSERT** |
| 62 | +* InnoDB为新播入的每-一行保存当前系统版本号作为行版本号。 |
| 63 | + |
| 64 | +**DELETE** |
| 65 | +* InnoDB为删除的每-*行保存当前系统版本号作为行删除标识。 |
| 66 | + |
| 67 | +**UPDATE** |
| 68 | +* InnoDB为插入-行新记录,保存当前系统版本号作为行版本号,同时保存当前系统 |
| 69 | +版本号到原来的行作为行删除标识。 |
0 commit comments