1、寫入數據量增加時,如何實現分庫分表?
數據使用單表存儲,但是隨着數據的增大,造成數據庫查詢、寫入性能下降,數據庫磁盤空間也發生報警,所以要解決這個問題,使得系統可以正常運轉下去。
1.1、需要考慮的問題
- 系統正在持續不斷的發展,數據庫中存儲的數據也越來越多,單個表的數據量超過了千萬甚至億級別。這時即使使用了索引,索引佔用的空間也隨着數據量的增長而增大,數據庫就無法緩存全量的索引信息,那麼就需要從磁盤上讀取索引數據,就會影響查詢性能了,需要提升查詢性能
- 數據量的增加也佔據了磁盤空間,數據庫在備份和恢復的時間變長,如何讓數據庫系統支持如此大的數據量
- 不同模塊的數據(比如用戶數據和用戶關係數據)全部存儲在一個主庫中,如果主庫發生故障,所有的模塊都會受到影響,如何做到不同模塊的故障隔離
- 4 核 8G 的雲服務器上的MySQL 5.7 大概可以支撐 500 TPS 和 10000QPS,鎖着系統下入請求量的增長,系統如何處理更高的併發寫入請求
數據庫的寫入請求量大造成的性能和可用性方面的問題,要解決這些問題,你所採取的措施就是對數據進行分片。這樣可以很好地分攤數據庫的讀寫壓力,也可以突破單機的存儲瓶頸,而常見的一種方式是對數據庫做“分庫分表”
2、分庫分表
將數據進行分片,基本思想是依照某一種策略將數據儘量平均分配到多個數據庫節點或者多個表中。
- 垂直拆分
- 水平拆分
2.1、如何對數據庫做垂直拆分
垂直拆分,對數據庫豎着拆分,也就是將數據的表拆分到多個不同的數據庫中。
垂直拆分的原則一般是按照業務類型來拆分,核心思想是專庫專用,將業務耦合度比較高的表拆分到單獨的庫中。
2.2、如何對數據庫做水平拆分
水平拆分指的是將單一數據表按照某一個規則拆分到多個數據庫或者多個數據表中。
拆分的規則
1、按照某一個字段的哈希值做拆分,這種拆分規則比較適合用於實體表,比如用戶表中,我們根據 ID 字段進行拆分。比如將用戶表拆分成3個表,先對用戶 ID 做哈希,哈希的目的地將 ID 儘量打散,然後再對 3 取餘,這用就得到了分表後的索引值。
2、按照某一個字段的區間來進行拆分,比較常用的是時間字段,比如可以把一個月的數據放入到一張表中,這樣在查詢時就可以根據創建時間先來定位數據存儲在哪個表中,再按照條件來進行查詢。
2.3、分庫分表引入了一些問題
問題一:查詢時每次都要帶分區鍵
問題描述:
引入分庫分表鍵,也叫做分區鍵。也就是我們對數據庫做分庫分表所依賴的字段。這樣也就是說,所有的查詢都需要帶上這個字段,才能找到數據所在的庫和表,否則就只能向所有的數據庫和數據表發送查詢命令。
問題解決:
比如,在用戶庫中我們使用 ID 作爲分區鍵,這時如果需要按照暱稱來查詢用戶時,你可以按照暱稱作爲分區鍵再做一次拆分,但是這樣會極大地增加存儲成本,如果以後我們還需要按照註冊時間來查詢時要怎麼辦呢,再做一次拆分嗎?
所以最合適的思路是你要建立一個暱稱和 ID 的映射表,在查詢的時候要先通過暱稱查詢到 ID,再通過 ID 查詢完整的數據,這個表也可以是分庫分表的,也需要佔用一定的存儲空間,但是因爲表中只有兩個字段,所以相比重新做一次拆分還是會節省不少的空間的。
問題二:一些數據庫的特性在實現是時可能變得困難。
問題描述:
多表的 JOIN 在單庫時是可以通過一個 SQL 語句完成的,但是拆分到多個數據庫之後就無法跨庫執行 SQL 了。未分庫分表之前查詢數據總數時只需要在 SQL 中執行 count() 即可,現在數據被分散到多個庫表中如何解決。
問題解決:
好在我們對於 JOIN 的需求不高,即使有也一般是把兩個表的數據取出後在業務代碼裏面做篩選,複雜是有一些,不過是可以實現的。現在數據被分散到多個庫表中,我們可能要考慮其他的方案,比方說將計數的數據單獨存儲在一張表中或者記錄在 Redis 裏。