優化需要 100% ACID 兼容但可以容忍持久性延遲的查詢
介紹
我正在用 C++ 編寫一個交易程序。該計劃涉及擁有使用者餘額和訂單簿。訂單簿基本上是一組買賣訂單。送出訂單時,比如買單,匹配引擎會搜尋是否有該價格或更低價格的賣單。如果是這樣,交易就會發生。一筆交易減去較小訂單的數量,剩餘的數量將添加到下一個買家/賣家的訂單簿中。
上下文
當使用者送出訂單時,C++ 程序:
- 讀取數據庫以查看使用者是否有足夠的餘額。
- 執行記憶體交易(在程序中)
- 修改數據庫,在一個事務中,它讀取餘額,讀取訂單簿,修改它們並將它們持久化 + 在數據庫中寫入日誌條目(2 個條目)。
如您所見,對於交易程序,原子性和一致性是零容忍的。否則會出現雙花。
但是,我可以容忍在數據庫上僅使用一層記憶體,這將確保一致性和原子性(通過程式),但會推遲持久性。
我正在為我的程序使用 PostgreSQL。此外,我對數據庫的所有訪問都使用索引。從預設值更改索引算法只會惡化性能。那裡幾乎沒有標準化。我所做的唯一規範化是將餘額表與使用者表分開。其他一切都是不可分離的。
問題
它很慢。在 Intel 4930k(8 核,16 執行緒,超頻至 4.5 GHz)上,我幾乎不能每秒處理 100 個事務,而且我至少需要每秒處理 1000 個事務。
我不確定問題出在哪裡,但我聽說數據庫不太擅長修改數據。那麼,如何在不危及 ACID 合規性的情況下優化該系統呢?
我的解決方案,我想問
我想在數據庫上實現一個軟體層,這是唯一允許直接訪問數據庫的層。該層會將數據庫記憶體在記憶體中,並定期將所有更改刷新到數據庫中以保持持久性。但是這樣做讓我覺得我正在做數據庫應該做的事情。
這是常見的事情嗎?我是否在計劃正確的解決方案?
評論編輯:
我可以承受失去僅在記憶體中的交易。原因是任何時間點的交易都應該發生或不發生。不管它發生在某個時刻還是在那之後的一秒鐘。我的問題是檢查以這種方式記憶體是否是有效的解決方案以及是否值得。這似乎是一個有效的解決方案。我只是不確定它可能有多大幫助以及為什麼數據庫不這樣做(或者無法配置這樣做)?
顯然我誤解了上面的規範化這個詞。我將規範化理解為“拆分錶格以使它們更有意義”,因此對於性能而言,規範化更糟糕(再次,我可能誤解了它)。唯一被拆分的表是我上面提到的那個,所以不是寫入 4 個表,而是寫入 4 個表並讀取 users 表。這就是為什麼我不太相信拆分該表是原因。但也許我必須重新審視設計。我不是那裡的專家。
我不確定我是否可以提供 DML,因為我使用ODB 作為 ORM。我得看看能不能把它們提取出來。當我到達系統所在位置時(幾個小時後),我會盡快提供 DDL 語句。
使用 ORM 是否意味著沒有希望?如果我失去 20-30% 的性能,我可以忍受它(現在)。與手動編寫查詢相比,我是否應該期望性能損失 10 倍(從 1000 tps 到 100)?顯得過分了。
pgbench -t 10000
在單個執行緒上產生 2350 個事務/秒(我的應用程序使用單個執行緒,因為每個訂單簿都可以採用單個執行緒,這很好)。編輯2:
我選擇了下面的答案,因為它實現了我正在尋找的第二層解決方案。但是我仍然沒有最好的每秒事務數,這意味著我仍然必須研究我擁有的模式和操作。
您的問題似乎在邏輯上不一致,因為容忍持久性的延遲和“實際上我可以承受失去僅在記憶體中的事務”的定義違反了“ACID”中的“D”。
如果您真的只想符合“ACI”,可以關閉“synchronous_commit”。
請注意,這意味著您可以向交易者報告他們的交易已執行,但後來發現,哎呀,實際上並沒有。它仍然是原子的和一致的,因為交易的雙方和現金餘額的兩個變化都將一起消失。但這仍然可能是糟糕的客戶體驗。
如果您決定不能在“synchronous_commit”關閉的情況下執行,您至少應該在關閉它的情況下進行測試。測試的結果將表明您應該如何進行進一步優化。