数据库事务的特性和隔离级别

数据库事务的特性

在数据库操作中,一项事务(Transaction)是由一条或多条操作数据库的 SQL 语句组成的一个不可分割的工作单元,这些操作要么都完成,要么都取消。接下来将围绕事务的特性、并发问题以及隔离级别进行讲解。

  1. 原子性(Atomicity)
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败

  2. 一致性(Consistency)
    事务必须使数据库从一个一致性状态变换到另外一个一致性状态

  3. 隔离性(Isolation)
    事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

  4. 持久性(Durability)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

如何保证在多用户并发的情况下时候的隔离性,这就需要设置数据库事务的隔离级别

事务没有隔离级别下产生的问题

  1. 脏读
    脏读指一个事务读取了另外一个事务未提交的数据。因为第二个事务可能会回滚,这时第一个事务就读到了数据库不存在的脏数据。

  2. 不可重复读
    一个事务对同一行数据读取两次时,读取到的内容不一样。在一个事务的两次相同查询之间,有另外一个事务更新了数据并提交。

  3. 幻读
    虚读(幻读)是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。

  4. 丢失更新
    指两个事务同时更新一行数据,后提交(或撤销)的事务将之前事务提交的数据覆盖了。

    1. 第一类丢失更新是指两个事务同时操作同一个数据时,当第一个事务撤销时,把已经提交的第二个事务的更新数据覆盖了,第二个事务就造成了数据丢失。

    2. 第二类丢失更新是指当两个事务同时操作同一个数据时,第一个事务将修改结果成功提交后,对第二个事务已经提交的修改结果进行了覆盖,对第二个事务造成了数据丢失。

事务的隔离级别

  1. 读未提交(Read uncommitted)
    顾名思义,就是可以读到未提交的内容。如果一个事务开始写操作,其他的事务不允许同时写操作,只能读,这种情况只能防止丢失更新的问题

    实现原理:
    对读取不加锁,对数据的更新的瞬间对其加行级共享锁,直到事务结束才释放。
    表现为:一个事务对一条数据更新时,其他的事务不能对相同的行更新,但是读取不受影响

  2. 读已提交(Read Committed)
    就是只能读到已经提交了的内容。这是各种系统中最常用的一种隔离级别,也是SQL Server和Oracle的默认隔离级别。
    因为读已提交采用的是快照读,假设没有“快照读”,那么当一个更新的事务没有提交时,另一个对更新数据进行查询的事务会因为无法查询而被阻塞,这种情况下,并发能力就相当的差。
    而“快照读”就可以完成高并发的查询,不过,“读提交”只能避免“脏读”,并不能避免“不可重复读”和“幻读”。

    实现原理:
    事务对当前被读取的数据加行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。
    表现为:事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,读到的只能是事务2更新前的版本,要不就是事务2提交后的版本(快照读)。

  3. 可重复读 (Repeated Read)
    可重复读,顾名思义,就是专门针对“不可重复读”这种情况而制定的隔离级别,自然,它就可以有效的避免“不可重复读”。而它也是MySql的默认隔离级别。
    在这个级别下,普通的查询同样是使用的“快照读”,但是,和“读提交”不同的是,当事务启动时,就不允许进行“修改操作(Update)”了(因为在读取的时候,加了行级共享锁,直到事务结束才释放,所以这之间是不能进行修改操作的),而“不可重复读”恰恰是因为两次读取之间进行了数据的修改,因此,“可重复读”能够有效的避免“不可重复读”,但却避免不了“幻读”,因为幻读是由于“插入或者删除操作(Insert or Delete)”而产生的。

    实现原理:
    事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放
    表现为:事务1读取某行记录时,事务2也能对这行记录进行读取、但不能更新,只有当事务1结束,行级共享锁释放才能进行更新操作;当事务2对该记录进行更新时是要等到事务1结束后释放了行级共享锁之后才可以,读到的仍然是第一次读取的那个版本。

  4. 串行化(Serializable)
    这是数据库最高的隔离级别,这种级别下,事务“串行化顺序执行”,也就是一个一个排队执行。
    这种级别下,“脏读”、“不可重复读”、“幻读”都可以被避免,但是执行效率奇差,性能开销也最大,所以基本没人会用。

    实现原理:
    事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。
    表现为:事务1正在读取A表中的记录时,则事务2也能读取A表,但不能对A表做更新、新增、删除,直到事务1结束。事务1正在更新A表中的记录时,则事务2不能读取A表的任意记录,更不可能对A表做更新、新增、删除,直到事务1结束。

共享锁与排他锁的区别

数据库的增删改操作默认都会加排他锁,而查询不会加任何锁。

  1. 共享锁:对某一资源加共享锁,自身可以读该资源,其他人也可以读该资源(也可以再继续加共享锁,即 共享锁可多个共存),但无法修改。要想修改就必须等所有共享锁都释放完之后。语法为:
    select * from table lock in share mode

  2. 排他锁:对某一资源加排他锁,自身可以进行增删改查,其他人无法进行任何操作。语法为:
    select * from table for update

总结:

为什么会出现“脏读”?因为没有“select”操作没有规矩。
为什么会出现“不可重复读”?因为“update”操作没有规矩。
为什么会出现“幻读”?因为“insert”和“delete”操作没有规矩。

“读未提(Read Uncommitted)”能预防啥?预防丢失更新。
“读提交(Read Committed)”能预防啥?使用“快照读(Snapshot Read)”,避免“脏读”,但是可能出现“不可重复读”和“幻读”。
“可重复读(Repeated Red)”能预防啥?使用“快照读(Snapshot Read)”,锁住被读取记录,避免出现“脏读”、“不可重复读”,但是可能出现“幻读”。
“串行化(Serializable)”能预防啥?排排坐,吃果果,有效避免“脏读”、“不可重复读”、“幻读”,不过效果谁用谁知道。

本文参考:

https://baijiahao.baidu.com/s?id=1611918898724887602&wfr=spider&for=pc
http://c.biancheng.net/view/4220.html
https://blog.csdn.net/sinat_36710456/article/details/83546510
https://blog.csdn.net/localhost01/article/details/78720727

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值