mysql 并发性_MySQL 事务

1、什么是事务,为什么需要事务

事务(Transaction)是数据库并发控制的不可分割的基本单位,可以将一系列的数据库操作集合到一个事务中,从总体上来讲这个事务可能会对数据库进行一些变动。事务存在的意义在于多个同时事务执行完成以后不管成功与否都要保持数据的干净、有序,也就是数据的一致性。比如我们在 12306 上购买火车票,如果付款成功我们将获得金钱 -1、火车票 +1,如果付款失败则金钱 -0、火车票 +0 。所以你有没有想过去找找免费购票的漏洞?

8ec1de52a397a7d75509da12e5ffaecf.png

2、事务的四大特性ACID

ACID四大特性是事务的基本特性,其保证了上面的买票行为能正确执行。

a3261a009674b9e86ee96009057bc163.png

2.1 原子性

事务要么成功,要么失败,如果失败则所有对数据库的操作都没有执行。如果在执行过程中出现异常错误,则所有执行都取消,否则就会对数据库产生副作用,造成数据“污染”。Mysql 原子性实现的基础是 undo log。

1BEGIN;
2INSERT INTO xx_table (id, name) VALUES (1, 'a');

上述操作在事务开启后,如果 commit 则应该更改数据记录,如果失败则可以通过 rollback 回滚到之前的状态。

2.2 持久性

都事务成功执行,则所有对数据库的修改都需要持久化的磁盘上,即使中途出现断电、宕机、重启等异常情况,否则就会出现数据不一致。Mysql 持久性实现的基础是 redo log。

1BEGIN;
2INSERT INTO xx_table (id, name) VALUES (1, 'a');
3COMMIT;

上述事务成功执行后应该保证数据可以准确无误的落入磁盘中,即使出现异常情况。

2.3 隔离性

当多个事务同时执行时会出现互相争夺同一资源,也可能同时修改同一数据,这就需要保证各个数据之间能合理的执行自身的操作,同时不影响另一事务执行,最后还要尽可能提高并发执行的性能。Mysql 根据性能和并发安全性实现了4个隔离级别,级别越高并发执行能力越低,并发安全性越高:

  • 读未提交 READ UNCOMMITTED
  • 读已提交 READ COMMITTED
  • 可重复读 REPEATABLE READ
  • 串行 SERIALIZABLE

6d141cbab1322a8618f50febf2940b16.png

Mysql 的隔离性是通过锁、多版本并发(MVCC)实现的。

2.4 一致性

保证数据的一致性正是数据库最重要的部分,Mysql 正是通过原子性、持久性和隔离性来保证了数据的一致性。

3、Mysql 如何实现事务

3.1 undo log (回滚日志)

如何保证事务失败情况下时间能倒流呢?一个简单的办法就是拿个小本本把事务中每一个操作记下来,如果出现异常就可以照着改回来就好了。Mysql 的 undo log 就是这个小本本,当事务开启后,mysql 会把相应的每一个的操作记录到内存中,再从内存持久化到硬盘中。而且undo log持久化到硬盘是在数据库变更之前执行完成的,这样即使中途出现断电等情况,重启后依然可以更加 undo log 就行回滚。

f8fe4aa62eca2de579fcd070ba90b72b.png

Mysql undo log 有两个特点:

  • 每个修改、删除语句都有一条对应的 undo log,并且 undo log 先于数据持久化到磁盘;
  • undo log 是逻辑日志,相当于回滚的是执行的逆向操作,如果数据 update 了,则重新 update 到原来的状态,删除了则重新生成。

undo log 保证了单个事务的原子性

3.2 redo log (重做日志)

当事务成功提交后需要保证数据变更能准确到落到磁盘中,假设这一过程中被拔了电源改怎么办?Mysql 写数据是先写到内存缓存区中 (buffer pool)中,然后由后台线程定期将数据由内存搬到磁盘中,所以一断电内存中的数据就没了。好记性不如烂笔头,同样一个简单的办法就是拿出我们的小本本记下来,等重启了就照着小本本重新执行一次就可以了,这就是 mysql 的 redo log。

2cf39f814215e3776e96667502d64379.png

redo log 从缓存到磁盘这一过程是同步的,redo log 保证了事务的持久性。

3.3 Mysql 锁和 MVCC

3.3.1 Mysql 锁

类似于多线程中的锁技术,Mysql 中的锁可以用来解决多个请求更改同一数据问题,这是一种常用的并发控制手段,锁可以解决数据不一致问题,同时也降低了并发性能。Mysql 有两种锁来控制并发操作:共享锁排他锁

1、共享锁 (share lock),读锁: 读锁可以共享,多个读请求可以并发执行;
2、排他锁 (exclusive lock),写锁:写锁会排斥其他所有获取锁的请求,一直阻塞至操作完成释放锁。

加读锁方法:

1SELECT * FROM xx_table WHERE id = 1 LOCK IN SHARE MODE;

加写锁方法:

1SELECT * FROM xx_table WHERE id = 1 FOR UPDATE;

3.3.2 Mysql MVCC

单纯使用锁来解决并发安全问题可能会发幅度降低性能,Mysql 引入了另外一种机制,多版本并发控制(MVCC)。当某一个事务连接到mysql后,它所查询到的数据为当前时刻的快照,其他事务对同一数据的更改并不会影响到当前事务的查询。也就是其他事务的变更操作在未提交之前对当前事务都是不可见的。

Mysql 会在每一张表后面隐式的添加三列,其中两列(DB_TRX_ID,DB_ROLL_PT)和 mvcc 有关,这两列标注了此行数据变更或被删除的事务id或时间戳。当事务变更、删除某一数据时,mysql 并不会真正执行操作,只是在相应的列做好标记,表示数据已经被更改,其他事务读到的仍是原来的老数据。

6ea9f9ccf8b6e608044c3048b3cf00f9.png

通过 mvcc 就可以解决读操作不一致问题(脏读),避免了锁带来的巨大开销

3.4 Mysql 事务隔离级别实现

Mysql 实现了四个级别的事务隔离,开发者可以针对不同的场景选择合适的事务隔离级别,已达到性能与安全之间的平衡。

读未提交 READ UNCOMMITTED

在这一级别,当前事务可以读取到其他事务未提交的数据,这样做的坏处是脏读,好处是不需要任何锁,可以做到读写并行,大幅度提高并发性能。

读已提交 READCOMMITTED

一个未提交的事务对当前事务是不可见的,当前事务可以读取到已经提交的变动,在实际开发中,大多数场景都使用此级别。Mysql 的读已提交使用排它锁和 mvcc 实现,实现了读写分离,但是会产生不可重复读幻读问题。

可重复读 REPEATABLE READ

这是 mysql 的默认隔离级别,单个事务内读取的结果是不变的,解决了脏读、不可重复读问题,但是仍存在幻读问题(mysql 通过 next-key lock 避免了幻读),此级别可以使用读写锁实现,也可以采用 mvcc 实现,采用读写锁逻辑简单但只能串行执行,采用写锁+ mvcc 实现复杂但可以读写分离。

串行读 SERIALIZABLE:

严格采用读写锁实现,所有操作全部串行执行,不会造成数据不一致问题,但并发性能极差。

3.5 一致性的实现

Mysql 正是通过原子性、持久性和隔离性,保证了数据的一致性。

参考资料

  • 『浅入浅出』MySQL 和 InnoDB
  • 『浅入深出』MySQL 中事务的实现
  • 【MySQL(5)| 五分钟搞清楚 MVCC 机制】
  • Mysql事务实现原理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值