在關聯式資料庫剛發展的年代,資料庫儲存資料的重要媒介 — — 硬碟,它的價格非常的高。也因此,舊時代關聯式資料庫的設計沒有例外,每一家廠商在儲存資料到硬碟上時,都把資料庫設計成 mutable database 。
什麼是 mutable database 呢?比方說,我在資料庫建立了一張 table ,然後,插入兩個 row ,並且修改了其中一個 row ,總共做了 3 個操作。過了兩天,我想取消最後一個操作。資料庫表示:「不好意思,我提供的 API 裡,並沒有回到過去、或是取消操作這一類的設計。」
相對於 mutable database 的設計則是 immutable database 。同樣的需求,如果是交給 immutable database 的話,它則是輕易地達成。
mutable database 的底層所儲存的資料,是過去到現在所有的操作所建構的結果,是當下的狀態。所以,資料庫的狀態,它只能前進,不能後退。
另一方面,immutable database 的底層,儲存的資料,則是過去到現在所有的操作本身,換言之,它所記錄的,不只是某個時刻的狀態,而是一連串足以推導出任意時刻資料庫狀態的日誌 — — 事件溯源 (event sourcing) 的 immutable log。
這個 mutable database 的特性,在舊時代因為相對節省硬碟空間,是很有道理的設計。然而,在新時代,因為硬碟已經不再是最重要的限制因素了,我們應該來檢驗,為了這個設計,付出了什麼代價。
概念很簡單啊,去玩一次 write only db 就知道這篇的意思了,建議去玩 clickhouse 然後不用 ALTER
指令(該 DB 內 delete / update 都是 ALTER 系列)玩過一次保證毀三觀 XDDD
簡單的來說,你的 DB 內如果有 UPDATE 和 DELE
TE 後你如何能保證資料的正確性?不如全用 INSERT 配 version column 還能做所有的溯源處理,而你要取得最新資料就拿最後的 version 即可
clickhouse 還有很多 write only 的 DB 的概念就是在寫一個超大的 csv 檔,所以你 create table 的時候就要指定 order by,partition 也需指定因為能自動改檔名,不希望 delete 因為檔案大小需折合變動,增加 version 也是不希望你做 update
然後你需要不同的 order by 時就自己幹 index 寫入另外一個表來做 relation ... 就能用寫入來完成所有的問題 XDD,anyway 如果有用 redis 甚至 kv only 的 rocksdb 自幹 index 甚至很多事情後,這種看似限制的 DB 其實說不定能讓你取得更多自由才是?單純要寫更多的 code 來維持而已
玩完 write only DB 後就開始反思,原本 DB schems 設計內,很常增加類似 delete_at 來禁止 DELETE,卻不用 version column 來禁止 UPDATE,最終無法溯源時 ... 最初的一票書籍和文章,是不是弄錯了些什麼 ...
所以總結這篇,mutable = 省硬碟可 UPDATE 與 DELETE,immutable = 用 INSERT 配 version / delete_at 解決所有問題,然後再去看原先那篇應該就知道在講啥了,先毀三觀可能有助於看清問題的本質哈哈
文中有談到讀寫分離,如果你能保證 DB 某個 table 如同 csv 檔只增不減,那就沒 lock 與其他任何問題,只剩後面資料是否有 sync 到最後對唄?所有事情就變得異常簡單 ...
事實上類似的玩法很早就在傳統資料庫中進行了,事件寫入先進binary log,後端table慢慢同步,crash也是binary log來修復/回溯,也同樣能做到回溯指定時間點,以前玩oracle的時候被搞得好痛苦XD
sakura26eq: 這概念和 bin log 完全不同喔,bin log 只能做操作記錄溯源,但基本上你不可能留存所有操作記錄,and 逆推也太麻煩了
一個是資料面的所有歷史保留,只增不減,一個是修改的操作記錄保留哩
jokercatz: 無法留存所有操作記錄確實是我之前被搞得很慘的原因,binary log成長速度快到驚人,所以會有定時rotate的設計,我是挺好奇這邊的immutable database是怎麼在不映射數據回原始table的情況下控制大小的?
sakura26eq: 簡單的來說一個一百萬資料的 table 在 binlog 被拋棄時,你怎麼證明 id 10 的 row 的某個 column 沒有被 update 過?write only 的 db 每次 write 都是整個 row base 的覆蓋,每次覆蓋 version + 1 你可以 select 出該 row 的所有 version 在任何時候
其次 write only db 的 partition 就異常重要,因為要省硬碟的話就一次砍一整個 partition 所以 partition 可以視為檔案名稱(很多也直接這樣做 XDD)
jokercatz: 還是不太懂,傳統DB所有操作都會映射回原始表,映射完的部分才會被rotate拋棄,write only db就算是row base update也只是讓他的log體積變大,沒有其他幫助啊
至於砍一次一partition在現在多數DB也有這樣的設計了
而且沒有映射回原始表代表當我要搜尋一筆數據時,我得掃描整份log找到最後的修改,如果這份表的存取是「越少修改的越頻繁」,那效能應該會有所衝擊才是
簡單的來說他的 data 就是 log 了啊 XDD,沒其他問題哩,所以 clickhouse 速度基本上和 redis 能非常接近,還能打敗 leveldb 超神
clickhouse 有做稀疏 index 不過這邊就是各家不同的幹法了
基本上上 index 都是在維持 tree 而已,這邊提的會是最原始的資料形態
jokercatz: 我之前做的那套產品ParseMe也是log based, 純log要找指定數據掃描跑不掉,index還是必要的。我的應用場景大多是循序輸出所以還好,但是對DB隨機存取來說,是不太可能在log型態跑得像radis的
倒是有可能他是把原始表映射放在記憶體裡面,這樣就說得通為什麼速度跟redis接近了
我們的另一套產品就是記憶體資料庫,那個真的吃記憶體怪獸,出給客戶的機器RAM都是幾百G在算XD
sakura26eq: 我一開始有說它建 table 的時候就要建 order by 了,所以實體資料面都是排序好的,index 只有說該 index 在類似第幾行,甚至不做緊密 index 我記得 8xx 才記一個,之後就快速跳行的檔案操作,所以爆快
如果你 8xx 行才一個 index 這代表你插入後 index tree 修改量能大幅下降對唄 XDD
jokercatz: 噢,標準noSQL作法,但是這對於會持續成長的log不適用吧?預先佈置的index代表每個index之間的空間是固定的,會有寫滿/重新index的可能性
比較的東西不多,但後面都不敢比 mysql 而是直接比類似 redis 因為速度和容量都差太多了 ...
好啦這篇其實我在推 clickhouse 哈哈,我記得我第一次測試時以為自己眼花 ...... 不過限制也非常多就是了,一樣的先毀三觀 ...
sakura26eq: 這種 db 最適合的就是 log 類,因為 log 就是只增不減,還能把 storage 換成 s3 呦(local 不留檔)其餘砍檔剛剛都有說了哩,anyway 自己先試試如何?它有本簡體書還不錯可以入手看看就是
看起來場景也不一樣 沒什麼告別不告別的...
就是能不能建snapshot, log structured....