数据库并发写入问题-丢失更新与写入偏差

MVCC

https://blog.csdn.net/qq_41775852/article/details/104853909
数据库中的MVCC多版本并发控制(快照隔离)可以避免事务并发时的脏读、不可重复读以及幻读的问题,但是却无法避免丢失更新以及写入偏差的问题。

丢失更新和写入偏差

  • 丢失更新:两个事务并发读取同一记录,并在此基础上修改记录,并将其写回数据库,第二次写入的结果会覆盖第一次写入的结果,导致数据库状态不一致。(丢失更新不仅出现在数据库中,在应用程序多线程并发修改变量、分布式系统多主复制和无主复制中都会遇见)

    例如:以下两个事务,对A变量进行读取10,T1事务将其减5,T2事务将其减8,两事务提交之后,A的状态为2,即T1事务的更新丢失了。如果A代表账户余额,这种错误是不可接受的。
    在这里插入图片描述

  • 写入偏差:写入偏差可视为丢失更新问题的一般化,如果两个事务读取相同的记录集,然后更新记录集,不同的事务可能更新(插入、删除)不同的记录,则可能发生写入偏差,导致数据库状态不一致或者不符合约束条件。

    比如:1.医院规定必须至少有一名医生在值班。而两个值班医生同时进行请假事务,由于快照隔离,两个医生在检查当前值班医生数量时结果都为2,所以他们都可能请假。但是两个事务提交之后,却没有医生值班了,违反了医院的规定。2.两个用户同时修改唯一用户名,由于快照隔离,两人都发现数据库中无重复用户名,则修改成功。两个修改事务提交后,却出现用户名冲突。
    在这里插入图片描述
    如果并发事务没有先读取记录集的值,而是直接写入新的记录集值,个人认为不属于丢失更新或者写入偏差,因为这导致的数据库不一致,是用户的错误逻辑所导致的,不是事务并发的原因。即使时事务串行,第二个写入还是会直接覆盖第一个写入。

丢失更新和写入偏差的原因是相同的:并发事务首先查询得到了相同的记录集,然后根据此记录集进行更新、插入、删除等动作。由于快照隔离,事务感受不到其他事务对记录集的更改,所以可以成功提交事务。但是并发事务的修改却出现了矛盾,导致数据库不一致。

《设计数据密集型应用》书中这样说:
在这里插入图片描述

解决方法

CAS

比较并设置(CAS, Compare And Set)是一种原子操作,此操作的目的是为了避免丢失更新:只有当前值从上次读取时一直未改变,才允许更新发生。如果当前值与先前读取的值不匹配,则更新不起作用,且必须重试读取-修改-写入序列。但是在提供快照隔离的事务中不起作用

显示锁定

由于并发事务的更改都是基于读取到的记录集,所以只要事务对读取到的记录集加排他锁,然后再对记录集更新,提交事务之后才释放排他锁。这样并发事务就只能读取到其他事务以及提交后的数据,则不会出现不一致的状态。

比如对于医生请假事务,读取并锁定所有医生的值班状态,修改自己的值班状态为请假,提交事务后释放锁。则第二个医生在请假事务中读取所有医生值班状态会阻塞,直到上一个事务提交,发现只剩自己一个人值班,则无法请假。

序列化隔离级别

使用事务的序列化隔离级别可以避免丢失更新和写入偏差。

幻读与写入偏差

http://www.likecs.com/show-44612.html
http://blog.sina.com.cn/s/blog_499740cb0100ugs7.html
从《设计数据密集型应用》看到,快照隔离避免了只读查询中幻读,但是对于读写事务中,幻读可能会导致特别棘手的写入偏差情况。

这里的意思表示在快照隔离的情况下,对于读写事务依然有可能出现幻读。这其实是与我之前的认知有所不同的,后来仔细查看了之后发现这里对幻读的定义似乎有所不同:
- 我之前对幻读的认知是,事务中进行两次范围查询,第二次查询到第一次查询没有的记录,称之为幻读。这样快照隔离是可以解决幻读的。
- 书中表示的读写事务幻读,是将事务中的写操作也当成一次查询操作,由于写操作在数据库内部是需要进行当前读的(当前读可以读取最新提交的数据,而不是快照的数据),所以写操作是可以感受到新插入的数据,导致幻读。这种幻读是快照隔离无法解决的,MySQL RR隔离级别中可使用间隙锁解决,即还是显示锁定方法。但是我不明白为什么书里说幻读导致了写入偏差,因为我觉得在写入时能够感受到其他事务的插入,反而导致本事务不能进行不一致的修改,从而事务回滚,避免了写入偏差。

对于幻读到底是那种定义,也有很多争论:
https://www.zhihu.com/question/47007926

但是我觉得只要明白其中的原理就可:
对于快照隔离(MVCC),事务中的快照读只能看到快照数据和当前事务的修改。而对于当前读则能看到最近提交事务后的数据。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 一些支持并发写入的轻量级数据库有: - SQLite - LevelDB - RocksDB - LMDB - Berkeley DB 这些数据库都是轻量级的,而且支持并发写入,但它们各有各自的特点。例如,SQLite 具有较高的兼容性和简单易用的特点,而 RocksDB 则具有更高的性能和更强的压缩能力。 ### 回答2: 支持并发写入的轻量级数据库有很多种,以下是其中一些常见的数据库: 1. SQLite:SQLite是一种嵌入式数据库,它具有轻量级和高效的特点。它支持多个进程同时写入,但是在同一时间只能有一个进程进行写操作。 2. H2:H2是一个Java编写的嵌入式数据库系统,它可以在内存中运行,并支持同时有多个连接对数据库进行写入操作。 3. MySQL:MySQL是一种常见的关系型数据库管理系统,它支持并发写入操作,可以通过设置适当的事务隔离级别来控制并发写入的行为。 4. PostgreSQL:PostgreSQL是另一种常见的关系型数据库管理系统,它也支持并发写入。它具有高度可定制性,并提供了许多高级功能,如触发器、视图和存储过程等。 5. MariaDB:MariaDB是一个MySQL的分支,它继承了MySQL的并发写入能力,并增加了一些新特性和改进。 总之,以上列举的数据库都是支持并发写入的轻量级数据库,选择哪一种数据库最适合取决于具体的应用场景和需求。 ### 回答3: 支持并发写入的轻量级数据库有很多,以下是其中几个常见的: 1. SQLite:SQLite 是一个嵌入式数据库引擎,以轻量、独立、无服务器和零配置而闻名。它支持并发写入操作,可以被广泛应用于移动设备、桌面应用和嵌入式系统中。 2. Firebird:Firebird 是一个开源的关系数据库管理系统,它提供了高度的并发性和可靠性。它支持多个并发事务的并发写入操作,具有强大的事务处理功能,适合于需要高并发写入的应用场景。 3. H2 Database:H2 Database 是一个嵌入式Java数据库引擎,它非常轻量级并且易于使用。它支持并发写入操作,并提供了各种特性如内存模式、持久化模式和集群模式,适用于各种规模的应用系统。 4. Berkeley DB:Berkeley DB 是一个嵌入式数据库引擎,它提供了高度并发的读写操作。它具有高性能、低资源消耗和可靠性等特点,适用于各种嵌入式系统、分布式系统和高速缓存等场景。 5. LevelDB:LevelDB 是一个开源的键值存储数据库,支持并发写入操作。它是由Google开发的,具有高性能和可靠性,适用于应用在需要大规模并发写入的场景。 总之,以上提到的这些轻量级数据库都支持并发写入操作,并且具有各自的特点和适用场景。在选择数据库时,可以根据具体的应用需求和性能要求来选择合适的数据库
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值