塊層快取 (bcache)

假設你有一個大型的、速度慢的 raid 6,以及一到三個 ssd。 如果你能將它們用作快取,那豈不是很好……因此有了 bcache。

bcache wiki 可以在以下網址找到:

https://bcache.evilpiepirate.org

這是 bcache-tools 的 git 倉庫

https://git.kernel.org/pub/scm/linux/kernel/git/colyli/bcache-tools.git/

最新的 bcache 核心程式碼可以從主線 Linux 核心中找到

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/

它圍繞 SSD 的效能特徵而設計 - 它只分配擦除塊大小的 bucket,並且它使用混合 btree/日誌來跟蹤快取的範圍(可以是單個扇區到 bucket 大小的任何位置)。 它的設計目標是不惜一切代價避免隨機寫入; 它會按順序填充一個擦除塊,然後在重複使用之前發出丟棄命令。

支援直寫和回寫快取。 回寫預設為關閉,但可以在執行時任意開啟和關閉。 Bcache 竭盡全力保護您的資料 - 它可以可靠地處理非正常關機。(它甚至沒有正常關機的概念; bcache 只有在寫入完成並存儲在穩定儲存上後才會返回)。

回寫快取可以使用大部分快取來緩衝寫入 - 將髒資料寫入到後備裝置始終是按順序完成的,從索引的開始到結束進行掃描。

由於隨機 IO 是 SSD 的優勢,因此快取大型順序 IO 通常不會有太多好處。 Bcache 檢測順序 IO 並跳過它; 它還會保留每個任務的 IO 大小的滾動平均值,只要平均值高於截止值,它就會跳過來自該任務的所有 IO - 而不是在每次尋道後快取前 512k。 因此,備份和大型檔案複製應該完全繞過快取。

如果快閃記憶體上發生資料 IO 錯誤,它將嘗試透過從磁碟讀取或使快取條目無效來恢復。 對於無法恢復的錯誤(元資料或髒資料),快取將自動停用; 如果快取中存在髒資料,它首先會停用回寫快取,並等待所有髒資料重新整理。

入門: 您需要 bcache-tools 倉庫中的 bcache util。 快取裝置和後備裝置都必須在使用前格式化

bcache make -B /dev/sdb
bcache make -C /dev/sdc

bcache make 具有同時格式化多個裝置的能力 - 如果您同時格式化後備裝置和快取裝置,則不必手動附加

bcache make -B /dev/sda /dev/sdb -C /dev/sdc

如果您的 bcache-tools 沒有更新到最新版本並且沒有統一的 bcache 實用程式,您可以使用舊的 make-bcache 實用程式來格式化具有相同 -B 和 -C 引數的 bcache 裝置。

bcache-tools 現在附帶 udev 規則,並且 bcache 裝置會立即被核心識別。 如果沒有 udev,您可以手動註冊裝置,如下所示

echo /dev/sdb > /sys/fs/bcache/register
echo /dev/sdc > /sys/fs/bcache/register

註冊後備裝置會使 bcache 裝置出現在 /dev 中; 您現在可以格式化它並像往常一樣使用它。 但是第一次使用新的 bcache 裝置時,它將以透傳模式執行,直到您將其附加到快取。 如果您考慮稍後使用 bcache,建議將所有速度較慢的裝置設定為沒有快取的 bcache 後備裝置,您可以選擇稍後新增快取裝置。 請參閱下面的“附加”部分。

裝置顯示為

/dev/bcache<N>

以及(使用 udev)

/dev/bcache/by-uuid/<uuid>
/dev/bcache/by-label/<label>

入門

mkfs.ext4 /dev/bcache0
mount /dev/bcache0 /mnt

您可以透過 /sys/block/bcache<N>/bcache 中的 sysfs 控制 bcache 裝置。 您還可以透過 /sys/fs//bcache/<cset-uuid>/ 控制它們。

快取裝置作為集合進行管理; 尚不支援每個集合多個快取,但將來可以映象元資料和髒資料。 您的新快取集合顯示為 /sys/fs/bcache/<UUID>

附加

註冊快取裝置和後備裝置後,必須將後備裝置附加到您的快取集合才能啟用快取。 將後備裝置附加到快取集合的方式如下,快取集合的 UUID 位於 /sys/fs/bcache 中

echo <CSET-UUID> > /sys/block/bcache0/bcache/attach

這隻需要做一次。 下次重新啟動時,只需重新註冊所有 bcache 裝置即可。 如果後備裝置在某個地方的快取中有資料,則在快取顯示之前不會建立 /dev/bcache<N> 裝置 - 如果您啟用了回寫快取,這一點尤為重要。

如果您正在啟動並且您的快取裝置消失並且永遠不會再回來,您可以強制執行後備裝置

echo 1 > /sys/block/sdb/bcache/running

(您需要使用 /sys/block/sdb(或您後備裝置的名稱),而不是 /sys/block/bcache0,因為 bcache0 尚不存在。如果您使用的是分割槽,則 bcache 目錄將位於 /sys/block/sdb/sdb2/bcache)

如果將來出現,後備裝置仍將使用該快取集合,但所有快取的資料都將失效。 如果快取中有髒資料,請不要指望檔案系統可以恢復 - 您將遇到大規模的檔案系統損壞,儘管 ext4 的 fsck 確實創造了奇蹟。

錯誤處理

Bcache 嘗試透明地處理與快取裝置之間的 IO 錯誤,而不會影響正常執行; 如果它看到太多錯誤(閾值是可配置的,預設為 0),它會關閉快取裝置並將所有後備裝置切換到透傳模式。

  • 對於從快取的讀取,如果發生錯誤,我們只需從後備裝置重試讀取。

  • 對於直寫寫入,如果寫入快取時發生錯誤,我們只需切換到使快取中該 lba 處的資料無效(即,我們對繞過快取的寫入執行的操作相同)

  • 對於回寫寫入,我們目前將該錯誤傳遞迴檔案系統/使用者空間。 這可以改進 - 我們可以將其作為跳過快取的寫入重試,這樣我們就不必返回寫入錯誤。

  • 當我們分離時,我們首先嚐試重新整理所有髒資料(如果我們以回寫模式執行)。 但是,如果它無法讀取某些髒資料,則目前不會執行任何智慧操作。

操作方法/食譜

  1. 使用丟失的快取裝置啟動 bcache

如果註冊後備裝置沒有幫助,它已經存在,您只需強制它在沒有快取的情況下執行

host:~# echo /dev/sdb1 > /sys/fs/bcache/register
[  119.844831] bcache: register_bcache() error opening /dev/sdb1: device already registered

接下來,如果存在快取裝置,請嘗試註冊它。 但是,如果它不存在,或者由於某種原因註冊失敗,您仍然可以在沒有快取的情況下啟動 bcache,如下所示

host:/sys/block/sdb/sdb1/bcache# echo 1 > running

請注意,如果您以回寫模式執行,這可能會導致資料丟失。

  1. Bcache 找不到其快取

    host:/sys/block/md5/bcache# echo 0226553a-37cf-41d5-b3ce-8b1e944543a8 > attach
    [ 1933.455082] bcache: bch_cached_dev_attach() Couldn't find uuid for md5 in set
    [ 1933.478179] bcache: __cached_dev_store() Can't attach 0226553a-37cf-41d5-b3ce-8b1e944543a8
    [ 1933.478179] : cache set not found
    

在這種情況下,快取裝置只是未在啟動時註冊或消失後又重新出現,需要(重新)註冊

host:/sys/block/md5/bcache# echo /dev/sdh2 > /sys/fs/bcache/register
  1. 損壞的 bcache 在設備註冊時導致核心崩潰

這永遠不應該發生。 如果確實發生,那麼您就發現了一個錯誤! 請將其報告給 bcache 開發列表:linux-bcache@vger.kernel.org

請務必提供儘可能多的資訊,包括核心 dmesg 輸出(如果可用),以便我們提供幫助。

  1. 在沒有 bcache 的情況下恢復資料

如果核心中沒有 bcache,則後備裝置上的檔案系統仍然可在 8KiB 偏移處使用。 因此,可以透過使用 --offset 8K 建立的後備裝置的 loopdev,或者在最初使用 bcache make 格式化 bcache 時由 --data-offset 定義的任何值。

例如

losetup -o 8192 /dev/loop0 /dev/your_bcache_backing_dev

這應該在 /dev/loop0 中呈現您未修改的後備裝置資料

如果您的快取處於直寫模式,則可以安全地丟棄快取裝置而不會丟失資料。

  1. 擦除快取裝置

host:~# wipefs -a /dev/sdh2
16 bytes were erased at offset 0x1018 (bcache)
they were: c6 85 73 f6 4e 1a 45 ca 82 65 f5 7f 48 ba 6d 81

在重新啟動並啟用 bcache 後,您將重新建立快取並附加它

host:~# bcache make -C /dev/sdh2
UUID:                   7be7e175-8f4c-4f99-94b2-9c904d227045
Set UUID:               5bc072a8-ab17-446d-9744-e247949913c1
version:                0
nbuckets:               106874
block_size:             1
bucket_size:            1024
nr_in_set:              1
nr_this_dev:            0
first_bucket:           1
[  650.511912] bcache: run_cache_set() invalidating existing data
[  650.549228] bcache: register_cache() registered cache device sdh2

使用丟失的快取啟動後備裝置

host:/sys/block/md5/bcache# echo 1 > running

附加新快取

host:/sys/block/md5/bcache# echo 5bc072a8-ab17-446d-9744-e247949913c1 > attach
[  865.276616] bcache: bch_cached_dev_attach() Caching md5 as bcache0 on set 5bc072a8-ab17-446d-9744-e247949913c1
  1. 刪除或替換快取裝置

    host:/sys/block/sda/sda7/bcache# echo 1 > detach
    [  695.872542] bcache: cached_dev_detach_finish() Caching disabled for sda7
    
    host:~# wipefs -a /dev/nvme0n1p4
    wipefs: error: /dev/nvme0n1p4: probing initialization failed: Device or resource busy
    Ooops, it's disabled, but not unregistered, so it's still protected
    

我們需要去登出它

host:/sys/fs/bcache/b7ba27a1-2398-4649-8ae3-0959f57ba128# ls -l cache0
lrwxrwxrwx 1 root root 0 Feb 25 18:33 cache0 -> ../../../devices/pci0000:00/0000:00:1d.0/0000:70:00.0/nvme/nvme0/nvme0n1/nvme0n1p4/bcache/
host:/sys/fs/bcache/b7ba27a1-2398-4649-8ae3-0959f57ba128# echo 1 > stop
kernel: [  917.041908] bcache: cache_set_free() Cache set b7ba27a1-2398-4649-8ae3-0959f57ba128 unregistered

現在我們可以擦除它

host:~# wipefs -a /dev/nvme0n1p4
/dev/nvme0n1p4: 16 bytes were erased at offset 0x00001018 (bcache): c6 85 73 f6 4e 1a 45 ca 82 65 f5 7f 48 ba 6d 81
  1. dm-crypt 和 bcache

首先設定未加密的 bcache,然後在 /dev/bcache<N> 上安裝 dmcrypt。 這將比您對後備裝置和快取裝置都進行 dmcrypt,然後在上面安裝 bcache 速度更快。[基準測試?]

  1. 停止/釋放已註冊的 bcache 以進行擦除和/或重新建立

假設您需要釋放所有 bcache 引用,以便您可以執行 fdisk 並重新註冊已更改的分割槽表,如果其上還有任何活動的後備或快取裝置,這將不起作用

  1. 它是否存在於 /dev/bcache* 中? (有時不會)

    如果是,那就很容易了

    host:/sys/block/bcache0/bcache# echo 1 > stop
    
  2. 但是如果您的後備裝置消失了,這將不起作用

    host:/sys/block/bcache0# cd bcache
    bash: cd: bcache: No such file or directory
    

    在這種情況下,您可能需要登出引用此 bcache 的 dmcrypt 塊裝置才能釋放它

    host:~# dmsetup remove oldds1
    bcache: bcache_device_free() bcache0 stopped
    bcache: cache_set_free() Cache set 5bc072a8-ab17-446d-9744-e247949913c1 unregistered
    

    這會導致後備 bcache 從 /sys/fs/bcache 中刪除,然後可以重複使用它。 這對於任何塊裝置堆疊都是如此,其中 bcache 是較低的裝置。

  3. 在其他情況下,您也可以在 /sys/fs/bcache/ 中查詢

    host:/sys/fs/bcache# ls -l */{cache?,bdev?}
    lrwxrwxrwx 1 root root 0 Mar  5 09:39 0226553a-37cf-41d5-b3ce-8b1e944543a8/bdev1 -> ../../../devices/virtual/block/dm-1/bcache/
    lrwxrwxrwx 1 root root 0 Mar  5 09:39 0226553a-37cf-41d5-b3ce-8b1e944543a8/cache0 -> ../../../devices/virtual/block/dm-4/bcache/
    lrwxrwxrwx 1 root root 0 Mar  5 09:39 5bc072a8-ab17-446d-9744-e247949913c1/cache0 -> ../../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/ata10/host9/target9:0:0/9:0:0:0/block/sdl/sdl2/bcache/
    

    裝置名稱將顯示哪個 UUID 相關,cd 進入該目錄並停止快取

    host:/sys/fs/bcache/5bc072a8-ab17-446d-9744-e247949913c1# echo 1 > stop
    

    這將釋放 bcache 引用,並允許您將該分割槽用於其他目的。

效能故障排除

Bcache 有很多配置選項和可調引數。 預設值旨在對典型的桌面和伺服器工作負載是合理的,但它們不是您在進行基準測試時獲得最佳數字所需的。

  • 後備裝置對齊

    bcache 中的預設元資料大小為 8k。 如果您的後備裝置基於 RAID,請務必使用 bcache make --data-offset 按步幅寬度的倍數對齊它。 如果您打算將來擴充套件磁碟陣列,請將一系列素數乘以您的 raid 條帶大小,以獲得您想要的磁碟倍數。

    例如: 如果您的條帶大小為 64k,則以下偏移量將為許多常見的 RAID5 資料碟片數提供對齊

    64k * 2*2*2*3*3*5*7 bytes = 161280k
    

    該空間被浪費了,但僅需 157.5MB,您就可以將 RAID 5 卷增長到以下資料碟片數,而無需重新對齊

    3,4,5,6,7,8,9,10,12,14,15,18,20,21 ...
    
  • 寫入效能不佳

    如果寫入效能不符合您的預期,您可能希望以回寫模式執行,這不是預設設定(不是因為缺乏成熟度,而是因為在回寫模式下,如果您的 SSD 出現問題,您將會丟失資料)

    # echo writeback > /sys/block/bcache0/bcache/cache_mode
    
  • 效能不佳,或流量沒有按預期流向 SSD

    預設情況下,bcache 不會快取所有內容。 它會嘗試跳過順序 IO - 因為您真正想要快取的是隨機 IO,並且如果您複製一個 10 GB 的檔案,您可能不希望它將 10 GB 的隨機訪問資料從快取中推出。

    但是,如果您想對從快取的讀取進行基準測試,並且您從 fio 寫入一個 8 GB 的測試檔案開始 - 因此您想停用它

    # echo 0 > /sys/block/bcache0/bcache/sequential_cutoff
    

    要將其設定回預設值 (4 mb),請執行

    # echo 4M > /sys/block/bcache0/bcache/sequential_cutoff
    
  • 流量仍然流向碟片/仍然遇到快取未命中

    在現實世界中,SSD 並不總是能跟上磁碟 - 尤其是對於速度較慢的 SSD,一個 SSD 快取多個磁碟,或者主要是順序 IO。 因此,您想避免受到 SSD 的限制,並使其減慢所有速度。

    為避免這種情況,bcache 會跟蹤到快取裝置的延遲,如果延遲超過閾值,它會逐漸限制流量(它透過降低順序繞過來實現這一點)。

    如果您需要,可以透過將閾值設定為 0 來停用此功能

    # echo 0 > /sys/fs/bcache/<cache set>/congested_read_threshold_us
    # echo 0 > /sys/fs/bcache/<cache set>/congested_write_threshold_us
    

    讀取的預設值為 2000 us(2 毫秒),寫入的預設值為 20000。

  • 仍然遇到快取未命中,但資料相同

    有時會讓人困惑的最後一個問題實際上是一箇舊的錯誤,這是由於快取未命中時處理快取一致性的方式造成的。 如果 btree 節點已滿,則快取未命中將無法插入新資料的金鑰,並且資料將不會寫入快取。

    實際上,這不是問題,因為一旦有寫入發生,就會導致 btree 節點被拆分,並且您幾乎不需要寫入流量就可以使其不明顯(特別是由於 bcache 的 btree 節點很大並且索引了裝置的大區域)。 但是,當您進行基準測試時,如果您嘗試透過讀取大量資料來預熱快取並且沒有其他流量 - 這可能是一個問題。

    解決方案: 透過執行寫入操作來預熱快取,或者使用測試分支(那裡有一個針對該問題的修復)。

Sysfs - 後備裝置

可在 /sys/block/<bdev>/bcache、/sys/block/bcache*/bcache 和(如果已連線)/sys/fs/bcache/<cset-uuid>/bdev* 中找到

attach

將快取集合的 UUID 回顯到此檔案以啟用快取。

cache_mode

可以是 writethrough、writeback、writearound 或 none 之一。

clear_stats

寫入此檔案會重置執行總計統計資訊(而不是每天/每小時/每 5 分鐘衰減的版本)。

detach

寫入此檔案以從快取集合中分離。 如果快取中有髒資料,將首先重新整理它。

dirty_data

快取中此後備裝置的髒資料量。 與快取集合的版本不同,會持續更新,但可能略有偏差。

label

底層裝置的名稱。

readahead

應執行的預讀大小。 預設為 0。如果設定為例如 1M,它將向上舍入快取未命中的讀取大小,但不會重疊現有快取條目。

running

如果 bcache 正在執行(即 /dev/bcache 裝置是否存在,它是處於透傳模式還是快取模式),則為 1。

sequential_cutoff

一旦超過此閾值,順序 IO 將繞過快取; 將跟蹤最近的 128 個 IO,因此即使並非一次全部完成,也可以檢測到順序 IO。

sequential_merge

如果非零,bcache 會保留提交的最後 128 個請求的列表,以與所有新請求進行比較,以確定哪些新請求是先前請求的順序延續,以確定順序截止。 如果順序截止值大於任何單個請求的最大可接受順序大小,則這是必要的。

state

後備裝置可以處於四種不同的狀態之一

no cache: 從未連線到快取集合。

clean: 快取集合的一部分,並且沒有快取的髒資料。

dirty: 快取集合的一部分,並且有快取的髒資料。

inconsistent: 當有髒資料被快取但快取集合不可用時,使用者強制運行了後備裝置; 後備裝置上的任何資料可能都已損壞。

stop

寫入此檔案以關閉 bcache 裝置並關閉後備裝置。

writeback_delay

當髒資料寫入快取並且之前不包含任何髒資料時,在啟動回寫之前等待一些秒數。 預設為 30。

writeback_percent

如果非零,bcache 會嘗試透過限制後臺回寫並使用 PD 控制器平穩地調整速率,來保持大約此百分比的快取為髒。

writeback_rate

速率,以每秒扇區數計 - 如果 writeback_percent 為非零,則後臺回寫將限制為此速率。 由 bcache 持續調整,但也可能由使用者設定。

writeback_running

如果關閉,則根本不會發生髒資料的回寫。 髒資料仍將新增到快取中,直到它幾乎已滿; 僅用於基準測試。 預設為開啟。

Sysfs - 後備裝置統計資訊

這些數字有目錄,用於執行總計,以及過去一天、一小時和 5 分鐘內衰減的版本; 它們也會在快取集合目錄中進行聚合。

bypassed

已繞過快取的 IO 量(讀取和寫入)

cache_hits, cache_misses, cache_hit_ratio

命中和未命中是根據 bcache 看到的每個單獨 IO 進行計數的; 部分命中被計為未命中。

cache_bypass_hits, cache_bypass_misses

仍然會計算打算跳過快取的 IO 的命中和未命中,但在此處將其分解。

cache_miss_collisions

計算了從快取未命中將資料插入快取的情況,但與寫入競爭並且資料已經存在的情況(通常為 0,因為快取未命中的同步已重寫)

Sysfs - 快取集合

可在 /sys/fs/bcache/<cset-uuid> 中找到

average_key_size

btree 中每個金鑰的平均資料量。

bdev<0..n>

指向每個連線的後備裝置的符號連結。

block_size

快取裝置的塊大小。

btree_cache_size

btree 快取當前使用的記憶體量

bucket_size

Bucket 的大小

cache<0..n>

指向組成此快取集合的每個快取裝置的符號連結。

cache_available_percent

不包含髒資料並且可能用於回寫的快取裝置百分比。 這並不意味著此空間未用於乾淨的快取資料; 未使用的統計資訊(在 priority_stats 中)通常低得多。

clear_stats

清除與此快取關聯的統計資訊

dirty_data

快取中的髒資料量(在垃圾回收執行時更新)。

flash_vol_create

將大小回顯到此檔案(以人類可讀的單位,k/M/G)會建立一個由快取集合支援的精簡配置卷。

io_error_halflife, io_error_limit

這些確定了我們在停用快取之前接受的錯誤數量。 每個錯誤都會按半衰期(以 # ios 計)衰減。 如果衰減的計數達到 io_error_limit,則會寫出髒資料並停用快取。

journal_delay_ms

日誌寫入將延遲最多這麼多的毫秒,除非快取刷新發生得更快。 預設為 100。

root_usage_percent

正在使用的根 btree 節點的百分比。 如果此值過高,則節點將拆分,從而增加樹的深度。

stop

寫入此檔案以關閉快取集合 - 等待直到所有連線的後備裝置都已關閉。

tree_depth

btree 的深度(單節點 btree 的深度為 0)。

unregister

分離所有後備裝置並關閉快取裝置; 如果存在髒資料,它將停用回寫快取並等待重新整理。

Sysfs - 快取集合內部

此目錄還公開了許多內部操作的計時,具有單獨的檔案用於平均持續時間、平均頻率、上次出現時間和最大持續時間:垃圾回收、btree 讀取、btree 節點排序和 btree 拆分。

active_journal_entries

比索引更新的日誌條目的數量。

btree_nodes

btree 中的總節點數。

btree_used_percent

正在使用的 btree 的平均分數。

bset_tree_stats

有關輔助搜尋樹的統計資訊

btree_cache_max_chain

btree 節點快取的雜湊表中最長的鏈

cache_read_races

計算了當從快取中讀取資料時,bucket 被重用並失效的例項 - 即,在讀取完成後,指標已過時。 發生這種情況時,將從後備裝置重新讀取資料。

trigger_gc

寫入此檔案會強制執行垃圾回收。

Sysfs - 快取裝置

可在 /sys/block/<cdev>/bcache 中找到

block_size

最小寫入粒度 - 應與硬體扇區大小匹配。

btree_written

所有 btree 寫入的總和,以(千/兆/吉)位元組計

bucket_size

Bucket 的大小

cache_replacement_policy

可以是 lru、fifo 或 random 之一。

discard

布林值; 如果開啟,則在重用每個 bucket 之前,將向其發出丟棄/TRIM 命令。 預設為關閉,因為 SATA TRIM 是一個非排隊命令(因此速度較慢)。

freelist_percent

空閒列表的大小,以 nbuckets 的百分比表示。 可以寫入以增加保留在空閒列表上的 bucket 數量,從而讓您在執行時人為地減小快取的大小。 主要用於測試目的(即,測試不同大小的快取如何影響您的命中率),但由於 bucket 在移動到空閒列表時會被丟棄,因此還可以透過有效地為其提供更多保留空間來簡化 SSD 的垃圾回收。

io_errors

發生的錯誤數,按 io_error_halflife 衰減。

metadata_written

所有非資料寫入的總和(btree 寫入和所有其他元資料)。

nbuckets

此快取中的總 bucket 數

priority_stats

有關快取中資料最近訪問時間的統計資訊。 這可以揭示您的工作集大小。 Unused 是不包含任何資料的快取的百分比。 Metadata 是 bcache 的元資料開銷。 Average 是快取 bucket 的平均優先順序。 Next 是一個量化列表,其中包含每個優先順序的優先順序閾值。

written

已寫入快取的所有資料的總和; 與 btree_written 進行比較可以得出 bcache 中的寫入膨脹量。