共享内存缓冲池

  通过本文的学习,您将能够:

  • 列出服务器为更新磁盘上的行而执行的步骤
  • 描述检查点进程
  • 描述阻塞检查点与非阻塞检查点之间的差别
  • 正确设置与检查点相关的配置参数
  • 描述引起检查点发生的事件
  • 解释自动检查点的好处
  • 监测检查点与服务器执行的缓冲写入

1. 对共享内存的访问

  线程通过使用下面机制来协调对共享内存缓冲区的访问:

  • 锁,在SinoDB数据库服务器中称作互斥锁 mutexes

  • 一系列队列,称为 LRU 队列

  • 共享内存缓冲锁
    — 共享锁
    — 互斥锁

1.1 锁 mutexes

  允许并发访问共享内存资源的系统需要一些方法来对访问进行同步且避免崩溃。服务器使用共享内存锁中的一类 —互斥锁 mutexes ), 来协调在共享内存中修改数据的线程。每个可以更改的共享内存资源均有一个关联的互斥锁。互斥锁是由服务器在其内部实现的,而非在操作系统中实现。

  Mutex是一种机制,其名称来源于术语 MUTual EXclusion。线程必须在一个共享内存资源被更改前获得该资源相关联的互斥锁。一旦线程完成其工作,线程就会释放该mutex。如果与一个共享内存资源关联的互斥锁不可用(因为它属于另一个线程),则该线程必须等待那个互斥锁被释放。

1.2 LRU队列

  LRU 队列包括了所有共享内存缓冲区的条目。在将一个新页调入共享内存之前,必须在一个 LRU 队列中找到一个可用的条目。如果缓冲池中一个缓冲区包含一个数据页,但数据未被修改,则在LRU 队列中的相应条目仍然被视为空闲或可用。如果缓冲区中包含自上一个检查点之后已修改的数据,其条目被视为已被修改或不可用。
  如果 LRU 队列中没有空闲的条目(自上一个检查点以来已修改过所有缓冲区),则修改后的缓冲区会被刷新磁盘上,且其 LRU 队列中的条目置为可用。

1.3 共享内存缓冲锁

  服务器使用两种类型的锁来管理对共享内存缓冲区的访问:共享锁(Share locks) 与*互斥(Exclusive locks) *。 这些锁在执行期间强制实施所需级别的线程隔离。共享锁允许多个线程从缓冲区读取数据,但都不可以更改缓冲区。互斥锁只允许由一个线程访问并更新缓冲区。当此执行线程准备释放互斥锁时,会唤醒等待队列中的下一个线程。

2. 将页面读入共享内存

  一个线程要访问共享内存缓冲池中的一个数据行时所一般步骤如下:

   ① 首先,必须根据 rowid 定位行所在的物理页。如果 rowid 未知,则执行索引页或数据页的搜索。

  ② 线程尝试在共享内存缓冲池中查找那页。如果找到该页,则线程试着获取位于共享内存的缓冲区表中的缓冲区标头条目上的mutex。线程检查该缓冲区的当前锁访问级别。如果是兼容级别,请求线程获得对该缓冲区的访问权限,并设置自己的锁。

  ③ 如果共享内存中未找到那页,则定位指向最近最少使用的 FLRU 队列的指针。将该指针暂时从 FLRU 队列中删除,并将那页从磁盘读入缓冲区。

  ④ 如果此缓冲区未被修改,则线程释放缓冲区锁、检查是否有等待缓冲区的其他线程。如果有,就唤醒等待队列中的第一个线程。如果没有线程等待缓冲区,线程将该缓冲区指针返回到最近最常使用 FLRU 队列。

  ⑤ 如果缓冲区已被修改,则将该页前映像的一个副本写入到物理日志缓冲区中。修改缓冲区中的数据行并且将此事务信息写入逻辑日志缓冲区。

  ⑥ 线程释放与所修改的缓冲区关联的锁,并检查是否有其他线程正在等待缓冲区。如果有,则唤醒等待队列中的第一个线程。如果没有线程等待缓冲区,该线程将已修改缓冲区的指针返回到最近最常使用 MLRU 队列。

  ⑦ 此时所发生的修改仅在内存而非磁盘。系统*检查点(checkpoint) 负责将共享内存缓冲区(缓冲池、物理日志缓冲区、逻辑日志缓冲区)与磁盘上页进行同步。在检查点发生期间,会将更新的页写入磁盘。

3. 检查点(checkpoint)

  检查点 是一种主要的服务器操作,指数据库服务器操作中磁盘上的页与共享内存缓冲池中的页同步时的点,其中数据在共享内存缓冲区与磁盘上的数据完全或部分同步。有以下两类检查点:

  • 阻塞检查点(Blocking checkpoint) —— 缓冲区页刷新到磁盘时数据库服务器被阻塞
  • 非阻塞检查点(Nonblocking checkpoint) —— 页面刷新期间服务器未被阻止

  数据库服务器为逻辑日志空间的每个跨度至少生成一个检查点,以保证它有一个开始快速恢复的检查点。

3.1 检查点历史

$ onstat -g ckp

Sinoregal SinoDB Dynamic Server Version 12.10.FC8 -- On-Line -- Up 19 days 16:28:55 -- 2765720 Kbytes

AUTO_CKPTS=On   RTO_SERVER_RESTART=Off   

                                                                    Critical Sections                          Physical Log    Logical Log    
           Clock                                  Total Flush Block #      Ckpt  Wait  Long  # Dirty   Dskflu  Total    Avg    Total    Avg   
Interval   Time      Trigger    LSN               Time  Time  Time  Waits  Time  Time  Time  Buffers   /Sec    Pages    /Sec   Pages    /Sec  

70876      09:39:52  CKPTINTVL  155:0x621f018     0.0   0.0   0.0   0      0.0   0.0   0.0   1         1       7        0      2        0     
70877      09:44:52  CKPTINTVL  155:0x627c5b8     0.0   0.0   0.0   0      0.0   0.0   0.0   658       658     620      2      93       0     
70878      09:54:52  CKPTINTVL  155:0x627e018     0.0   0.0   0.0   0      0.0   0.0   0.0   1         1       21       0      2        0     
70879      10:01:26 *User       155:0x6280018     0.0   0.0   0.0   1      0.0   0.0   0.0   0         0       0        0      2        0     
70880      10:01:37 *Backup     155:0x6282018     0.0   0.0   0.0   0      0.0   0.0   0.0   3         3       0        0      2        0     
70881      10:01:50 *Backup     155:0x6289018     0.0   0.0   0.0   0      0.0   0.0   0.0   34        34      28       2      7        0     

Max Plog       Max Llog       Max Dskflush   Avg Dskflush   Avg Dirty      Blocked      
pages/sec      pages/sec      Time           pages/sec      pages/sec      Time         
10240          5365           0              1              0              0            

  要监视检查点活动,请运行 onstat -g ckp 命令。它提供了有关系统上最近发生的检查点的信息。它还提供了一些基于最近的检查点活动计算的统计信息。
  “Trigger”列指示触发检查点的事件。阻止检查点用星号表示。
  onstat 实用程序从sysmaster数据库中的两个表中获取检查点信息。syscheckpoint 表包含最近 20 个检查点的相关信息,sysckptinfo 表包含自动检查点的相关信息。

3.2 什么触发了检查点?

  • 触发阻塞检查点:
    – 执行管理任务时,例如添加数据库空间
    – 执行数据库空间备份时
    – 当 onmode -c 手动请求检查点时

  • 触发非阻塞检查点:
    – 当资源受限时,例如当物理日志已满 75% 时
    – 当 CKPTINTVL 参数指示的时间间隔已到
    – 服务器启动时

  在检查点中,数据库服务器将共享内存缓冲池中所有已修改的页刷新到磁盘。当检查点完成时,所有物理操作都已完成,MLRU 队列为空,并且数据库服务器被视为物理上一致。

  尽管检查点由系统通过各种事件自动启动,但在某些情况下,应考虑手动强制检查点:

  • 释放包含最新检查点记录且已备份但尚未释放(onstat -l 显示状态为 U-B-L 或 U-B)的逻辑日志文件。
  • 发出 onmode -sy 将数据库服务器置于静止模式时。
  • 在完成一个大型索引的建立后,如果数据库服务器在下一个检查点之前终止,则下一次数据库服务器初始化时将重建该索引。
  • 在尝试任何可能中断数据库服务器的系统操作之前。如果检查点长时间未发生,则快速恢复可能比正常情况下用时更长。

3.3 在阻塞检查点期间会发生什么?

  ① 不允许线程进入关键区
  ② 物理日志缓冲区(Physical log buffers)刷新到磁盘
  ③ 修改后的缓冲区(buffer pages)页刷新到磁盘
  ④ 将检查点记录写入保留页面
  ⑤ 逻辑日志缓冲区(Logical log buffer)刷新到磁盘
  ⑥ 物理日志在逻辑上清空

3.3.1 步骤1:禁止用户线程进入关键区

  当服务器发出检查点的请求时,用户线程会被阻止进入代码的关键区。 当有检查点请求时,处于关键区内的用户线程被允许执行完成,直到关键区结束。

  关键区由作为一个单元必须执行或者完全不执行的磁盘修改组成。指定关键区以确保物理一致性。关键区中的用户线程持有用于执行数据修改所需的共享内存资源。在检查点期间,这些资源必须对检查点进程是可用,以实现磁盘与共享内存缓冲区的同步。

3.3.2 步骤2:将物理日志缓冲区刷新至磁盘

  一旦用户线程不再位于代码的关键部分,检查点活动将继续。页清理线程会将共享内存中的物理日志缓冲区刷新到磁盘上的物理日志中。

3.3.3 步骤3:将修改后的缓冲区刷新至磁盘

  在此步骤,页清除线程将共享内存缓冲池中所有修改的页都刷新到磁盘上。这个刷新过程叫做chunk write,本文稍后将做说明。

3.3.4 步骤4:写入检查点记录

  页清除程序线程将* checkpoint-complete* 记录写入到逻辑日志缓冲区。这为系统出现故障时提供了一个恢复点。此外,页清除程序线程会更新跟踪检查点活动(PAGE_1CKPT 与 PAGE_2CKPT)的系统保留页,并且将* checkpoint-complete* 记录写入到消息日志文件。

3.3.5 步骤5:将逻辑日志缓冲区刷新至磁盘

  在此步骤,将逻辑日志缓冲区刷新到磁盘上的当前逻辑日志文件中。

3.3.6 步骤6:逻辑上清空物理日志

  在* checkpoint-complete* 记录写入之后,磁盘上的物理日志逻辑上 被清空。物理日志的内容并不会被删除,因为这是一项代价高昂的操作。而是将物理日志中的当前位置标记为逻辑上的开始位置,并且从该点开始将新条目写入日志。

  如果到达物理日志的结尾处,新页将会写入到该日志物理上的开始位置。可以将物理日志视作一个循环文件,不存在开始或者结束。因此,物理日志的逻辑起点会随检查点的发生而改变。

4. 非阻塞检查点的优点

  • 在检查点处理磁盘刷新期间,事务处理不会被挂起

  • 允许放缓LRU 刷新
    – 具有非常频繁的 LRU 刷新的系统在放缓LRU 刷新后可能会体验到性能的显著提高
    – 自动调节LRUs

  将共享内存缓冲区刷新到磁盘时,事务处理不再被挂起。此外,SinoDB动态服务器现在支持 LRU 的自动调整。因此,不再需要主动调整 LRU 缓冲区和阈值。

4.1 非阻塞检查点是如何工作的?

  非阻塞检查点和阻塞检查点之间的主要区别在于,在将共享内存缓冲区刷新到磁盘期间,事务活动不会被阻塞。相反恢复信息在检查点开始时存储在内存中,然后在检查点操作的最后步骤中写入磁盘。如果在检查点期间服务器中断,则服务器只是从上次完成的检查点恢复。

4.2 非阻塞检查点建议

  • 将BUFFERPOOLS配置参数中的 lru_min_dirtylru_max_dirty增加到至少 60 和70

  • 确保物理日志足够大
    – 您现在可以在服务器联机时移动物理日志并调整其大小
    – 物理日志可以大于 2GB

  • 确保逻辑日志空间很大

  • 在进行以上参数配置时,使用 onstat –g ckp 命令进行资源指导

5. 什么时候检查点阻塞?

  • 当检查点耗尽日志资源时
    – 当物理日志已满 75% 时仍会触发
    – 每个日志空间仍然需要 1 个检查点

  • 在检查点处理期间,交易继续消耗物理和逻辑日志空间
    – 在检查点处理期间刷新缓冲区所需的时间越长,需要更多的逻辑和物理日志空间

  • 如果日志资源变得严重不足,事务将阻塞以让检查点完成

5.1 修复阻塞的检查点

  • 增加物理或逻辑日志大小
    – 服务器将建议该增加哪个资源以及大小

  • 增加 LRU 刷新

  • 使用自动检查点功能
    – 服务器会自动触发更频繁的检查点,以防止由于缺乏资源而发生阻塞

6. 自动检查点

  • 根据以前的使用模式以及物理和逻辑日志的当前状态,服务器预测何时必须发生检查点,并自动请求检查点以避免服务器阻塞。

  • 帮助自动检查点:
    – 增加物理日志大小
    – 增加逻辑日志大小
    – 增加 LRU 刷新次数(使用自动 LRU 调试)

  当资源不足时,服务器将提出建议。请监视online.log文件并运行onstat –g ckp。

6.1 自动检查点设置

  • onconfig配置参数:AUTO_CKPTS
    – 设置为 0 表示禁用
    – 设置为 1 以启用
    – 默认设置为 1

  • 动态启用或关闭
    onmode –wm AUTO_CKPTS=0|1

7.配置LRUs : BUFFERPOOL参数

BUFFERPOOL size=page_size,lrus=num_lrus,buffers=num_buffers,
lru_min_dirty=min_dirty_pages,lru_max_dirty=max_dirty_pages

– page_size = page size,以K为单位
– num_lrus = LRU 队列数
– num_buffers = 缓冲区数量,其中每个缓冲区的大小为page_size
– min_dirty_pages = 当缓冲池中脏页数达到缓冲池总页数的此百分比时,页面刷新将停止
– max_dirty_pages = 当缓冲池中脏页数达到缓冲池总页数的此百分比时,开始刷新页面

7.1 LRU队列活动

  共享内存缓冲池是通过使用几个指向缓冲区表的指针链接列表来管理的。LRU 配置参数决定了服务器的 LRU 队列的数目。可以配置三个或更多(取决于平台) LRU 队列对。每一对均包含一个干净的队列(空闲 LRU 或 FLRU)与一个脏队列(修改过的 LRU 或 MLRU)。

LRU_MIN_DIRTY LRU_MAX_ DIRTY

  配置参数 LRU_MIN_DIRTY 与 LRU_MAX_DIRTY 决定了 MLRU 队列缓冲区刷新至磁盘的频率。

  LRU_MAX_DIRTY 定义了页清除线程活动开始前,系统允许的LRU 队列对中脏页的最大百分比。当队列中于缓冲区总数相关的已修改缓冲区的数量超过 LRU_MAX_DIRTY 设置的百分比时,页清除程序线程就开始在其下一个活动周期(至少每60秒发生一次)将页写入磁盘。

  LRU_MIN_DIRTY 定义了页清除线程活动完成后允许保留修改的 LRU 队列对所占的百分比。一旦已修改缓冲区的数量低于 LRU_MIN_DIRTY 设置的百分比,则页清除线程停止活动。

  在处理海量数据的数据库服务器中,当LRU_MAX_DIRTY 和 LRU_MIN_DIRTY有单一的百分点时,待处理的页数可以有非常大的差别。此时,可以将这些参数设置为十进制值,例如 .5 或甚至是 .05。

自动LRU调整

  您可能会发现,有时系统上的活动比其他时间更大,并且您可能希望更频繁地刷新缓冲区页。通过启用 自动 LRU 调整功能,可以让服务器监视页面刷新活动并更改其使用的lru_min_dirty和lru_max_dirty阈值。

  要启用此功能,请将AUTO_LRU_TUNING配置参数设置为 1。通过将参数设置为 0 来禁用该功能。默认情况下启用自动 LRU 调整,但是,如果数据库确定物理日志资源不足,则会忽略AUTO_LRU_TUNING设置并禁用该功能。消息日志中会写入消息以指示是否因此而禁用了自动调整。可以使用onmode -wm 或者onmode -wf 来动态调整:

onmode -wm AUTO_LRU_TUNING=1
onmode -wf AUTO_LRU_TUNING=1

监视LRU队列

  要监测 LRU 队列的状态,包括已修改缓冲区的百分比与 LRU 参数值,可以使用 onstat -R 命令。

8. 缓冲区写入类型

  在服务器中会发生几种类型的缓冲区写入。页清除线程活动的有效性可以通过页清除线程执行的不同类型的写入来进行衡量。写入类型为chunk写入、LRU 写入与前台写入。

  • chunk写入——发生在检查点期间

  • LRU 写入——发生在达到 LRU_MAX_DIRTY 参数值时

  • 前台写入(Foreground write)——发生在当需要将某页调入共享内存、而FLRU队列中没有足够可用的缓冲区时

chunk写入

  chunk写入发生在检查点期间,当页清除线程将共享内存缓冲池中的已修改缓冲区刷新到磁盘中时。每个页清除线程被指定给数据库中一个特定的chunk,从添加到服务器的第一个chunk开始,接着是第二个chunk、第三个chunk…页清除程序线程通过读取缓冲区头来查找与那些chunk关联的页。

  页清除线程通过页号对所有页进行排序成为磁盘序列,并将按序列写入磁盘。这种称为排序写 ,它可以最小化检查点期间的磁盘寻找时间。

  在一个服务器中,对每 100 个普通缓冲区会在共享内存的虚拟部分创建一个8页的大缓冲区(big buffer 。大缓冲区通过执行大型写入或读取操作来提高性能。每当有多个页写入到磁盘且这些页在磁盘上是物理连续的,就使用大缓冲区在一次 I/O 中写入8个连续的页。

LRU 写入

  当 LRU_MAX_DIRTY 和 LRU_MIN_DIRTY 的参数达到一定值时,页清除线程会启动 LRU 写入操作。

前台写入(Foreground write)

  当一个页需要写入共享内存,但在FLRU 队列中找不到空的或未修改的缓冲区时,用户线程启动前台写入操作。前台写入降低了性能,应通过调节 LRU 参数来避免。

onstat -F

  可以使用onstat -F 命令来监测出现在服务器中的写入类型。

9. 刷新物理日志缓冲区

  一般情况下,物理日志缓冲区,它包含了共享内存缓冲池中被修改页面的前映像,必须在修改缓冲池中页面之前写入磁盘。有几个事件可以导致物理日志缓冲区被刷新到磁盘:

  • 请求一个检查点。
  • 一个物理日志缓冲区变满。
  • 共享内存缓冲池中被修改页面必须被写到磁盘中,但页面的前映像仍位于物理日志缓冲区中。

  如果页清除线程活动开始,并且一个被修改页面被写入磁盘,那么有可能发生:该页被修改版本已在磁盘上,而它的前映像仍然在物理日志缓冲区中。同样,如果一个用户线程在这样的页上启动了一个前台写入操作,也可能出现同样的情况。

  每次物理日志缓冲区刷新的时候,服务器都会存储一个时间戳。当服务器需要刷新修改过的缓冲区时,会将被修改缓冲区的时间戳与上一次物理日志缓冲区刷新时记录的时间戳进行比较。如果页面上的时间戳大于或等于上一次物理日志缓冲区刷新时的时间戳,则物理日志缓冲区就会被刷新,因为被修改页面的前映像可能还在物理日志缓冲区中。必须总是在服务器能更新磁盘上的页面之前,将其前映像刷新到物理日志文件中。

onstat -l

  使用 onstat -l 命令来监视服务器中的物理日志缓冲区写入操作。

10. 刷新逻辑日志缓冲区

逻辑日志缓冲区的刷新是由如下所示的数据库日志模式所决定的:

数据库日志模式 说明
Buffered logging 直到缓冲区满时才会刷新
Unbuffered logging 只要有事务已完成,缓冲区就会被刷新。(例如,一旦提交或者回滚的记录写到日志缓冲区中)。

  对于同时使用 buffered 和 unbuffered logging的数据库,当有事务处理时,下面二种情况下缓冲区都会被刷新:要么当缓冲区满时,要么当在 Unbuffered 模式的数据库中事务完成时,看谁先发生。

  所有对磁盘上逻辑日志的 I/O 操作都是以页为单位进行的。如果在被刷新的缓冲区的页中有未使用的空间,则那部分空间也会被写到磁盘上的逻辑日志中。因此,unbuffered logging填充逻辑日志的速度比buffered logging的要快,因为日志页并不总需要填满。

下面这些事件可以导致逻辑日志缓冲区被刷新到磁盘中:

  • 一个检查点事件
  • 一个逻辑日志缓冲区变满
  • 使用 unbuffered logging的数据库中一个事务完成
  • 当一个nologging数据库中的会话结束时。另外,除了事务信息,DLL语句也会被写进逻辑日志中(例如,CREATE INDEX, CREATE TABLE)。这些信息必须写入磁盘。