基於記憶體的關聯式資料庫memsql初探

longbozhan發表於2021-12-27
  1. 背景

    1. 廣告系統中,演算法模型預估需要根據廣告的實時轉化統計結果,才能做出更精準的預估;同時,支援多維度聚合查詢(例如按照廣告各個不同層級維度,按照時間不同粒度的維度),並跨大區合併。一開始的版本是基於mysql,但由於統計資料更新太頻繁,非同步定期從mysql撈統計結果,導致mysql經常出現活躍連線數太多而頻頻出現告警。
    2. 嘗試過很多優化:
      1. 加資料接入層:中間加一層mysql代理服務data access,快取mysql的統計結果,所有的讀請求都從data access獲取,可以減少mysql的讀壓力(也可以從mysql從庫讀,減少master節點壓力,只是沒去嘗試)
      2. 減少資料量:刪除歷史的統計資料,只保留業務需要的時間視窗的統計結果
      3. 減少代理的訪問頻率:即使加了代理(2個節點),但由於mysql需要同步的表數量太多(接近200個),每個表的資料也達到了百萬級別,頻繁訪問也還是會增加mysql的負擔,後面不得已採取犧牲統計的時效性,降低對mysql的訪問頻率
      4. 加入redis作為統計寫入的快取佇列:前面提到的基本都是針對讀的優化,實時統計的寫入量很大,如果沒有先聚合,拿到一條就寫入一次,一樣會導致mysql負載過高。所以在服務中先聚合後再寫入,但是,由於聚合是在記憶體的,要是服務上線重啟,臨時聚合的資料來不及寫入mysql,又會導致資料丟失。。所以採取了寫入redis的方式,redis作為快取佇列,儲存一天內具體到每一分鐘的統計結果,統計程式定期的從redis中撈取結果,再聚合到5分鐘,1個小時,1天,1周的維度。可能這裡有人會問,為什麼不用kafka或者其他主流mq作為快取佇列,由於需要從其他大區把資料撈出來聚合後一起做統計,這個過程可能會由於物理距離原因而超時,導致統計不準,把消費事務設定為 Exactly Once,跨區消費完了後需要傳送確認訊息,這個延遲比較大。而且,即使能在一次統計中統計正確,但如果前面有其他的統計出現錯誤,會導致當天的統計一直錯下去,沒法補資料。如果用redis,只要資料未過期,可以對之前統計的結果重新統計後覆蓋,雖然會犧牲一點redis的qps,但redis支援的qps能達到15w,本身效能就很好,所以不是什麼大問題。結果就變成了定期的的insert into xx on duplicate xx
    3. 結果
      1. 一系列優化之後,mysql不再告警了,然而,維護的複雜度也變高了,如果需要增加一個維度,redis、data access、上層應用服務都要改一遍,改起來相當麻煩,而且,維度多,也會導致統計的壓力變大。
    4. 主角出場
      1. 這陣子在看《資料密集型應用系統設計》,作者是少有的從工業界幹到學術界的牛人,書中提到了基於記憶體的關聯式資料庫memsql,因此做了簡單的調研。
  2. memsql簡介

    1. 基於記憶體,宣稱是世界上最快的關聯式資料庫,為了避免服務重啟後丟資料,資料定期寫磁碟,這點類似redis
    2. 支援橫向和縱向擴充套件:縱向使得單機效能更好,橫向支援水平擴充套件
    3. sql解析:sql預編譯到C++中,這就要求sql語句不要經常變化,好處就是常用的語句少了解析部分,執行會比較快
    4. ACID:不是full ACID,A:只對單條語句;I:只支援read commited;D:不是每個事務都持久化。C是目標,AID有缺陷,C也在某種程度下不能完全達到
    5. 效能:官方有做測評,但被一個facebook的員工吐槽了(https://dom.as/2012/06/26/memsql-rage/,知乎有人對觀點做了總結:https://zhuanlan.zhihu.com/p/49159963),大概意思就是拿mysql和memsql的不對等的引數做基準測試對比,沒有意義。例如:
      1. mysql可以設定 innodb_buffer_pool_size大小,如果設定太小,會導致快取命中率變低。
      2. mysql設定事務落磁碟的頻率: innodb_flush_log_at_trx_commit,頻率太高也會影響效能
      3. 最坑的是:由於memsql是以skiplist存的,如果要order by獲取pk最大的元素(定義表是asc),則需要排序,這在mysql中不需要排序。不過,這個問題貌似後來已經修復了(見知乎的評論:)
    6. 高可用:支援雙機備份
    7. 有人說,如果把mysql的表的儲存引擎設定為memory,而且設定觸發器寫磁碟,不就可以代替memsql了嗎?於是有人正對innodb、memory、memsql的效能做了測評,請看以下的測評報告。
    8. 效能測評:
      1. 直接說結論:在增刪改查都執行的情況下,qps分別是:
        資料庫/引擎 mysql innodb mysql memory memsql
        qps 750 10k 50k
        如果僅僅是追求效能,犧牲部分ACID特性,還是可以試試。
      2. 測評過程錄製成youtube視訊:https://youtu.be/zKh8CsgF1OQ
    9. 但是,這玩意不開源,只有受限制的開發版本(叢集總記憶體不能超過128G)和全功能的商業版本,不開源的情況下,如果使用的不夠普遍,而且對其原理不瞭解的情況下,在生產環境還是不太敢使用。
  3. memsql體驗

    1. docker映象自帶基準測試,在開10個併發的情況下,寫入qps可以達到140w(當然這個基準測試也是比較水,表欄位只有2個)
  4. 總結

    1. 在滿足基本功能的情況下追求效能,memsql確實是不錯的選擇;但是如果對其原理不瞭解的情況下,在生產環境使用,容易踩坑。
  5. 其他記憶體資料庫

    1.  VoltDB,也是基於記憶體的關聯式資料庫,也是有收費的商業版和開源版,java寫的,特性待調研。
  6. 相關資料

    1. facebook工程師吐槽memsql:https://dom.as/2012/06/26/memsql-rage/

相關文章