日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

PostgreSQL checksum

發(fā)布時(shí)間:2024/3/24 数据库 66 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PostgreSQL checksum 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在計(jì)算機(jī)系統(tǒng)中,checksum 通常用于校驗(yàn)數(shù)據(jù)在傳輸或存取過(guò)程中是否發(fā)生錯(cuò)誤。PostgreSQL 從 9.3 開(kāi)始支持 checksum,以發(fā)現(xiàn)數(shù)據(jù)因磁盤(pán)、 I/O 損壞等原因造成的數(shù)據(jù)異常。本文介紹 PostgreSQL 中 checksum 的使用及其實(shí)現(xiàn)原理。

概述

PostgreSQL 從 9.3 開(kāi)始支持?jǐn)?shù)據(jù)頁(yè)的 checksum,可以在執(zhí)行 initdb 時(shí)指定 -k 或 --data-checksums 參數(shù)開(kāi)啟 checksum,但開(kāi)啟 checksum 可能會(huì)對(duì)系統(tǒng)性能有一定影響,官網(wǎng)描述如下:

Use checksums on data pages to help detect corruption by the I/O system that would otherwise be silent. Enabling checksums may incur a noticeable performance penalty. This option can only be set during initialization, and cannot be changed later. If set, checksums are calculated for all objects, in all databases.

啟用 checksum 后,系統(tǒng)會(huì)對(duì)每個(gè)數(shù)據(jù)頁(yè)計(jì)算 checksum,從存儲(chǔ)讀取數(shù)據(jù)時(shí)如果檢測(cè) checksum 失敗,則會(huì)發(fā)生錯(cuò)誤并終止當(dāng)前正在執(zhí)行的事務(wù),該功能使得 PostgreSQL 自身?yè)碛辛藱z測(cè) I/O 或硬件錯(cuò)誤的能力。

Checksum 引入一個(gè) GUC 參數(shù) ignore_checksum_failure,該參數(shù)若設(shè)置為 true,checksum 校驗(yàn)失敗后不會(huì)產(chǎn)生錯(cuò)誤,而是給客戶(hù)端發(fā)送一個(gè)警告。當(dāng)然,checksum 失敗意味著磁盤(pán)上的數(shù)據(jù)已經(jīng)損壞,忽略此類(lèi)錯(cuò)誤可能導(dǎo)致數(shù)據(jù)損壞擴(kuò)散甚至導(dǎo)致系統(tǒng)奔潰,此時(shí)宜盡早修復(fù),因此,若開(kāi)啟 checksum,該參數(shù)建議設(shè)置為 false。

實(shí)現(xiàn)原理

設(shè)置 checksum

數(shù)據(jù)頁(yè)的 checksum 在從 Buffer pool 刷到存儲(chǔ)時(shí)才設(shè)置,當(dāng)頁(yè)面再此讀取至 Buffer pool 時(shí)進(jìn)行檢測(cè)。

PostgreSQL 中 Buffer 刷盤(pán)的邏輯集中在 FlushBuffer 中,其中設(shè)置 checksum 的邏輯如下:

/** Update page checksum if desired. Since we have only shared lock on the* buffer, other processes might be updating hint bits in it, so we must* copy the page to private storage if we do checksumming.*/ bufToWrite = PageSetChecksumCopy((Page) bufBlock, buf->tag.blockNum);

比較有意思的是,其他進(jìn)程可能會(huì)在只加 content-lock 共享鎖的情況下并發(fā)修改 page 的 Hint Bits,從而導(dǎo)致 checksum 值發(fā)生變化,為確保 page 的內(nèi)容及其 checksum 保持一致,PostgreSQL 采用了 先復(fù)制頁(yè),然后計(jì)算 checksum 的方式,如下:

/** We allocate the copy space once and use it over on each subsequent* call. The point of palloc'ing here, rather than having a static char* array, is first to ensure adequate alignment for the checksumming code* and second to avoid wasting space in processes that never call this.*/ if (pageCopy == NULL)pageCopy = MemoryContextAlloc(TopMemoryContext, BLCKSZ);memcpy(pageCopy, (char *) page, BLCKSZ); ((PageHeader) pageCopy)->pd_checksum = pg_checksum_page(pageCopy, blkno);

即先將數(shù)據(jù)頁(yè)的內(nèi)容拷貝一份,拷貝的數(shù)據(jù)自然不會(huì)被其他進(jìn)程修改,然后基于該拷貝頁(yè)計(jì)算并設(shè)置 checksum 值。

checksum 算法

數(shù)據(jù)頁(yè)的 checksum 算法基于 FNV-1a hash 改造而來(lái),其結(jié)果為 32 位無(wú)符號(hào)整型。由于 PageHeaderData 中 pd_checksum 是 16 位無(wú)符號(hào)整型,因此將其截取 16 位作為數(shù)據(jù)頁(yè)的 checksum 值,如下:

/** Save pd_checksum and temporarily set it to zero, so that the checksum* calculation isn't affected by the old checksum stored on the page.* Restore it after, because actually updating the checksum is NOT part of* the API of this function.*/ save_checksum = cpage->phdr.pd_checksum; cpage->phdr.pd_checksum = 0; checksum = pg_checksum_block(cpage); cpage->phdr.pd_checksum = save_checksum;/* Mix in the block number to detect transposed pages */ checksum ^= blkno;/** Reduce to a uint16 (to fit in the pd_checksum field) with an offset of* one. That avoids checksums of zero, which seems like a good idea.*/ return (checksum % 65535) + 1;

pg_checksum_block 函數(shù)計(jì)算數(shù)據(jù)頁(yè)的 32 位 checksum 值,具體算法可以參考源碼,在此不詳述。

檢測(cè) checksum

PostgreSQL 會(huì)在頁(yè)面從存儲(chǔ)讀入內(nèi)存時(shí)檢測(cè)其是否可用,調(diào)用函數(shù)為 PageIsVerified,該函數(shù)不僅會(huì)檢測(cè)正常初始化過(guò)的頁(yè)(non-zero page),還會(huì)檢測(cè) 全零頁(yè)(all-zero page)。

為什么會(huì)出現(xiàn) 全零頁(yè) 呢?
在特定場(chǎng)景下表中可能出現(xiàn) 全零頁(yè),比如有進(jìn)程擴(kuò)展了一個(gè)表,即在該表中添加了一個(gè)新頁(yè),但在 WAL 日志寫(xiě)入存儲(chǔ)之前,進(jìn)程崩潰了。此時(shí)新加的頁(yè)可能已經(jīng)在表文件中,下次重啟時(shí)就會(huì)讀取到。

對(duì)于 non-zero page,檢測(cè)其 checksum 是否一致以及 page header 信息是否正確,若 checksum 失敗,但 header 信息正確,此時(shí)會(huì)根據(jù) ignore_checksum_failure 值判斷驗(yàn)證是否通過(guò);對(duì)于 all-zero page,如果為全零,則驗(yàn)證通過(guò)。

若驗(yàn)證失敗,兩種處理方式:

  • 若讀取數(shù)據(jù)的模式為 RBM_ZERO_ON_ERROR 且 GUC 參數(shù) zero_damaged_pages 為 true,則將該頁(yè)全部置 0
  • 報(bào)錯(cuò),invalid page

checksum 與 Hint bits

數(shù)據(jù)頁(yè)寫(xiě)至存儲(chǔ)時(shí),如果寫(xiě)失敗,可能會(huì)導(dǎo)致破碎的頁(yè)(torn page),PostgreSQL 通過(guò) full_page_writes 特性解決此類(lèi)寫(xiě)失敗導(dǎo)致數(shù)據(jù)不可用的問(wèn)題。

Hint Bits 是數(shù)據(jù)頁(yè)中用于標(biāo)識(shí)事務(wù)狀態(tài)的標(biāo)記位,一般情況下,作為提示位,不是很重要。但如果使用了 checksum,Hint Bits 的變化會(huì)導(dǎo)致 checksum 值發(fā)生改變。設(shè)想如果一個(gè)頁(yè)面發(fā)生部分寫(xiě),恰好把某些 Hint Bits 寫(xiě)錯(cuò),此頁(yè)面可能并不影響正常使用,但 checksum 會(huì)拋出異常,此時(shí)應(yīng)如何恢復(fù)呢?

在 checksum 的實(shí)現(xiàn)中,checkpoint 后,如果頁(yè)面因更新 Hint Bits 第一次被標(biāo)記為 dirty,需要記錄一個(gè) Full Page Image 至 WAL 日志中,以應(yīng)對(duì)以上提到的因 Hint Bits 更新丟失導(dǎo)致 checksum 失敗的問(wèn)題,具體實(shí)現(xiàn)可參考 MarkBufferDirtyHint。對(duì)于已經(jīng)是 dirty 的頁(yè),更新 Hint Bits 則不需要記錄 WAL 日志,因?yàn)樵?checkpoint 后,第一次將該頁(yè)標(biāo)記為 dirty 時(shí)已經(jīng)寫(xiě)入了對(duì)應(yīng)的 Full Page Image。

可見(jiàn),在啟用 checksum 的情況下,checkpoint 后頁(yè)面的第一次修改如果是更新 Hint Bits, 會(huì)寫(xiě) Full Page Image 至 WAL 日志,這會(huì)導(dǎo)致 WAL 日志占用更多的存儲(chǔ)空間。

關(guān)于 PostgreSQL checksum 和 Full Page Image 的關(guān)系,可以參考 stackoverflow 上這個(gè)問(wèn)題。

查看 checksum

PostgreSQL 10 在 pageinspect 插件中添加了函數(shù) page_checksum() 用來(lái)查看 page 的 checksum,當(dāng)然使用 page_header() 也可以查看 page 的 checksum,如下:

postgres=# SELECT page_checksum(get_raw_page('pg_class', 0), 0);page_checksum ---------------17448 (1 row)postgres=# SELECT * FROM page_header(get_raw_page('pg_class', 0));lsn | checksum | flags | lower | upper | special | pagesize | version | prune_xid ------------+----------+-------+-------+-------+---------+----------+---------+-----------0/78A1E918 | 17448 | 0 | 200 | 368 | 8192 | 8192 | 4 | 0 (1 row)

總結(jié)

Checksum 使 PostgreSQL 具備檢測(cè)因硬件故障或傳輸導(dǎo)致數(shù)據(jù)不一致的能力,一旦發(fā)生異常,通常會(huì)報(bào)錯(cuò)并終止當(dāng)前事務(wù),用戶(hù)可以盡早察覺(jué)數(shù)據(jù)異常并予以恢復(fù)。當(dāng)然,開(kāi)啟 checksum 也會(huì)引入一些開(kāi)銷(xiāo),體現(xiàn)在兩個(gè)方面:

  • 計(jì)算數(shù)據(jù)頁(yè)的 checksum 會(huì)引入一些 CPU 開(kāi)銷(xiāo),具體開(kāi)銷(xiāo)取決于 checksum 算法的效率
  • checkpoint 后,若因更新 Hint Bits 將頁(yè)面第一次置為 dirty 會(huì)寫(xiě)一條記錄 Full Page Image 的 WAL 日志,以用于恢復(fù)因更新 Hint Bits 產(chǎn)生的破碎頁(yè)。

對(duì)于數(shù)據(jù)可用性要求較高的場(chǎng)景,通常建議將 full_page_writes 和 checksum 都打開(kāi),前者用于避免寫(xiě)失敗導(dǎo)致的數(shù)據(jù)缺失,后者用于盡早發(fā)現(xiàn)因硬件或傳輸導(dǎo)致數(shù)據(jù)不一致的場(chǎng)景,一旦發(fā)現(xiàn),可以利用 full_page_writes 和 checksum 記錄在 WAL 日志中的 Full Page Image 進(jìn)行數(shù)據(jù)恢復(fù)。

References

  • https://paquier.xyz/postgresql-2/postgres-9-3-feature-highlight-data-checksums/
  • https://en.wikipedia.org/wiki/Checksum
  • https://www.postgresql.org/docs/11/pgverifychecksums.html

總結(jié)

以上是生活随笔為你收集整理的PostgreSQL checksum的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。