记一种数据库水平扩展的技巧
起因
這段時間我們游戲在持續(xù)推廣,每天數(shù)萬的玩家注冊,服務器壓力增加得很快。雖然我們服務器進程可以多開,redis用的是集群版,也可以水平擴展,但數(shù)據(jù)庫不是,一直是只有一個數(shù)據(jù)庫在支撐。即便大部分玩家數(shù)據(jù)都存放在redis中,只有redis中的數(shù)據(jù)過期后才會去請求數(shù)據(jù)庫進行查詢,但一樣頂不住人多,現(xiàn)在每天日活躍玩家將近20萬,同時在線接近2萬,數(shù)據(jù)庫壓力還是很大,所以擴展成多個數(shù)據(jù)庫支持是唯一選擇
思考
原來只有一個數(shù)據(jù)庫,現(xiàn)在要擴展多個數(shù)據(jù)庫,把新玩家哈希到不同的數(shù)據(jù)庫上,同時還要兼容原來的老玩家,依然能正確讀取,更新其舊數(shù)據(jù),不能出錯
第一種方法
再買2個數(shù)據(jù)庫(當然也可以買多個),加上原來的數(shù)據(jù)庫,這樣一共有3個數(shù)據(jù)庫,新注冊的玩家根據(jù)playerID對3進行取余,這樣把新玩家的數(shù)據(jù)均勻地哈希到3個數(shù)據(jù)庫上,以做負載均衡。當讀取玩家數(shù)據(jù)時,按照playerID對3進行取余的算法,得到數(shù)據(jù)庫ID,先嘗試從這個數(shù)據(jù)庫里面讀取,如果讀取不到,說明是舊玩家,那么再到舊數(shù)據(jù)庫里面讀取。修改更麻煩一些,因為要先判斷在哪個數(shù)據(jù)庫上,然后再進行更新。
看似也沒什么問題,既能哈希式地存儲,也能哈希式地讀取
請仔細想一下,這種方法有什么問題嗎?
如果后面數(shù)據(jù)庫壓力越來越大,3個數(shù)據(jù)庫也不夠了怎么辦呢?只能再買幾個數(shù)據(jù)庫,比如5個,這樣一共有8個數(shù)據(jù)庫,進行新的哈希算法,新注冊的玩家根據(jù)playerID對8進行取余,把新玩家的數(shù)據(jù)均勻地哈希到8個數(shù)據(jù)庫上。讀取的時候就麻煩了,因為有三類不同的玩家了,先對8取余得到一個數(shù)據(jù)庫進行查詢,沒有的話再對3進行取余進行查詢,仍然沒有的話再到最舊的數(shù)據(jù)庫上進行查詢。讀取更新玩家數(shù)據(jù)的話就更痛苦了,要進行多次哈希算法庫判斷到底在哪個數(shù)據(jù)庫上,找到后才能進行更新,數(shù)據(jù)庫操作需要多次
看到了吧,每擴展一次數(shù)據(jù)庫,讀取玩家數(shù)據(jù)的時候就要多嘗試一次DB
想一想,讀取,更新玩家數(shù)據(jù)庫的地方有很多,每次都這么麻煩的判斷,累也累死了
(有個優(yōu)化點,我們可以記錄下來分界點玩家ID,歸于哪個時間段生成的,在這個時間段內(nèi)的玩家都是用一種算法,比如playerID在1~M之間的玩家直接到舊的數(shù)據(jù)庫上查詢,playerID在M~N之間的玩家直接對3進行取余后到對應的數(shù)據(jù)庫上進行操作,playerID在N之后的玩家對8進行取余后到對應的數(shù)據(jù)庫上進行操作。這個M,N可以提前在代碼里面定好,玩家注冊的時候也根據(jù)M,N進行不同的哈希算法。但是缺點也有,那就是M,N的預估不能出錯,而且沒法測試,一旦啟用了這個規(guī)則,玩家數(shù)據(jù)就會被自動歸類,出錯就完蛋了。而且再擴展一次數(shù)據(jù)庫的話要在所有選擇數(shù)據(jù)庫的地方修改一遍,吐血到死)
問題在哪里呢?
答案:擴展數(shù)據(jù)后,讀取更新玩家數(shù)據(jù)時的算法嚴重依賴生成數(shù)據(jù)時的算法,二者必須一樣也就是高耦合,因為有舊數(shù)據(jù),只能多次哈希后判斷在不在當前數(shù)據(jù)庫上
所以我們要找到一個讀取更新數(shù)據(jù)時,不依賴于生成數(shù)據(jù)時算法的辦法,也就是下面方法2
第二種方法
再買N個數(shù)據(jù)庫,比如2個,加上原來的一個舊數(shù)據(jù)庫,一共3個。仍然采用對3取余的哈希辦法,把新玩家均衡的撒在3個數(shù)據(jù)庫上,但是同時記錄下新玩家所在的數(shù)據(jù)庫號,建議記錄在redis中,這樣最高效。讀取更新玩家數(shù)據(jù)時,先從redis中查到這個玩家所在的數(shù)據(jù)庫號,再到對應的數(shù)據(jù)庫上進行操作,如果redis中不存在,說明是舊玩家那就到舊數(shù)據(jù)庫中。由于redis的效率超高,每秒達到10萬級別,相對于數(shù)據(jù)庫的操作,這點兒耗時可以說忽略不計了
這個辦法有
優(yōu)點
1.?查詢更新玩家數(shù)據(jù)時的算法永不需改變,只用先從redis中查找所在數(shù)據(jù)庫號,再到對應的數(shù)據(jù)庫操作即可,永遠只有一次數(shù)據(jù)庫操作。redis中查詢不到說明是當初沒有記,直接到舊數(shù)據(jù)庫查詢即可
2.?代碼改動比較小,只需新玩家記錄下數(shù)據(jù)庫ID號,讀取更新數(shù)據(jù)庫時根據(jù)redis的值選擇對應的數(shù)據(jù)庫連接即可
3.?以后再擴展數(shù)據(jù)庫的時候,只需更改玩家注冊時的哈希算法。讀取,更新時找數(shù)據(jù)庫的算法不需更新
缺點
1. 需要額外引入redis,且這些數(shù)據(jù)在redis中永久存在
2. 若redis出問題導致數(shù)據(jù)丟了,那這些玩家就找不到準確的歸屬數(shù)據(jù)庫了。不過現(xiàn)在云redis的集群模式下,數(shù)據(jù)安全是有保證的。如果你仍然不放心,也可以先到數(shù)據(jù)庫中保存一份玩家的歸屬數(shù)據(jù)庫ID,再加載到redis中做長期保存,這樣更保險,即使redis中丟了,再到數(shù)據(jù)庫中查詢導入即可。由于我們絕大多數(shù)都是查詢redis,數(shù)據(jù)庫里面只是用來做備份保險用,性能不用擔心
總結(jié)
以上是生活随笔為你收集整理的记一种数据库水平扩展的技巧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis会遇到的15个「坑」,你踩过几
- 下一篇: centos安装mysql8.0