数据库的并发控制
2016-06-26 00:00
369 查看
摘要: Database基础理论
事务的定义
事务(Transaction)是构成单一逻辑工作单元的操作集合,要么完整地执行,要么完全不执行。不论发生何种情况,DBS必须保证事务能正确、完整地执行。
事务的ACID性质:
1.原子性(Atomicity)
一个事务对数据库的所有操作,是一个不可分割的工作单元。这些操作要么全部执行,要么什么也不做
保证原子性是数据库系统本身的职责,有DBMS的事务管理子系统来实现。
2.一致性(Consistency)
一个事务独立执行的结果,应保持数据库的一致性,即数据不会因事务的执行而遭受破会。
确保单个事务的一致性是编写事务的应用程序员的职责。在系统运行时,有DBMS的完整性子系统执行测试任务。
3.隔离性(Isolation)
在多个事务并发执行时,系统应保证与这些事务先后单独执行时的结果一样,此时称事务达到了隔离性的要求。也就是在多个事务并发执行时,保证执行结果是正确的,如同单用户环境一样。
隔离性是由DBMS的并发控制子系统实现的。
4.持久性(Durability)
一个事务一旦完成全部操作后,它对数据库的所有更新应永久地反映在数据库中,不会丢失。即使以后系统发生故障,也是如此。
持久性有DBMS的恢复管理子系统实现的。
并发操作带来的问题
在多用户共享系统中,许多事务可能同时对同一数据进行操作(“并发操作”),此时可能会破坏数据库的完整性。这里的”并发”(Concurrent)是指在单处理机(一个CPU)上,利用分时方法实行多个事务同时做。
DBMS的并发控制子系统,就是负责协调并发事务的执行,保证数据库的完整性,同时避免用户得到不正确的数据。
即使每个事务单独执行时时正确的,但多个事务并发执行时,如果系统不加以控制,任会破坏数据库的一致性,或者用户读了不正确的数据。数据库的并发操作通常会带来如下几个问题:丢失更新问题、读脏数据问题、不可重复读问题、幻读。
Ø 更新丢失(Lost Update)
当两个或多个事务选择同一行,然后基于最初选定的值更新该行,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题—最后的更新覆盖了由其他事务所做的更新。例如,两个编辑人员制作了同一文档的电子副本。每个编辑人员独立地更改其副本,然后保存更改后的更改。如果在一个编辑人员完成并提交事务之前,另一个编辑人员不能访问同一个文件,则可避免此问题。
Ø 脏读(Dirty Reads)
一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些”脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系,这种现象被形象地叫做”脏读”。
Ø 不可重复读(Non-Repeatable Reads)
一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变或某些记录已经被删除了!这种现象就叫做”不可重复读”。
Ø 幻读(Phantom Reads)
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为”幻读”。
1. 丢失更新问题
在表1-1中,数据库中A的初值是100,事务T1对A的值减30,事务T2对A的值增加1倍。如果执行次序是先T1后T2,那么结果A的值是140.如果是先T2后T1,那么A的值是170。这两种情况都应该是正确的。但是按表中的并发执行,结果A的值是200,这个值肯定是错误的,因为在时间t7丢失了事务T1对数据库的更新操作。因而这个并发操作是不正确的。
表1-1 在时间t7丢失了事务T1的更新
(FIND 表示从DB中读值,UPD表示把值写回到DB)
2. 读脏数据问题
这里有两种情况,用两个例子说明。
2.1 (用户读了”脏数据”,但没有破坏数据库的完整性)
在表1-2中,事务T1把A的值修改为70,但尚未提交(即未做COMMIT操作),事务T2紧跟着读未提交的A值(70)。随后,事务T1做ROLLBACK操作,把A的值恢复为100。而事务T2仍在使用被撤销了的A值70。在数据库技术中,把未提交的随后被撤销的数据称为”脏数据”。
表1-2 事务T2在时间t4读了未提交的A值
(FIND 表示从DB中读值,UPD表示把值写回到DB)
2.2 (用户读了”脏数据”,引起自身的更新操作被丢失,破坏了数据库的完整性)
在表1-2中,只是用户读了不正确的数据,而没有破坏数据库的完整性。但是表1-3的情况更糟,事务T2不仅在时间t4读了未提交的A值(70),而且实际上在时间t8还丢失了自己的更新操作。此时破坏了数据库的完整性。
表1-3 事务T2在时间t4读了未提交的A值,并在时间t8丢失了自己的更新
(FIND 表示从DB中读值,UPD表示把值写回到DB)
3. 不可重复读问题
表1-4表示T1需要两次读取同一数据项A,但是在两次读操作的间隔中,另一个事务T2改变了A的值。因此,T1在两次读同一数据项A时却读出了不同的值。
表1-4事务T1两次读取A的值,却得到了不同的结果
(FIND 表示从DB中读值,UPD表示把值写回到DB)
这些问题都需要并发控制子系统来解决。譬如对于表1-1的丢失更新问题通常采用封锁(locking)技术加以解决:
1) 在时间t2应避免事务T2执行FIND操作。因为此时事务T1已读了A值,将要执行更新。或者
2) 在时间t5应避免事务T1执行UPD操作。因为事务T2已在使用A值。
锁(Lock)是一个与数据项相关的变量,对可能应用于该数据项上的操作而言,锁描述了该数据项的状态。
通常在数据库中每个数据项都有一个锁。锁的作用是使并发事务对数据库中数据项的访问能够同步。封锁技术中主要有两种封锁:排他型封锁(Exclusive Lock)【写锁】和共享型封锁(Shard Lock)【读锁】
Ø 排他型封锁(X锁)
如果事务T对某个数据R(可以是数据项、记录、数据集乃至整个数据库)实现了X锁,那么在T对数据R解除封锁之前,不允许其他事务T再对该数据加任何类型的锁。这种锁称为”X 锁”
使用X锁技术,可以解决表1-1的丢失更新问题。如何表1-5所示。事务T1先对A实现X锁,更新A值后,在COMMIT之后,事务T2再重新执行”XFIND A”操作,并对A进行更新(此时A已是事务T1更新过的值)。这样就能得出正确的结果。
表1-5等事务T1更新完成后再执行事务T2
Ø 共享型封锁(S锁)
采用X锁的并发控制度低,只允许一个事务独锁数据。而其他申请封锁的事务只能去排队去等。为此,降低要求,允许并发的读,就引入了共享型封锁(Shared Lock),这种锁简称为S 锁,又称为读锁。
定义:如果事务T对某数据加上S锁后,仍允许其他事务再对该数据加S锁,但在对该数据的所有S锁都解除之前决不允许任何事务对该数据加X锁。
使用S锁的操作有三个:
l 申请S锁操作”SFIND R”:表示事务对数据R申请加S锁,若成功,则可以读数据R,但不可以写数据R;若不成功,那么这个事务将进入等待队列,一直到获准S锁,事务才能继续做下去。
l 升级和写操作”UPDX R”:表示事务要把对数据R的S锁升级为X锁,若成功则更新数据R,否则这个事务进入等待队列。
l 解除S锁操作”SRELEASE R”:表示事务要解除对数据R的S锁。
可以看出,获准S锁的事务只能读数据,不能更新数据,若要更新,则先要把S锁升级为X锁。另外,由于S锁只允许读数据,因此解除S锁的操作不必非要合并到事务的结束操作中去,可以随时根据需要解除S锁。
使用S锁技术,也可以解决表1-1的丢失更新问题,但有可能出现表1-6所示的那种情况。也就是S锁解决了丢失更新问题,但同时又可能会引起另外一个问题-死锁。
表1-6更新未丢失,但在时间t6发生了死锁
事务的存取模式
SQL2允许事务有两种模式:
1. READ ONLY(只读型):事务对数据库的操作只能是读操作。定义这个模式后,表示随后 的事务均是只读型。
2. READ WRITE(读写型):事务对数据库的操作可以是读操作,也可以是写操作。定义这个模式后,表示随后的事务均是读写型。在程序开始时默认这种模式。
事务的隔离级别
SQL2提供事务的四种隔离级别让用户选择。这四个级别从高到低如下所述:
1.SERIALIZABLE(可串行化):允许事务与其他事务并发执行,但系统必须保证并发调度室可串行化,不致发生错误。在程序开始时默认这个级别。
2.REPEATABLE READ(可重复读):只允许事务读已提交的数据,并且在两次读同一数据时不允许其他事务修改此数据。
3.READ COMMITTED(读提交数据):允许事务读已提交的数据,但不要求”可重复读”。例如,事务对同一记录的两次读取之间,记录可能已经被提交的事务更新。
4.READ UNCOMMITTED(可以读未提交数据):允许事务读已提交或未提交的数据。这时SQL2中所允许的最低一致性级别。
上述四种级别可以用下列SQL语句定义:
SET TRANSACTIONN ISOLATION LEVEL SERIALIZABLE
SET TRANSACTIONN ISOLATION LEVEL REPEATABLE READ
SET TRANSACTIONN ISOLATION LEVEL READ COMMITTED
SET TRANSACTIONN ISOLATION LEVEL READ UNCOMMITTED
4种隔离级别比较
事务的定义
事务(Transaction)是构成单一逻辑工作单元的操作集合,要么完整地执行,要么完全不执行。不论发生何种情况,DBS必须保证事务能正确、完整地执行。
事务的ACID性质:
1.原子性(Atomicity)
一个事务对数据库的所有操作,是一个不可分割的工作单元。这些操作要么全部执行,要么什么也不做
保证原子性是数据库系统本身的职责,有DBMS的事务管理子系统来实现。
2.一致性(Consistency)
一个事务独立执行的结果,应保持数据库的一致性,即数据不会因事务的执行而遭受破会。
确保单个事务的一致性是编写事务的应用程序员的职责。在系统运行时,有DBMS的完整性子系统执行测试任务。
3.隔离性(Isolation)
在多个事务并发执行时,系统应保证与这些事务先后单独执行时的结果一样,此时称事务达到了隔离性的要求。也就是在多个事务并发执行时,保证执行结果是正确的,如同单用户环境一样。
隔离性是由DBMS的并发控制子系统实现的。
4.持久性(Durability)
一个事务一旦完成全部操作后,它对数据库的所有更新应永久地反映在数据库中,不会丢失。即使以后系统发生故障,也是如此。
持久性有DBMS的恢复管理子系统实现的。
并发操作带来的问题
在多用户共享系统中,许多事务可能同时对同一数据进行操作(“并发操作”),此时可能会破坏数据库的完整性。这里的”并发”(Concurrent)是指在单处理机(一个CPU)上,利用分时方法实行多个事务同时做。
DBMS的并发控制子系统,就是负责协调并发事务的执行,保证数据库的完整性,同时避免用户得到不正确的数据。
即使每个事务单独执行时时正确的,但多个事务并发执行时,如果系统不加以控制,任会破坏数据库的一致性,或者用户读了不正确的数据。数据库的并发操作通常会带来如下几个问题:丢失更新问题、读脏数据问题、不可重复读问题、幻读。
Ø 更新丢失(Lost Update)
当两个或多个事务选择同一行,然后基于最初选定的值更新该行,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题—最后的更新覆盖了由其他事务所做的更新。例如,两个编辑人员制作了同一文档的电子副本。每个编辑人员独立地更改其副本,然后保存更改后的更改。如果在一个编辑人员完成并提交事务之前,另一个编辑人员不能访问同一个文件,则可避免此问题。
Ø 脏读(Dirty Reads)
一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些”脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系,这种现象被形象地叫做”脏读”。
Ø 不可重复读(Non-Repeatable Reads)
一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变或某些记录已经被删除了!这种现象就叫做”不可重复读”。
Ø 幻读(Phantom Reads)
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为”幻读”。
1. 丢失更新问题
在表1-1中,数据库中A的初值是100,事务T1对A的值减30,事务T2对A的值增加1倍。如果执行次序是先T1后T2,那么结果A的值是140.如果是先T2后T1,那么A的值是170。这两种情况都应该是正确的。但是按表中的并发执行,结果A的值是200,这个值肯定是错误的,因为在时间t7丢失了事务T1对数据库的更新操作。因而这个并发操作是不正确的。
表1-1 在时间t7丢失了事务T1的更新
(FIND 表示从DB中读值,UPD表示把值写回到DB)
时间 | 更新事务T1 | 数据库中A的值 | 更新事务T2 |
t0 | 100 | ||
t1 | FIND A | ||
t2 | FIND A | ||
t3 | A:=A-30 | ||
t4 | A:=A*2 | ||
t5 | UPD A | ||
t6 | 70 | UPD A | |
t7 | 200 |
这里有两种情况,用两个例子说明。
2.1 (用户读了”脏数据”,但没有破坏数据库的完整性)
在表1-2中,事务T1把A的值修改为70,但尚未提交(即未做COMMIT操作),事务T2紧跟着读未提交的A值(70)。随后,事务T1做ROLLBACK操作,把A的值恢复为100。而事务T2仍在使用被撤销了的A值70。在数据库技术中,把未提交的随后被撤销的数据称为”脏数据”。
表1-2 事务T2在时间t4读了未提交的A值
(FIND 表示从DB中读值,UPD表示把值写回到DB)
时间 | 更新事务T1 | 数据库中A的值 | 更新事务T2 |
t0 | 100 | ||
t1 | FIND A | ||
t2 | A:=A-30 | ||
t3 | UPD A | ||
t4 | 70 | FIND A | |
t5 | *ROLLBACK* | ||
t6 | 100 |
在表1-2中,只是用户读了不正确的数据,而没有破坏数据库的完整性。但是表1-3的情况更糟,事务T2不仅在时间t4读了未提交的A值(70),而且实际上在时间t8还丢失了自己的更新操作。此时破坏了数据库的完整性。
表1-3 事务T2在时间t4读了未提交的A值,并在时间t8丢失了自己的更新
(FIND 表示从DB中读值,UPD表示把值写回到DB)
时间 | 更新事务T1 | 数据库中A的值 | 更新事务T2 |
t0 | 100 | ||
t1 | FIND A | ||
t2 | A:=A-30 | ||
t3 | UPD A | ||
t4 | 70 | FIND A | |
t5 | A:=A*2 | ||
t6 | UPD A | ||
t7 | 140 | ||
t8 | *ROLLBACK* | ||
t9 | 100 |
表1-4表示T1需要两次读取同一数据项A,但是在两次读操作的间隔中,另一个事务T2改变了A的值。因此,T1在两次读同一数据项A时却读出了不同的值。
表1-4事务T1两次读取A的值,却得到了不同的结果
(FIND 表示从DB中读值,UPD表示把值写回到DB)
时间 | 更新事务T1 | 数据库中A的值 | 更新事务T2 |
t0 | 100 | ||
t1 | FIND A | ||
t2 | XFIND A | ||
t3 | A:=A*2 | ||
t4 | UPD A | ||
t5 | 200 | COMMIT | |
t6 | FIND A |
1) 在时间t2应避免事务T2执行FIND操作。因为此时事务T1已读了A值,将要执行更新。或者
2) 在时间t5应避免事务T1执行UPD操作。因为事务T2已在使用A值。
锁(Lock)是一个与数据项相关的变量,对可能应用于该数据项上的操作而言,锁描述了该数据项的状态。
通常在数据库中每个数据项都有一个锁。锁的作用是使并发事务对数据库中数据项的访问能够同步。封锁技术中主要有两种封锁:排他型封锁(Exclusive Lock)【写锁】和共享型封锁(Shard Lock)【读锁】
Ø 排他型封锁(X锁)
如果事务T对某个数据R(可以是数据项、记录、数据集乃至整个数据库)实现了X锁,那么在T对数据R解除封锁之前,不允许其他事务T再对该数据加任何类型的锁。这种锁称为”X 锁”
使用X锁技术,可以解决表1-1的丢失更新问题。如何表1-5所示。事务T1先对A实现X锁,更新A值后,在COMMIT之后,事务T2再重新执行”XFIND A”操作,并对A进行更新(此时A已是事务T1更新过的值)。这样就能得出正确的结果。
表1-5等事务T1更新完成后再执行事务T2
时间 | 更新事务T1 | 数据库中A的值 | 更新事务T2 |
t0 | 100 | ||
t1 | XFIND A | ||
t2 | XFIND A(失败) | ||
wait(等待) | |||
t3 | A:=A-30 | wait | |
t4 | wait | ||
t5 | UPD A | wait | |
t6 | 70 | wait | |
t7 | COMMIT(包括解锁) | wait | |
t8 | XFIND A(重做) | ||
t9 | A:=A*2 | ||
t10 | UPD A | ||
t11 | 140 | COMMIT(包括解锁) |
采用X锁的并发控制度低,只允许一个事务独锁数据。而其他申请封锁的事务只能去排队去等。为此,降低要求,允许并发的读,就引入了共享型封锁(Shared Lock),这种锁简称为S 锁,又称为读锁。
定义:如果事务T对某数据加上S锁后,仍允许其他事务再对该数据加S锁,但在对该数据的所有S锁都解除之前决不允许任何事务对该数据加X锁。
使用S锁的操作有三个:
l 申请S锁操作”SFIND R”:表示事务对数据R申请加S锁,若成功,则可以读数据R,但不可以写数据R;若不成功,那么这个事务将进入等待队列,一直到获准S锁,事务才能继续做下去。
l 升级和写操作”UPDX R”:表示事务要把对数据R的S锁升级为X锁,若成功则更新数据R,否则这个事务进入等待队列。
l 解除S锁操作”SRELEASE R”:表示事务要解除对数据R的S锁。
可以看出,获准S锁的事务只能读数据,不能更新数据,若要更新,则先要把S锁升级为X锁。另外,由于S锁只允许读数据,因此解除S锁的操作不必非要合并到事务的结束操作中去,可以随时根据需要解除S锁。
使用S锁技术,也可以解决表1-1的丢失更新问题,但有可能出现表1-6所示的那种情况。也就是S锁解决了丢失更新问题,但同时又可能会引起另外一个问题-死锁。
表1-6更新未丢失,但在时间t6发生了死锁
时间 | 更新事务T1 | 数据库中A的值 | 更新事务T2 |
t0 | 100 | ||
t1 | SFIND A | ||
t2 | SFIND A | ||
t3 | A:=A-30 | ||
t4 | A:=A*2 | ||
t5 | UPDX A(失败) | wait | |
t6 | wait | UPDX A(失败) | |
t7 | wait | wait | |
t8 | wait | wait |
SQL2允许事务有两种模式:
1. READ ONLY(只读型):事务对数据库的操作只能是读操作。定义这个模式后,表示随后 的事务均是只读型。
2. READ WRITE(读写型):事务对数据库的操作可以是读操作,也可以是写操作。定义这个模式后,表示随后的事务均是读写型。在程序开始时默认这种模式。
事务的隔离级别
SQL2提供事务的四种隔离级别让用户选择。这四个级别从高到低如下所述:
1.SERIALIZABLE(可串行化):允许事务与其他事务并发执行,但系统必须保证并发调度室可串行化,不致发生错误。在程序开始时默认这个级别。
2.REPEATABLE READ(可重复读):只允许事务读已提交的数据,并且在两次读同一数据时不允许其他事务修改此数据。
3.READ COMMITTED(读提交数据):允许事务读已提交的数据,但不要求”可重复读”。例如,事务对同一记录的两次读取之间,记录可能已经被提交的事务更新。
4.READ UNCOMMITTED(可以读未提交数据):允许事务读已提交或未提交的数据。这时SQL2中所允许的最低一致性级别。
上述四种级别可以用下列SQL语句定义:
SET TRANSACTIONN ISOLATION LEVEL SERIALIZABLE
SET TRANSACTIONN ISOLATION LEVEL REPEATABLE READ
SET TRANSACTIONN ISOLATION LEVEL READ COMMITTED
SET TRANSACTIONN ISOLATION LEVEL READ UNCOMMITTED
4种隔离级别比较
读数据一致性及允许的并发副作用 隔离级别 | 读数据一致性 | 脏读 | 不可重复读 | 幻读 |
未提交读 | 最低级别,只能保证不读取物理上损坏的数据 | 是 | 是 | 是 |
已提交读 | 语句级 | 否 | 是 | 是 |
可重复读 | 事务级 | 否 | 否 | 是 |
可序列化 | 最高级别,事务级 | 否 | 否 | 否 |
相关文章推荐
- oracle 查询表的索引
- redis命令一览
- B+树|MYSQL索引使用原则
- Greenplum的Oracle兼容性函数
- 数据库查询优化——Mysql索引
- navicat for mysql 10.1注册码
- 一个由库表到POJO的myBatis配置文件建立的例子
- mysql AB复制
- PDO的三种数据库连接方式
- springmvc+mybatis+redis(转)
- object_PDO基础连接数据库代码
- centos7安装mariadb
- excel数据表存入mysql数据库
- Redis学习笔记(1)-Redis安装与其他
- Redis学习笔记(1)-Redis安装与其他
- Redis学习笔记(1)-Redis安装与其他
- Redis学习笔记(1)-Redis安装与其他
- Redis、Memcached、Guava、Ehcache中的算法
- 数据库update语句中where后跟IN和EXISTS效率测试
- 使用MongoDB的开源项目(转)