塊層快取 (bcache)¶
假設你有一個大型的、速度慢的 raid 6,以及一到三個 ssd。 如果你能將它們用作快取,那豈不是很好……因此有了 bcache。
- bcache wiki 可以在以下網址找到:
- 這是 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 處的資料無效(即,我們對繞過快取的寫入執行的操作相同)
對於回寫寫入,我們目前將該錯誤傳遞迴檔案系統/使用者空間。 這可以改進 - 我們可以將其作為跳過快取的寫入重試,這樣我們就不必返回寫入錯誤。
當我們分離時,我們首先嚐試重新整理所有髒資料(如果我們以回寫模式執行)。 但是,如果它無法讀取某些髒資料,則目前不會執行任何智慧操作。
操作方法/食譜¶
使用丟失的快取裝置啟動 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
請注意,如果您以回寫模式執行,這可能會導致資料丟失。
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
損壞的 bcache 在設備註冊時導致核心崩潰
這永遠不應該發生。 如果確實發生,那麼您就發現了一個錯誤! 請將其報告給 bcache 開發列表:linux-bcache@vger.kernel.org
請務必提供儘可能多的資訊,包括核心 dmesg 輸出(如果可用),以便我們提供幫助。
在沒有 bcache 的情況下恢復資料
如果核心中沒有 bcache,則後備裝置上的檔案系統仍然可在 8KiB 偏移處使用。 因此,可以透過使用 --offset 8K 建立的後備裝置的 loopdev,或者在最初使用 bcache make 格式化 bcache 時由 --data-offset 定義的任何值。
例如
losetup -o 8192 /dev/loop0 /dev/your_bcache_backing_dev
這應該在 /dev/loop0 中呈現您未修改的後備裝置資料
如果您的快取處於直寫模式,則可以安全地丟棄快取裝置而不會丟失資料。
擦除快取裝置
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
刪除或替換快取裝置
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
dm-crypt 和 bcache
首先設定未加密的 bcache,然後在 /dev/bcache<N> 上安裝 dmcrypt。 這將比您對後備裝置和快取裝置都進行 dmcrypt,然後在上面安裝 bcache 速度更快。[基準測試?]
停止/釋放已註冊的 bcache 以進行擦除和/或重新建立
假設您需要釋放所有 bcache 引用,以便您可以執行 fdisk 並重新註冊已更改的分割槽表,如果其上還有任何活動的後備或快取裝置,這將不起作用
它是否存在於 /dev/bcache* 中? (有時不會)
如果是,那就很容易了
host:/sys/block/bcache0/bcache# echo 1 > stop
但是如果您的後備裝置消失了,這將不起作用
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 是較低的裝置。
在其他情況下,您也可以在 /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 中的寫入膨脹量。