什么是事务
事务(Transaction)是访问和更新数据库的程序执行单元;事务中可能包含一个或多个 sql 语句,这些语句要么都执行,要么都不执行。
事务四大特性
- 原子性:事务是原子性操作,要么全部成功,要么全部失败
- 一致性:多个事务对数据库操作会保证数据一致性
- 隔离性:并发时,事务之间互不影响
- 持久性:事务提交之后对数据库的影响是持久性的,不会因为数据库宕机导致数据丢失
事务并发带来的问题
脏读
读到其它事务为提交的数据
不可重复读
同一个事务,select语句相同但读到数据不同
幻读
同一个事务,select语句项目但读到数据行数不同
不可重复读的重点是修改,幻读的重点在于新增或者删除。
隔离级别
读未提交(READ-UNCOMMITTED)
一个事务中,可以读取到其他事务未提交的变更
读提交(READ-COMMITTED)
一个事务中,可以读取到其他事务已经提交的变更
可重复读(REPEATABLE-READ)
一个事务中,直到事务结束前,都可以反复读取到事务刚开始看到的数据,不会发生变化
mysql的默认隔离级别是RR
RR和RC的区别是在一个事务中RR隔离级别的读到一张表的数据都是一样
可串行化(SERIALIZABLE)
即便每次读都需要获得表级共享锁,每次写都加表级排它锁,两个会话间读写会相互阻塞,会导致MySQL性能降低。
查看隔离级别:
SELECT @@tx_isolation;
原理
事务ACID特性是如何实现的哪?
事务的原子性、隔离性、持久性都是为了实现数据一致性
undo_log实现原子性
InnoDB实现事务的回滚靠的就是undo_log
对数据进行修改会追加undo_log
redo_log实现持久性
对数据库的修改并不会立即修改磁盘中的数据表,而是先追加redo_log,再更新内存中数据(Write-ahead
logging,预写式日志),后续由master thread或者刷脏线程阶段性将这些数据刷到磁盘
这样阶段性的刷脏可以将多个IO merge成一个IO,也降低了访问延时,提供系统吞吐,当MySQL突然宕机也不会导致数据丢失,可根据redo_log进行数据恢复
锁+MVCC实现隔离性
严格的隔离性对应Serializable(可串性化)隔离级别,在实际使用处于性能的考虑很少使用。
隔离性追求的是并发情况下事务之间互补影响
- (一个事务)写操作与写操作(另一个事务)之间隔离通过锁机制实现
- (一个事务)写操作与读操作(另一个事务)之间隔离通过MVCC实现
锁机制
在对数据进行修改时,需要获得锁,在数据修改成功事务提交之后释放锁
MySQL有行锁和表锁,InnoDB使用的行锁,行锁比表锁锁定更少的资源,在大部分情况的性能更高
模拟并发更新导致死锁的情况:
在一个窗口连接mysql并执行以下操作
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update tuser set age=20 where id_card=4;
Query OK, 0 rows affected (0.12 sec)
Rows matched: 0 Changed: 0 Warnings: 0
另一个窗口连接mysql并执行
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update tuser set age=20 where id_card=4;
查看锁情况
mysql> select * from information_schema.innodb_locks;
+-----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+-----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+-----------+
| 2150479:671:3:2 | 2150479 | X | RECORD | `test`.`tuser` | PRIMARY | 671 | 3 | 2 | 1 |
| 2150478:671:3:2 | 2150478 | X | RECORD | `test`.`tuser` | PRIMARY | 671 | 3 | 2 | 1 |
+-----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+-----------+
2 rows in set, 1 warning (0.08 sec)
mysql> show engine innodb status;
MVCC
Multi-Version Concurrency Control,即多版本的并发控制协议
MVCC解决了脏读、不可重复读、幻读
MVCC,多个版本的数据可以共存,主要是依靠数据的隐藏列(也可以称之为标记位)和 undo log。
每行数据都会多2列
create_time系统版本号
delete_time系统版本号
select
1.判断create_time, 行的系统版本号 <= 当前事务的系统版本号
2.判断delete_time, 行的删除版本未定义 || 大于当前事务版本号
update
插入一条新纪录,更新create_time
删除一条数据,并更新delete_time
insert
更新create_time
delete
更新delete_time
1.事务A执行begin开启事务,select查询时向系统申请一个版本号
2.事务B执行update更新了tuser表中id=4这一行数据的版本号
3.事务A select查询使用的还是第一次select时使用的版本号,判断id=4这一行的版本号比当前版本号大,通过undo_log找到和当前版本号一致的数据,事务A二次查询结果时一致的,实现了事务的可重复读
总结
MySQL作为一个开源数据库,被很多公司使用,在使用过程中去深入了解其内部原理是很有必要的事情,知其然并知其所以然,学习是一个积累的过程,只要坚持就会有收获。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。