并发访问带来的问题
丢失更新:一个事务修改某行数据时,另一个事务同时修改了该行数据,使第一个事务对数据的修改丢失。
脏读:一个事务读取了另一个事务未提交的数据。
不可重复读:一个事务再次读取之前曾读取过的数据时,发现该数据已经被另一个已提交的事务修改。
幻读:一个事务根据相同的查询条件,重新执行查询,返回的记录中包含与前一次执行查询返回的记录不同的行。
锁基本概念
锁:用来共享资源控制并发访问的一种机制。
①、锁由Oracle自动管理,锁持续的时间等于被提交事务处理的时间。
②、单用户数据库不需要锁机制。
Oracle锁的分类:
▲锁的类型:
①、共享锁(share locks)也称读锁,s锁
②、排它锁(exclusive locks)也称写锁,x锁
▲按锁保护的内容分类
①、DML锁(DML lock):用来保护数据的完整性和一致性。
TX锁(事务锁或行级锁)
- 事务发起第一个修改数据的语句时会自动得到TX锁,而且会一直持有这个锁,直到事务执行提交或回滚。TX锁用作一种排队机制,使得其他会话可以等待这个事务执行。事务中修改或通过悲观锁定选择的每一行都会指向该事务的一个相关TX锁。
TM锁(表级锁)
- TM锁用于确保在修改表的内容时,表的结构不会改变当一个会话开始更新一个表时,会自动获得这个表的TM锁,这样能够防止另一个会话在该表上执行DROP或者ALTER语句删除该表或更改该表的结构。
②、DDL锁(DDL lock):用来保护数据对象结构定义。
- DDL操作会自动为数据库对象加DDL锁。
注意:在Oracle中,DDL语句包装在隐式提交(回滚)中来执行操作。
DDL语句执行伪代码
③、内部锁和闩(internal locks and latchs):用来保护数据库内部数据结构。
- 内部锁有Oracle来管理,以保护内部数据库的结构。
- 闩就是一种锁,设计为只保持极短的时间,用于保护某些内存结构。
-
▲锁的机制问题
○死锁:两个事务(会话)都进入了彼此等候对方锁定的资源时的一种停止状态。
①、会话1
UPDATE A SET X=X+1; UPDATE B SETX=X+1;
②、会话2 语句
UPDATE B SET X=X+1; UPDATE A SET X=X+1;
解决死锁:“牺牲”一个会话,回滚一个会话的事务,是另一个会话事务继续执行。
在发生死锁时Oracle数据库会在服务器上创建一个跟踪文件记录死锁。
注意:不要在开发过程中人为的“提供条件”使Oracle产生死锁。
○阻塞:如果一个会话持有某个资源的锁,而另一个会话在请求这个资源,就造成了阻塞。
【阻塞是可以避免的,最常出现在交互式应用中,这些应用程序会在数据库中读取一些数据,并且让用户在屏幕上修改这些数据。但对于多用户系统来说,此时另一个用户可能正在修改这些数据,他已经锁定了这些数据,要修改数据的用户就处于阻塞状态。如果
在交互应用中存在阻塞,说明可能同时存在丢失更新。
如果真的发现会话在一个交互应用中被阻塞,就说明很有可能同时存在另一个BUG,丢失更新】
○悲观锁定:是指在读取数据后马上锁定相关资源。
语法:select … for update[of column_list] [waitn|nowait]
- Of 子句用于指定即将更新的列,即锁定行上的特定列。
- Wait子句指定等待其他用户释放锁的秒数,防止无限期的等待,nowait表示不等待。
示例:
○乐观锁定:把所有锁定都延迟到即将执行更新之前
语法:
UPDATE Table_Name SET column_name1=NewValue1,column_name2=NewValue2…
WHERE column_name1=oldvalue1 and column_name2=oldvalue2 …