这个博客是在继续我以前的博客腐败与主题的基本理解终极指南数据库腐败:第1部分,概述。如果您还没有经历过,我建议您阅读理解数据腐败。
介绍
这个博客专注于b -树索引和各种贪污问题发生。了解Postgres实现b -树索引之前,需要一个可以理解腐败指数。一旦我们有一个索引结构的正确理解,以及b -树索引访问,很容易理解腐败影响数据检索操作,以及各种方法来解决这些冲突。
同时也将讨论一些案例研究在这个博客。
b -树索引结构
PostgreSQL的b -树索引结构是使用雷曼和姚明的高并发性实现b -树算法。从逻辑上讲,它包含四种类型的页面层次结构组织的。
- 元的页面
- 根页面
- 内部页面
- 叶页面
元一页:
这是第一个页面的索引基本上包含元数据信息,如类型的指数,也被称为零页。根的位置页面可以从这个页面获得。如果这个页面是损坏或无法访问,访问索引是不可能的。
根页面和内部页面:
根页面的第一页包含链接的页面称为内部页面和叶页面。在存储方面,内部页面并不比根不同的页面;他们还存储指向其他内部页面。唯一的区别在于,只有一个根在每个索引页,虽然可能是一个内部页面的数量。
这些页面不包含任何信息访问表记录。
叶页面:
这些页面躺在过去的水平,不能进一步催生了。它们包含实际信息访问表数据。在这些页面价值和存储的数据CTIDs从表中特定的价值所在。
一个典型的b -树索引结构如下。

作为叙述在上面的图中,根页面和内部页面的元组与内部页面或叶页面。每个内部和叶子页面都应该有值等于或大于该值与前一页,下一个更高的价值比在该页面相关的价值。第一个元组在每根和内部页面是空白的;它指向的页面包含所有的值低于它的直接右邻居。
例如,在上面的图片中,
- 根页面有三个元组(空白,80),和“内部页面1”相关的元组的值为空白,和下一个更高的价值根源是40页。这里,“内部页面1”可能包含值不到40。
- 而“内部第二页”包含值大于或等于40和小于80。
案例研究
指数的腐败,它给不同的结果按照位置数据损坏。在这里,腐败可能存在于任何页面(根、内部、叶)。但仔细学习时,你可以明白,腐败可能并不总是显示它的存在。在的场合,它误导了用户。如果页眉或格式是腐败,就会抛出错误而执行查询本身。但是,当实际数据内页损坏而不是格式,它不能检测到损坏,但是它返回一些结果。
在这个博客中,我故意选择索引没有任何内部页面,以便读者能明白内部页面不一定存在于每一个b -树索引。这里使用的索引是一个主键索引。
本节处理情况查询返回不正确的结果由于腐败。这里,我们深入研究数据腐败与叶页面和内部页面。
案例1 -数据被破坏,就无法测度
这是腐败的一个经典案例,Postgres试图找到一条记录,但不能被发现,因为不属于当前父节点的值。在这种情况下,在一个叶页面,得到相互交换两个相邻位。细节如下。
这里,我们随机从一个表中选择一个号码和准备一个案例。下面是记录和相关细节。记录(id = 612)中描述下面的快照将我们的目标描述腐败。


我们甲型肝炎e corruption_test id列上的索引,如下所述,下划线部分的位置(10号和11号)中交换一个索引。因此,实际值从612变成1124;然而,相关表中的值还是一样的。
表名:- corruption_test
索引名称:- test_tbl_pkey
腐败在页面2:-叶页面
CTID关联表:- (101 6)
实际值的元组索引:-00000年010 01100100(612)
价值元组索引后腐败:-00000年100 01100100 (1124)
下面的图像描绘了实际发生在test_tbl_pkey指数。

现在,我们将看到如何查询回复当我们请求来检索数据值与这个案子有关。
查询一个表使用原始号码:
如上所述,实际值为612(腐败)。让我们看看结果我们接受通过查询使用“id = 612”谓词。

现在,w帽子如果我们使用以前的搜索数据表中提取CTID吗?

这是足以让你大吃一惊,因为表中的记录确实存在,但是,它不能直接查询检索它,当我们使用一个特定的值。
这是由于612年取代了1124年的腐败指数。这里,搜索执行索引扫描获取记录,索引是无法找到所需的记录;因此,它无法显示任何数据。下面描述了这种情况。

查询一个表使用一个损坏的数量:
我们已经明白,612年该指数不存在,它可能不是从索引。但是,我们知道1124年存在于指数。很想知道会发生什么查询,1124。

让我们深入一点更深的知道为什么我们没有得到任何记录查询的数据。
这里,每一个有效的索引结构,1124年应该在叶子页面3中,但它可能没有被发现,因为它不存在的页面或桌子上。而叶第二页包含细节有关1124年,PostgreSQL不会搜索1124年叶页面2逻辑上不满足探索价值1124的页面;因此,价值不会被发现。
下面的图片了。

例2 -错误数据指针(CTID)存储值
在这种情况下,PostgreSQL试图找到一个记录的索引,它可以找到记录,但显示的是错误的。这是由于假CTID值存储在索引中。因此,它显示了一个完全不同的记录。

这里,一点CTID会发生改变,使腐败的新的动力。我们随机选择一个记录(id = 245)从表作为一门学科来描述测试用例。下面的快照是记录的细节。
表名:- corruption_test
索引名称:- test_tbl_pkey
腐败在页面1:-叶页面
CTID关联表:-(40岁,5)
价值在CTID:- 245
实际CTID索引元组:-(40岁,5)- (00101000年,00000101)
CTID索引元组在腐败:-(56岁,5)- (00111000年,00000101)
如上所述,由于变化的价值在第五位,它存储CTID不同。如下描述的。

现在,我们将观察不同的查询行为在这种情况下。
查询一个表使用一个可疑数字:
如上所述,怀疑是245的价值。让我们看看结果我们接受通过查询使用“id = 245”谓词。

这是令人震惊和令人费解的返回的数据比我们预期的完全不同。不需要解释一下它的含义可能是日常业务。
在这里,在一个索引,我们可以观察到CTID存储对245点到一个不同的值。所以,这里的查询返回不同的值。以下图片描述内部到底发生了什么。

查询一个表只通过选择id:
如果我们只选择查询的id列,它显示了正确的结果。

得到正确的结果的原因是,它执行一个“唯一索引扫描”,因为该指数存在于id列。因此不需要访问表数据显示记录的索引包含所有所需的记录。
例3 -不正确的索引格式
每个数据库对象有一个预定义的格式。PostgreSQL数据库(或任何其他)编程读和写在一个特定的格式,只是一个字符序列(或字节)。如果一个或多个字符得到改变,对象变得不可读。在这样一个腐败案中,查询立即返回一个错误,错误文本每次都是不一样的。
让我们考虑各种情况下的错误。
损坏元页面:
如上所述,元任何索引的页面是一个核心页面;它存储的元数据索引。因此,如果任何format-related腐败是在元页面,该指数变得面目全非。因此,查询没有返回任何结果。
以下快照查询特定记录时显示了结果。
![]()
以下查询整个表查询时显示了结果。
![]()
这是一个陷阱!不接收数据是可以理解的,如果一个查询执行索引扫描。但是,它是微扰查询时不能提供,因为索引腐败时不会使用索引。但是,这种情况由于计划。
损坏non-meta页面:
如果format-related腐败存在于non-meta页面(根、内部、或叶),它会返回一个不同的错误。但是,它不会影响查询执行顺序扫描。
![]()
原因
读完上面的部分,你可以理解腐败只是将不恰当的字节存储在数据库文件,导致异常数据。但是,这里的问题是如何发生的。尽管许多事件离开腐败,很难创建任何可再生的测试用例的腐败,因为没有担保实际引起的。我们唯一的选择就是投机。大多数问题都是由硬件故障或硬件问题引起的,和下面是最可能的原因。
- 错误的RAID磁盘或RAID控制器
- 有缺陷的磁盘或内存
- 磁盘没有电源故障保护
- CPU过载
- PostgreSQL / OS错误
此外,存在一些问题造成的用户。其中有一些是如下。
- 杀死一个与信号9
- 突然停止PostgreSQL
- 没有pg_start_backup复制文件的备份
- 执行pg_resetwal
腐败不是频繁的事件;然而,它变成了常规硬件故障或缺陷。
检测
如前所述在案例研究部分,是很困难的(虽然也有例外)检测腐败数据索引内损坏时。这是因为数据不能扫描的数据库产品;然而,这是不正确的格式。
当b -树索引的格式是损坏的,我们可以使用的功能amcheck和pageinspect扩展验证其格式。这里有一个例子验证索引页面使用bt_page_items amcheck扩展的函数。
我们可以遍历所有页的索引和列表页面是损坏的。然而,这几乎是有道理的,因为我们不能改变数据在一个索引。
修复
消除腐败的b -树索引不需要深入分析;这只是一个重新创建索引的问题。正如我们所知,一个索引表数据的一个子集,使更快的数据检索。只要没有问题表中的数据,我们可以重建索引。它可以由以下选项。
- 删除和重新创建索引
- 使用重建索引命令
- 重建使用pg_repack实用程序
所有这些,pg_repack是最可行的选择,因为它开始创建并行索引,以便运行操作不会受到影响。有关更多信息,请访问pg_repack博客。
期待
回顾与腐败有关的各种场景后,我们可以肯定说,这真的是可怕的,因为它可能会吓到我们通过显示不必要的和意想不到的结果。数据是一种宝贵的财富,因此,它是不必要的描述其业务的影响;我们可以肯定认为是一样的。好吧,将它扼杀在萌芽状态怎么样?
是的,这是可能的。我们可以得到腐败记录在数据库中使用一个校验和特性。在执行initdb,我们可以使用- k选项来启用校验和,后来需要保持data_checksums参数启用。这将记录每一个腐败pg_stat_database视图。在这里,如果我们已经知道腐败的存在,可以传播到用户之前采取必要的行动。
总结
一个健壮的b -树索引结构是专为更快的数据访问。然而,当某些比特序列改变,变得不可读或开始返回false数据,影响日常业务。明确,这是一个可怕的情况。然而,它可以检测某些类型的腐败,并建议采取先发制人的措施来应对这种情况。
请把你的查询或在评论部分的建议。




