闩锁、锁定和并发性

 

转载自:http://blog.chinaunix.net/uid-23177306-id-2531201.html

闩锁、锁定和并发性

数据库系统本身是一个多用户并发处理系统,在同一个时间点上,可能会有多个用户同时操作数据库。这里就涉及两个很重要的问题。

这些用户之间的操作不会互相破坏。比如两个用户同时在相同的物理位置上写数据时,不能发生互相覆盖的情况。这叫串行化,也就是说,即便两个用户同时 写,也必须有先后,一个用户写完,另一个用户继续写。串行化会降低系统的并发性,但这对于保护数据结构不被破坏来说则是必需的。

在满足串行化的前提下,如何将并发性提升到最大。

在Oracle数据库中,通过闩锁(latch)和锁定(lock)来解决这两个问题。闩锁和锁定既有相同点又有不同点。相同点在于它们都是用于实 现串行化的资源。而不同点则在于闩锁是一个低级别、轻量级的锁,获得和释放的速度很快,以类似于信号灯的方式实现。而锁定则可能持续的时间很长,通过使用 队列,按照先进先出的方式实现。也可以简单地理解为闩锁是微观领域的,而锁定则是宏观领域的。

读完本章以后,我们能够了解到:

闩锁是什么,以及latch是如何保护资源的;
锁定(包括TX锁和TM锁)是如何保护资源的;
如何检测并解决锁定冲突;
DDL锁定的概念;
如何创建我们自己的锁定。

10.1 闩锁(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,也就是百万分之一秒)来计算。

latch分为两种类型:

愿意等待(Willing-To-Wait)

大部分的latch都属于这种类型。这种类型的latch都是通过Test-And-Set的方式来实现的。也就是说,如果当前进程不能获得 latch的时候,会绕着CPU旋转,而不放弃CPU。这也就是所谓的SPIN CPU,实际就是执行一段空循环,类似执行下面一段代码(其中的N由Oracle内部来控制):

loop
exit when i>= N
i := i+1;
null;
end loop;

进程之所以不释放CPU而是绕着CPU旋转,是由于latch操作本身是一个很快速的动作,因此可能等一会就能获得latch了。当进程一旦获得 CPU,但是获得不了latch时,如果这时候立刻放弃CPU,那么需要进行上下文切换,下次再次尝试获得latch时,又要进行上下文切换,可能反而要 消耗更多的时间。因此,进程在不能获得latch的时候,会执行上面这段代码,绕着CPU转一会,然后再次尝试获得latch,如果仍然不能获得,则再次 旋转CPU。当反复旋转CPU并尝试获得latch的的次数超过某个上限(该上限由隐藏参数控制)时,这时进程会释放CPU,并进入睡眠(Sleep)状 态。进程一旦进入睡眠状态,则会抛出一个对应的等待事件,并记录在视图v$session_wait里,说明当前该进程正在等待的latch的类型等信 息。初始状态下,一个进程会睡眠0.01秒。然后醒过来,并再次尝试获得latch。如果旋转CPU的次数达到上限以后,仍然不能获得latch,则再次 进入睡眠,这时会睡眠两倍的时间,依此类推,直到达到睡眠的最大值:0.2秒。

这是在数据库服务器具有多个CPU时的情形,如果只有一个CPU,就不存在旋转CPU的情况,一旦获得不了latch,就进入睡眠。

总的来说,当进程尝试获取Willing-To-Wait类型的latch时,如果失败,则进程会一直尝试对latch的获取,不断循环,直到获得 latch为止,或者是达到所指定的上限值为止。当达到上限值时,进程进入睡眠。

不等待(No-Wait)

这种类型的latch比较少,对于这种类型的latch来说,都会有很多个可用的latch。当一个进程请求其中的一个latch时,会以no- wait模式开始请求。如果所请求的latch不可用,则进程不会等待,而是立刻请求另外一个latch。只有当所有的latch都不能获得时,才会进入 等待。

从另外一个角度来说,latch分为单个latch(Solitary latch,比如shared pool latch以及redo allocation latch等)和latch组(比如library cache latch、cache buffers lru chain latch以及cache buffers chains latch等)。latch组包括父latch和子latch。单个latch和父latch都是定义在数据库软件代码里的,而且都是静态分配的。对于每 种类型的latch,只有一个父latch。而子latch则根据参数或默认值而动态设定,而且子latch的访问独立于父latch。通常来说,父 latch只用于汇总显示报表的目的。
如果latch资源被争用,通常都会表现为CPU资源使用过高。而反过来说,如果我们发现CPU资源很紧张,利用率总是在90%以上,甚至总是在 100%,其主要原因有以下几点。

SQL语句没有使用绑定变量。如果没有使用绑定变量,或者书写SQL时随意性过大,比如大小写混用等。则Oracle对每一条SQL语句都要进行解 析,也就是要非常频繁地读写shared pool里的内存块,从而导致与解析SQL相关的latch争用。

执行SQL语句时,扫描的数据块过多,或者说SQL语句写的比较低效,导致要扫描很多的数据块才能返回所要的记录。因为在查找、扫描数据块的过程 中,进程也要获得latch,直到找到数据块为止。

为何一旦latch资源发生争用,就会导致CPU繁忙呢?可以想象一下,假设某个进程(A)执行一条SQL语句需要访问10000个数据块,那么该 进程在扫描数据块的过程中,一直持有latch。而另一个进程B也要执行SQL,但是由于A持有了latch,导致B无法获得,于是旋转一会CPU,再去 获得latch,直到进入睡眠才释放CPU。接下来C进程也要执行SQL,同样的,由于A持有了latch,导致C无法获得,于是也旋转一会CPU,再去 获得latch,直到进入睡眠才释放CPU。如果类似B和C的进程很多的话,那我们会发现,CPU总是在被旋转,也就是在做空的循环,而无法做其他的事 情。因此,体现出CPU的使用率过高。

要解决latch的争用,关键在于共享SQL语句(比如使用绑定变量、规范SQL的书写等)以及优化SQL语句,使其搜索以及扫描的数据块的个数下 降到最低。

时间: 2024-10-06 14:02:50

闩锁、锁定和并发性的相关文章

SQL锁表解决并发性

在数据库开发过程中,不得不考虑并发性的问题,因为很有可能当别人正在更新表中记录时,你又从该表中读数据,那你读出来的数据有可能就不是你希望得到的数据.可以说有些数据同时只能有一个事物去更新,否则最终显示给用户的数据不是数据库中现存的数据.锁表就限制不同的事物在同一时间内不允许同时操作一张表,实例很简单,可以用select来锁定整张表,那别人就不可能更新或是读取表的记录.select * from dbo.Employee with(holdlock); with关键字来设置锁表的方式.下面是wit

【巨杉数据库SequoiaDB】巨杉 Tech | 并发性与锁机制解析与实践

01 概述 数据库是一个多用户使用的共享资源.当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性.加锁是实现数据库并发控制的一个非常重要的技术.当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁.加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作. OLTP 场景下通常要求具有很高的并发性.并发事务实际上取决于资源的使用状况,原则上应尽量减少

探索 ConcurrentHashMap 高并发性的实现机制

简介 ConcurrentHashMap 是 util.concurrent 包的重要成员.本文将结合 Java 内存模型,分析 JDK 源代码,探索 ConcurrentHashMap 高并发的具体实现机制. 由于 ConcurrentHashMap 的源代码实现依赖于 Java 内存模型,所以阅读本文需要读者了解 Java 内存模型.同时,ConcurrentHashMap 的源代码会涉及到散列算法和链表数据结构,所以,读者需要对散列算法和基于链表的数据结构有所了解. Java 内存模型 由

深入了解 Scala 并发性

2003 年,Herb Sutter 在他的文章 “The Free Lunch Is Over” 中揭露了行业中最不可告人的一个小秘密,他明确论证了处理器在速度上的发展已经走到了尽头,并且将由全新的单芯片上的并行 “内核”(虚拟 CPU)所取代.这一发现对编程社区造成了不小的冲击,因为正确创建线程安全的代码,在理论而非实践中,始终会提高高性能开发人员的身价,而让各公司难以聘用他们.看上去,仅有少数人充分理解了 Java 的线程模型.并发 API 以及 “同步” 的含义,以便能够编写同时提供安全

SQL-乐观锁,悲观锁之于并发

SQL-乐观锁,悲观锁之于并发 每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不爱说话,默默承受着编程的巨大压力,除了技术上的交流外,他们不愿意也不擅长和别人交流,更不乐意任何人走进他们的内心! 最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来.我们都知道计算机技术发展日新月异,速度惊人的快,你我稍不留神

ConcurrentHashMap高并发性的实现原理

ConcurrentHashMap是Java5中新增加的一个线程安全的Map集合,可以用来替代HashTable.HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁

SQL Server里的闩锁介绍

在今天的文章里我想谈下SQL Server使用的更高级的,轻量级的同步对象:闩锁(Latch).闩锁是SQL Server存储引擎使用轻量级同步对象,用来保护多线程访问内存内结构.文章的第1部分我会介绍SQL Server里为什么需要闩锁,在第2部分我会给你介绍各个闩锁类型,还有你如何能对它们进行故障排除. 为什么我们需要闩锁? 闩锁首次在SQL Server 7.0里引入,同时微软首次引入了行级别锁(row-level locking).对于行级别锁引入闩锁的概念是非常重要的,不然的话在内存中

shared pool 和buffer pool 详解(之二, Cache Buffers LRU Chain、Cache Buffers LRU Chain闩锁竞争与解决)

[深入解析--eygle]学习笔记 1.1.2  Cache BuffersLRU Chain闩锁竞争与解决 当用户进程需要读数据到Buffer Cache时或Cache Buffer根据LRU算法进行管理等,就不可避免的要扫描LRU  List获取可用Buffer或更改Buffer状态,我们知道,Oracle的Buffer Cache是共享内存,可以为众多并发进程并发访问,所以在搜索的过程中必须获取Latch(Latch是Oracle的一种串行锁机制,用于保护共享内存结构),锁定内存结构,防止

MySQL中的latch(闩锁)详解——易产生的问题以及原因分析

Latch 什么是latch: 锁是数据库系统区别与文件系统的一个关键特性.锁机制用于管理对共享资源的并发访问.Innodb存储引擎在行级别上对表数据上锁,这固然不错.但是Innodb也会在多个地方使用锁,从而允许多种不同资源提供并发访问.例如,操作缓冲池汇总的LRU列表,删除.添加.移动LRU列表中的元素,为了保证一致性,必须有锁的介入,这就是latch锁. latch与lock的区别 latch一般称为闩锁(轻量级别的锁),因为其要求锁定的时间必须非常短.若持续的时间长,则应用的性能会非常差