面试系列 | MySQL事务特性及事务隔离

数据库中事务的四大特性(ACID):原子性、一致性、隔离性、持久性。如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性:

(1)原子性(Atomicity)

​    原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

(2)一致性(Consistency)

    一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

    如:拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

(3)隔离性(Isolation)

    隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

    即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

(4)持久性(Durability)

    持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

    例如:我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。

 

事务隔离机制:

(1) READ_UNCOMMITTED(读未提交)

  这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。

  解决第一类丢失更新的问题,但是会出现脏读、不可重复读、第二类丢失更新的问题,幻读 。

(2) READ_COMMITTED(读已提交)

  保证一个事务修改的数据提交后才能被另外一个事务读取,即另外一个事务不能读取该事务未提交的数据。

  解决第一类丢失更新和脏读的问题,但会出现不可重复读、第二类丢失更新的问题,幻读问题

(3) REPEATABLE_READ(可重复读)

  保证一个事务相同条件下前后两次获取的数据是一致的 (注意是 一个事务,可以理解为事务间的数据互不影响)

(4) SERIALIZABLE(串行化)

  事务被处理为顺序执行。

  解决所有问题, 对数据库的并发性能影响较大,一般不采用这个级别的隔离。

 

 

事务隔离性说明:

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

事务隔离级别为读提交时,写数据只会锁住相应的行。

事务隔离级别为串行化时,读写数据都会锁住整张表。

MySQL默认事务隔离级别为REPEATABLE_READ

1.查看当前会话隔离级别

select @@tx_isolation;

2.查看系统当前隔离级别

select @@global.tx_isolation;

3.设置当前会话隔离级别

set session transaction isolatin level repeatable read;

4.设置系统当前隔离级别

set global transaction isolation level repeatable read;

 

并发事务引起的问题:

更新丢失:

两个事务都同时更新一行数据时,假如第二个事务执行过程中途失败发生回滚,回滚数据导致第一个事务修改的数据被覆盖为原来的数据, 最终导致两个事务修改都失败。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。

READ COMMITTED以及以上的隔离机制都能解决

 

脏读:

脏读又称无效数据读出。一个事务读取另外一个事务还没有提交的数据叫脏读。

例如: A B两个事务处理同一条数据,在事务A修改这条数据但是还未提交数据时,事务B读取了事务A修改后的数据,之后事务A失败发生回滚,导致事务B读取的该条数据是不存在的, 因为读取的数据是事务A为提交的失败数据。

REPEATABLE READ隔离机制就能解决

 

不可重复读:

不可重复读是指在同一个事务内,两个相同的查询返回了不同的结果。

例如:A B两个事务处理同一条数据, 事务A读取某一数据,事务B读取并修改了该数据,A为了对读取值进行检验而再次读取该数据,得到了不同的结果。

REPEATABLE READ隔离机制就能解决

 

幻读:

事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据

例如:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级(假设数据库字段的类型为字符串类型, 主要是用来举例),但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样。这就叫幻读。

 

实际MYSQL高并发的事务问题的解决办法:

多用户并发修改同一条记录时,肯定是后提交的用户将覆盖掉前者提交的结果了。

这个直接可以使用加锁机制去解决,乐观锁或者悲观锁。

乐观锁:

    就是在数据库设计一个版本号的字段,每次修改都使其+1 , 这样在提交时比对提交前的版本号就知道是不是并发提交了,但是有个缺点就是只能是应用中控制,如果有跨应用修改同一条数据乐观锁就没办法了,这个时候可以考虑悲观锁。

乐观锁常用的是版本控制或者时间戳控制: 

事务A:执行 此时版本为 1;执行完update ... set version=2 where version = 1;

事务B:执行 此时版本为 1;执行完update ... set version=2 where version = 1;

这时候事务B如果发现version=1已经不存在了,因为事务A首先执行完更新了数据库,把version字段设为了2,这样就会导致该事务提交失败,需要我们在程序中判断异常情况下的后续操作。

悲观锁:

    就是直接在数据库层面将数据锁死,类似于oralce中使用select xxxxx from xxxx where xx=xx for update,这样其他线程将无法提交数据。

 

除了加锁的方式也可以使用接收锁定的方式:

    思路是在数据库中设计一个状态标识位,用户在对数据进行修改前,将状态标识位标识为正在编辑的状态,这样其他用户要编辑此条记录时系统将发现有其他用户正在编辑,则拒绝其编辑的请求,类似于你在操作系统中某文件正在执行,然后你要修改该文件时,系统会提醒你该文件不可编辑或删除。

    其次,不建议在数据库层面加锁,建议通过服务端的内存锁(锁主键)。

当某个用户要修改某个id的数据时,把要修改的id存入memcache,若其他用户触发修改此id的数据时,读到memcache有这个id的值时,就阻止那个用户修改。实际应用中,并不是让mysql去直面大并发读写,会借助“外力”,比如缓存、利用主从库实现读写分离、分表、使用队列写入等方法来降低并发读写。

 

欢迎关注我的微信公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值