事务处理(二) - 数据库事务

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luo4105/article/details/81568762

事务简介

事务的作用

事务的作用是将一系列操作作为一个整体,一但其中出现问题,会回滚到事务的开始状态。即事务维护了数据的完整性和一致性。

事务的四个特性(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;

不可重复读和虚读都是事务在执行过程中,有其他事务修改了数据,不过不可重复读是修改,而虚读是添加或删除。

扫描二维码关注公众号,回复: 3086491 查看本文章

事务提交

在事务中,使用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。使用的间隙锁,所以能解决幻读。

猜你喜欢

转载自blog.csdn.net/luo4105/article/details/81568762