什么是事务

事务(Transaction)是访问和更新数据库的程序执行单元;事务中可能包含一个或多个 sql 语句,这些语句要么都执行,要么都不执行。

事务四大特性

  • 原子性:事务是原子性操作,要么全部成功,要么全部失败
  • 一致性:多个事务对数据库操作会保证数据一致性
  • 隔离性:并发时,事务之间互不影响
  • 持久性:事务提交之后对数据库的影响是持久性的,不会因为数据库宕机导致数据丢失

事务并发带来的问题

脏读

读到其它事务为提交的数据

不可重复读

同一个事务,select语句相同但读到数据不同

幻读

同一个事务,select语句项目但读到数据行数不同
不可重复读的重点是修改,幻读的重点在于新增或者删除。

隔离级别

读未提交(READ-UNCOMMITTED)

一个事务中,可以读取到其他事务未提交的变更

读提交(READ-COMMITTED)

一个事务中,可以读取到其他事务已经提交的变更

可重复读(REPEATABLE-READ)

一个事务中,直到事务结束前,都可以反复读取到事务刚开始看到的数据,不会发生变化
mysql的默认隔离级别是RR
RR和RC的区别是在一个事务中RR隔离级别的读到一张表的数据都是一样

可串行化(SERIALIZABLE)

即便每次读都需要获得表级共享锁,每次写都加表级排它锁,两个会话间读写会相互阻塞,会导致MySQL性能降低。

查看隔离级别:

SELECT @@tx_isolation;

clipboard.png

原理

事务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;

clipboard.png

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

clipboard.png
1.事务A执行begin开启事务,select查询时向系统申请一个版本号
2.事务B执行update更新了tuser表中id=4这一行数据的版本号
3.事务A select查询使用的还是第一次select时使用的版本号,判断id=4这一行的版本号比当前版本号大,通过undo_log找到和当前版本号一致的数据,事务A二次查询结果时一致的,实现了事务的可重复读

总结

MySQL作为一个开源数据库,被很多公司使用,在使用过程中去深入了解其内部原理是很有必要的事情,知其然并知其所以然,学习是一个积累的过程,只要坚持就会有收获。

参考

http://www.sohu.com/a/2925068...


小小的太阳
123 声望7 粉丝

reloading...