二. Oracle数据库的锁机制

本文深入解析数据库中的锁机制,包括悲观锁、乐观锁、排他锁、共享锁等概念,探讨其在事务处理中的作用及在OLTP和OLAP场景下的应用。同时,介绍了Oracle数据库的锁机制特点和常见问题解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       以前在学数据库的时候,总是看到“悲观锁”,“乐观锁”,“排他锁”,“共享锁”,"读锁",“写锁”,“S锁”,“死锁”,“行锁”,“表锁”等名词,还有“不可重复读”,“读脏数据”等等。这次有时间可以记录下来,各种锁的含义已经相应的应用。

       在介绍锁之前,先了解一下事务。Oracle的OLTP模式尤其注重"事务"。什么是事务呢?官方解释 事务是恢复和并发控制的基本单位。简单点讲就是指几条SQL在一块跑满足下面4个特性(ACID),就可以称为事务了:

       1. 原子性(Automicity),就是指这几条SQL要么一块执行成功,commit,要么一块执行失败,rollback。不存在执行成功一些,失败一些。

       2.一致性(Consistency),官方解释:事务必须使数据库从一个一致性状态变换到另一个一致性状态。说实话,刚开始学的时候看到这个解释是懵逼的,后来才慢慢了解了。这个特性从SQL层面不好解释,毕竟我们只在乎SQL执行成功还是失败,以及失败原因。一致性可以更好的从SQL操作的业务逻辑上面来解释,比如这几条SQL是负责转账的,A和B一共有5000元,那么不管A和B相互之间怎么转账,总和必须是5000元。这样才符合业务逻辑,所以个人的见解,可以理解成为需要符合业务逻辑上的一致性。

      3.隔离性(Isolation),这个是针对并发的情况,就是指在多人并发的运行这几条SQL的情况下,不能相互有影响。

      4.持久性(Durability), 就是说这几条SQL一旦执行成功,对数据库的数据的改变是永久性的。

      解释完上面的事务的特性,就可以解释锁的用处了。对的,锁的用处就是为了实现事务的这几条特性的。下面对常见的锁做一下分类,方便我们记忆:

     1. “悲观锁”和“乐观锁”,这个是针对Web/后台开发的,其实就是一种概念。“悲观锁”:开发者悲观的认为,一个事务在读表数据时,肯定有别的事务正在修改要读的数据。所以开发者直接使用数据库提供的锁机制 for update来确保事务的一致性和隔离性。“乐观锁”:开发者乐观的认为,一个事务在读表数据时,应该没有别的事务在做修改。所有开发者不使用数据库提供的锁机制,只是使用时间戳(Timestamp)或者版本控制做验证。

     2.“排他锁”,“共享锁”,“行锁”,“表锁”,这些锁才是真正用作用在数据库上的。从约束的范围来看有: “行锁”,“表锁”。从约束的类型上来看有:“共享锁”,“排他锁”。

     共享锁(Share Lock):也叫被称为读锁,读共享锁,S锁,根据作用的范围,又有行共享锁,其实都是一个意思。是指一个事务在读数据的时候,会给数据加上一个共享锁,这样子,在读数据的期间,别的事务也只能加共享锁,实现并发读,但不允许做修改操作。在这段数据上面的修改操作要等到所有的读操作执行完成了才能进行。

    排他锁(Exclusive Lock):也叫被称为写锁,写排他锁,X锁,根据作用的范围,又有行排他锁,其实都是一个意思。是指一个事务在写数据的时候,会给数据加上一个排他锁,这样子,在写数据的期间,别的事务既不能读这段数据,也不能写这段数据,只能等待,直到这个事务结束,释放排他锁为止。

    行锁(Row Lock):就是指作用范围是一行或者多行,是一个概念。真正作用到数据库中的就是行共享锁,行排他锁。

    表锁(Table Lock):就是指作用范围是整个表,是一个概念。真正作用到数据库中的就是共享锁,排他锁。

    上面大概解释了这些锁的分类,和含义。Oracle是支持事务的,我们可以轻松的利用Oracle数据库本身的锁机制就能实现事务,但是用Oracle自身的锁机制去实现事务有一个很大的弊端,就是资源消耗大,不适合高并发。当然还要注意一下"死锁":一般是由于资源的争用引起来的,更确切的来说也不叫争用,更像一种资源分配上面的问题,导致多个事务进入无限循环的相互等待中。接下来来讨论一下不同场景的一些应用:

    1. OLTP模式下的,这个模式通常用于高可用的在线系统,它非常的注重事务,同时也要兼顾高并发,所以大部分时候,用悲观锁能很好的解决事务的问题(用数据库本身的锁机制,在事务开始的时候锁住需要的资源,直到事务结束),但不适用高并发。所以大部分时候,这些事务会采用乐观锁,也就是不用锁,用数据版本或者时间戳来控制,兼顾事务和高并发。更深入的了解乐观锁的话,可以试着去理解下面这个核心SQL就行了:

update table set x=x+1, version=version+1 where id=#{id} and version=#{version};

    2.OLAP模式下的,主要用于数据计算汇总和数据分析,注重的是高并行。(我这边说的并行简单将就是指将一段庞大的数据分成很多块同时处理,并发是指多个用户同时访问同一段数据。) 再加上数据仓库的设计,事务和并发没有那么重要,最多也就是并发读会多一点。所以就使用Oracle自身的锁机制就可以了。

   介绍完两种场景的应用,这边再记录一下平常用到的一些问题:

   1. 我们平时用的select * from table_a,是不是会造成S锁 ? 不会,因为现在用的传统关系型数据库都会采用快照读(Snap Read),也就是说你select并不是本地读,而是从快照表里面读出来的。做dml操作的时候,才会做一次本地读,完成后会更新快照。所以在你执行一个很长的select语句时,如果期间有人更改了源表,你的SQL有可能会报错"snap is too old"。

   2. 在OLAP应用中,每天有大量的数据需要入库,Oracle的写操作会造成X锁,是不是只能使用串行写? 不是的,我们知道串行写insert 使用 append + no logging 会极大的提高插入的速度,但是同样会浪费存储空间。这个时候,Oracle使用了数据库缓存(buffer cache)允许我们并行的写入数据缓存中,最后再写入表中。用的时候需要用下面的语句先开启:alter session enable parallel dml ,用完了需要关闭:alter session disable parallel dml

  3. 在PL/SQL中,在当前页,我对表A新插入了一条记录id = 10,还没有commit。如果你在当前页select * from A, 能看到id = 10的记录。但是如果你再新开一个窗口,select * from A,是没法看到id = 10的记录了。为什么? 这个就涉及到数据库的事务隔离级别了。从事务的角度,你在当前页,在没有commit前都属于同一个事务,在同一个事务中是能够看到未提交的数据的。如果你新开了一个窗口,Oracle就认为你开始了一个新的事务,这个新的事务是无法看到未提交的数据的。Oracel的事务隔离级别有两种:读已提交(Read committed)可避免脏读的发生,串行化(Serializable)可避免脏读、不可重复读,幻读的发生。Oracle默认的事务隔离级别是读已提交,也就是说,不同事务之间能读到的数据都是已经成功提交的数据了。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值