在数据库中,在相同的时间段内,超过两个(含)以上的会话事务对共享数据进行存取操作(包括查询、插入、修改、删除等)即形成并发。
并 发 = 共 享 数 据 + 对 共 享 数 据 的 操 作 并发 = 共享数据 + 对共享数据的操作 并发=共享数据+对共享数据的操作
如果多会话事务并发操作共享数据时出现了问题,则称发生了并发冲突。
并 发 冲 突 = 共 享 数 据 + 对 共 享 数 据 的 问 题 操 作 并发冲突 = 共享数据 + 对共享数据的问题操作 并发冲突=共享数据+对共享数据的问题操作
如果多会话事务并发操作的共享数据正确,则称并发一致性。指数据在多会话事务并发运行时的一致性。多会话事务并发运行时,要保证操作共享数据的正确性。
数据业务一致性:指数据在业务逻辑上的一致性。一事务内部SQL操作系列,要保证相关数据在业务逻辑上的一致性。
并 发 一 致 性 = 共 享 数 据 + 对 共 享 数 据 的 正 确 操 作 并发一致性 = 共享数据 + 对共享数据的正确操作 并发一致性=共享数据+对共享数据的正确操作
在保证数据正确的前提下, 在相同的时间段内,同时运行的程序数量。
加 大 并 发 性 = 有 并 发 冲 突 的 会 话 事 务 串 行 运 行 + 无 并 发 冲 突 的 会 话 事 务 并 发 运 行 加大并发性 = 有并发冲突的会话事务串行运行 + 无并发冲突的会话事务并发运行 加大并发性=有并发冲突的会话事务串行运行+无并发冲突的会话事务并发运行
当用户与数据库建立会话之后,便可以与数据库进行相应权限的操作,而这就必然涉及到了事物。
事物是一组具有ACID属性的SQL命令组成的单个逻辑工作单元。事务是一个逻辑操作序列,这些逻辑操作要么都执行,要么都不执行,它是一个不可分割的工作单位。数据库事务只能保证单线程数据的正确性。
事物具有四个基本属性ACID,即原子性、一致性、隔离性和持久性。
指事务中的SQL操作要么全部成功(提交事务写日志),要么全部失败(回滚事务)。想要保证事务的原子性,就意味着需要在操作发生异常时,对该事务所有之前执行过的操作进行回滚。回滚是通过回滚日志(Undo Log)实现的。简单的说,回滚日志就是记录了你所有操作的逆操作,在需要回滚时,就把这个事务的回滚日志里的操作全部执行一次。需要注意的是原子性是由事务日志保证的,与开发人员无关。开发人员只需要选择事物的提交模式以及事物的开始和结束时机。
指该会话事务内部的SLQ操作及操作的数据库对象对并发的其它会话事务是隔离的。事务的隔离级别是由数据库提供的,数据库隔离级别在服务器端保证客户端用户一定不会发生哪些并发冲突。服务器端采用的技术手段有隐式锁、多版本(MVCC)。开发人员可以选择使用哪种隔离级别并且指定显示锁。隔离级别有RU,RC,RR和S,这主要是用于数据库多用户并发控制,与单用户关系不大。
指事务一旦被提交,对数据库的改变是永久的。数据一定会被写入到数据库中并持久储存起来。当事务被提交后就无法再回滚。持久性是由事务日志保证的。也需要开发人员commit来保证。
事务的一致性是指在没有并发其他事务下(单用户),事务执行前和执行后的数据都要满足数据库约束(完整性约束(列约束、行约束)和自定义约束(触发器、存储过程))和业务逻辑约束。在关系型数据库中,所有的规则必须应用到事务的修改上,以便维护所有数据的完整性。
事物一致性基本可以理解为是事务对数据完整性约束的遵循。这些约束可能包括主键约束、外键约束或是一些用户自定义约束(触发器等)。事务执行的前后都是合法的数据状态,不会违背任何的数据完整性,这就是“一致”的意思。
同时这个含义中也隐含着对开发者的要求,就是不能写出错误的事务逻辑,比如银行的转账不能只加钱不减钱,这是应用层面的一致性要求。
读不一致性本质上是读写操作的不一致性
写不一致性本质上是写写操作的不一致性
丢失修改即一个事务的更新覆盖了另一个事务的更新。事务A和事务B需要对同一个row的元素进行修改。A和B同时读到该row的的数据,分别修改,后提交的事务B覆盖了事务A的更新。更新丢失本质上是写写操作的冲突,解决办法是一个一个地写。
防止丢失修改的并发控制类型常见的有三种方法:
事物的隔离级别是指一个会话事务对数据库的存取与并发的另一个会话事务的隔离程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 并发一致性就越好, 但并发性越弱。
锁是用于多用户并发控制的。数据库有两种基本的锁模式,排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他会话的事务不能对它加共享锁读取和加排它锁修改。加了共享锁的数据对象可以被其他会话的事务加共享锁读取,但不能加排它锁修改。Oracle数据库利用这两种基本的锁模式来对数据库的事务进行并发控制。
其中DML锁(data locks,数据锁,Data Manipulation Language),用于保护并发情况下数据的并发一致性,DML锁主要包括TM锁(Table Manager 表级锁总称)和TX锁(Transaction Exclusive),其中TM锁为表级锁,TX锁称为事务锁或行级锁。
锁有显示锁和隐式锁之分,属于悲观控制模型
锁的阻塞是指假如两个会话都要对一个字段的值做修改,第一个会话持有锁,第二个会话申请锁,如果出现锁互斥,等第一个会话提交解锁后它才能持有锁,然后才能进行修改。第二个会话在等待锁的期间,就是被阻塞状态。如果第一个会话持有锁很长时间不提交,可能会导致其它多个会话在申请该对象的锁时,都被阻塞或出现超时异常。阻塞是由于资源不足引起的排队等待现象。
而死锁则是因为两个对象在拥有一份资源的情况下申请另一份资源,而另一份资源恰好又是这两对象持有的,导致两对象无法完成操作,且所持资源无法释放,引起死锁。假如说事物A现在需要修改row=1的数据,加了隐式的X锁,而事物B需要修改row=2的数据,也加了隐式的X锁。之后事物A需要修改row=2的数据,因为事物B的锁正占有row=2,所以此时出现锁等待,而当事物B修改row=1的数据时,这是便会产生死锁。
避免阻塞的手段:
select…where…order by rand();
这样的语句,由于类似这样的语句用不到索引,因此将导致整个表的数据都被锁住。避免死锁的手段:
使用锁的原则:在满足完整性约束、业务需求,解决多事务并发冲突,保证数据正确性的前提下,尽可能减少阻塞和避免死锁,提高事务的并发性,保证程序的并发质量。
在商业数据库中,通过多版本,select不需要加锁,也不会读到脏数据,不存在读写依赖,写的排它锁不会阻塞读,加大了并发性。这属于乐观控制模型
多版本一般会在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。在版本号方法中,要更新的记录必须具有一个包含日期时间戳或版本号的列。当读取该记录时,日期时间戳或版本号将保存在客户端。然后,将对该值进行部分更新。
客户端的解决方法是通过记录原始行来解决的,这属于乐观控制模型。首先通过客户端读取数据行,然后where通过记录原始行进行定位。
处理并发的一种方法是仅当 WHERE 子句中的值与记录上的值匹配时才进行更新。该方法的 SQL 表示形式为:
UPDATE Table1 SET Column1 = @newvalue1, Column2 = @newvalue2 WHERE DateTimeStamp = @origDateTimeStamp
或者,可以使用版本号进行比较:
UPDATE Table1 SET Column1 = @newvalue1, Column2 = @newvalue2 WHERE RowVersion = @origRowVersionValue
如果日期时间戳或版本号匹配,则表明数据存储区中的记录未被更改,并且可以安全地使用新值对该记录进行更新。如果不匹配,则将返回错误。您可以编写代码,实现这种形式的并发检查。同时必须编写代码来响应任何更新冲突。为了确保日期时间戳或版本号的准确性,您需要在表上设置触发器,以便在发生对行的更改时,对日期时间戳或版本号进行更新。