数据库系统本身是一个多用户并发处理系统,在同一个时间点上,可能会有多个用户同时操作数据 库。这里就涉及两个很重要的问题。
这些用户之间的操作不会互相破坏。比如两个用户同时在相同的物理位置上写数据时,不能发生互 相覆盖的情况。这叫串行化,也就是说,即便两个用户同时写,也必须有先后,一个用户写完,另一个 用户继续写。串行化会降低系统的并发性,但这对于保护数据结构不被破坏来说则是必需的。
在满足串行化的前提下,如何将并发性提升到最大。
在Oracle数据库中,通过闩锁(latch)和锁定(lock)来解决这两个问题。闩锁和锁定既有相同点 又有不同点。相同点在于它们都是用于实现串行化的资源。而不同点则在于闩锁是一个低级别、轻量级 的锁,获得和释放的速度很快,以类似于信号灯的方式实现。而锁定则可能持续的时间很长,通过使用 队列,按照先进先出的方式实现。也可以简单地理解为闩锁是微观领域的,而锁定则是宏观领域的。
读完本章以后,我们能够了解到:
闩锁是什么,以及latch是如何保护资源的;
锁定(包括TX锁和TM锁)是如何保护资源的;
如何检测并解决锁定冲突;
DDL锁定的概念;
如何创建我们自己的锁定。
闩锁(latch)概述
Oracle数据库使用闩锁来管理内存的分配和释放。假设,某个用户进程(假设其为A)发出一条 update语句,要去更新58号数据块里的某条记录。则该用户进程对应的服务器进程在写内存的时候,找 到58号数据块,并往里写内容。A在写58号数据块的过程中,这时,另一个用户进程B发出insert语句, 要将某个新的记录插入到58号数据块里。如果没有一定的保护机制,A正要写入的空间可能会被B抢先写 入,或者相反,B正要写入的空间也可能会被A抢先写入。不管哪个用户先抢先写入,造成的结果就是, 58号数据块里的数据都混乱了,因为这时,A和B之间的数据互相交织在一起了。
因此,必须使用latch对此进行保护。简单来说,任何进程要写数据块时,都必须先获得latch,在 写入过程中,一直持有该latch,写完以后,释放该latch。对于上面的例子来说,当A在写入58号数据 块时,先获得latch,然后开始写。而当A正在写入的过程中,B也要写58号数据块。这时B在尝试获得 latch时,发现该latch正被其他用户(也就是A)持有,因此B进入等待状态。直到A写完数据块并释放 latch以后,B才能获得latch,获得latch以后,才能在58号数据块里写入数据。
这里只是以写数据块为例来说明为何要使用latch。而事实上,latch不仅仅用于写数据块,比如对 于shared pool来说,其内存单位就不是数据块了。latch也不仅仅用于写操作,只要涉及内存地址的读 和写,都需要通过获得latch来实现串行化,一次只能有一个服务器进程在读或者写内存地址。
Oracle在实例管理中,不管是buffer cache、shared pool还是log buffer,都引入了各种各样的 latch。
实现latch时,实际是由操作系统的旗语(semaphore:也叫信号量)来完成的。为了便于理解,可 以把它们想象为,通过某个变量值的变化而实现的。变量值为0则说明latch当前没有被其他进程获取, 否则如果为非0值,则说明它已经被其他进程所获取了。Oracle在设计latch的时候将其定义为轻量级锁 ,因此它的操作非常快,以微秒(microsecond,也就是百万分之一秒)来计算。