事务处理(二) – 数据库事务
- 2019 年 10 月 30 日
- 筆記
事务简介
事务的作用
事务的作用是将一系列操作作为一个整体,一但其中出现问题,会回滚到事务的开始状态。即事务维护了数据的完整性和一致性。
事务的四个特性(ACID)
- 原子性:事务的操作是原子不可分割的。
- 一致性:事务的操作数据保证一致性,不存在一部分改变一部分不改变。
- 隔离性:隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
- 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响。
隔离级别导致的问题
如果不考虑隔离性,事务会出现以下问题。
脏读
脏读又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的。脏读是读到了另一个事务的中为commit的脏数据,所以称为脏读。
重现过程,将数据库隔离界别设为read-committed
,打开两个数据库连接,连接a去开启事务,insert 一个调数据,连接b查询出这条数据,连接a提交rollback,连接b再查询。
connect_A > begin; connect_A > insert into app(app_code,app_name,customer_code,latest_version_code,client_version,create_time) values ('1','1','003','1.0','1.0',now()); connect_B > select app_name from app; connect_A > rollback; connect_B > select app_name from app;
不可重复读
不可重复读是指一个线程中的事务读取到了另外一个线程中提交的update的数据。
如事务t1查询某人员数据,这时事务t2修改了该人员数据,t1再次查询该人员数据,发现该事务中两次查询的数据不一致。
重现过程,将数据库隔离设为read-uncommitted,打开两个数据库连接,连接a开启事务,查询表信息,连接b修改一条数据,连接a再查表记录,导致连接a两次查询数据不一致。
connect_A > begin; connect_A > select app_name from app; connect_B > update app set app_name='2' where app_code='1'; connect_A > select app_name from app; connect_A > commit;
虚读(幻读)
虚读是指一个线程中的事务读取到了另外一个线程中提交的insert的数据。
如事务t1查询人物数据有6条,事务t2插入1条数据,事务t1再查询人物数据就为7条了。
重现过程,将数据库隔离级别设为repeatable-read,打开两个数据库连接,连接a开启事务,查询表记录,连接b插入一条表记录,连接a再次查询表记录。
connect_A > begin; connect_A > select app_name from app; connect_B > update app set app_name='2' where app_code='1'; connect_A > select app_name from app; connect_A > commit;
不可重复读和虚读都是事务在执行过程中,有其他事务修改了数据,不过不可重复读是修改,而虚读是添加或删除。
事务提交
在事务中,使用rollback或commit作为事务结束的标志,如果事务未提交就关闭了,需要手动查询出事务进程,并杀掉
sql > show processlist; sql > kill xx;
事务的隔离级别
- Read uncommitted 读未提交,就是能读到未提交的数据
- Read committed 读已提交,读到已提交的数据
- Repeatable read 可重复读,一个事务中,重复读的数据都是一样的,不管是否有别的事务修改数据
- Serializable 串行化,每次读都获得整个表的锁,读写相互堵塞。称为悲观锁。
下面是隔离级别与数据问题的关系
|
读未提交(Read uncommitted) |
读已提交(Read committed) |
可重复读(Repeatable read) |
串行化(Serializable) |
---|---|---|---|---|
脏读 |
会发生 |
不会发生 |
不会发生 |
不会发生 |
不可重复读 |
会发生 |
会发生 |
不会发生 |
不会发生 |
虚读(幻读) |
会发生 |
会发生 |
会发生 |
不会发生 |
MySQL支持以上四中隔离级别,默认隔离级别是Repeatable read;Oracle只支持串行化(Serializable)、读已提交(Read committed),默认是读已提交(Read committed)级别。
MySQL查看当前隔离级别:
select @@tx_isolation;
MySQL设置隔离级别语句
set [glogal | session] transactionisolation level 隔离级别名称;
或
set tx_isolation='隔离级别名称';
如
set tx_isolation='read-committed'
锁
数据库的锁有X锁(排他锁),S锁(共享锁)。每个数据库的查询执行前,都必须要拿到锁才能执行。当一个连接拿到X锁(排他锁)时,其他的连接都拿不到锁了,要等这个连接的X锁解锁。如果一个连接拿到了S锁,其他数据库连接还是能拿到S锁的,互不干扰。
锁的粒度类型有record lock(行锁),gap lock(间隙锁)。行锁是按照索引锁住这个索引对应的数据。间隙锁是锁住这个索引以上的或以下的锁,就是范围锁。
三级封锁协议
第一级
在修改时修改数据开启X锁,事务提交关闭。事务只读不修改是不用加锁的。可以保证提交数据丢失的情况。可能会出现脏读,不可重复读,虚读。
第二级
在事务中,第一级封锁协议加上查询时对相应的数据添加S锁,查询结束关闭。这样可以保证事务读取的数据都是事务已经提交的,解决脏读问题
第三级
在事务中,第一级封锁协议加上查询对应数据添加S锁,S锁直到事务提交。可以解决可重复的问题。
mysql Innodb幻读
mysql通过(read view),mvcc实现避免幻读。即读的数据不是最新的数据,而是之前的快照。
锁的策略 — 事务隔离级别
读未提交(read-uncommitted)
查询使用S锁,修改时对应行数据使用X锁,修改结束释放X锁。
读已提交(read-committed)
查询使用S锁,查询结束,释放S锁。修改时,对应行数据使用X锁,直到事务结束,释放X锁。
可重复读(repeatable-read)
使用MVVC。
串行化(serializable)
该隔离级别会在读取的每一行数据上都加上锁,退化为基于锁的并发控制,即LBCC。使用的间隙锁,所以能解决幻读。