轉自:https://blog.csdn.net/kenianni/article/details/84910638 有改動,僅供個人學習
問題提出:緩存的冷啟動問題
應用系統新版本上線,這時候 redis cluster 集群內存中可能沒有數據的,這時候大量請求進去,會導致大量的高並發請求和流量直接打到mysql 中,完蛋,mysql 掛了,redis cluster 集群中也沒有數據,這時候整個系統就處於不可用狀態;應用系統運行過程中,突然 redis cluster 集群掛了,內存中數據也沒有了,就算開啟了持久化也無法恢復數據,然后集群在故障中重新啟動,這時候全部請求同樣進入mysql,mysql 也搞掛了,系統同樣出於不可用狀態。
從上面兩點看,不管如何,只要redis cluster 集群內存中沒有數據,那么大量請求進來,都有可能導致mysql崩潰,從而系統不可以用。
redis cluster 集群啟動,沒有任何的緩存數據,可以稱之為redis緩存冷啟動。
緩存冷啟動,redis cluster啟動后,沒有任何數據,就直接對外提供服務了,這是mysql 就相當於裸奔狀態。解決冷啟動的方案是數據預熱:
解決方案思路
redis 啟動后,提前給redis 灌入部分數據,然后再給應用提供服務部分數據指的是根據當天具體的訪問情況,進行時時統計出訪問頻率較高的數據(熱數據),因為我們不可能將所有數據寫入redis,數據量大,灌入數據時間消耗長,而且也沒必要熱數據會比較多,這時候我們需要多個服務並行進行讀寫(並行的分布式緩存預熱)完成以上數據預熱,然后提供對外服務,這樣就不會存在redis 冷啟動了,從而減少了大部分數據的 mysql 讀壓力。
舉例場景:存儲游戲玩家的任務數據,游戲服務器啟動時將mysql中玩家的數據同步到redis中。
1 從MySQL中將數據導入到Redis的Hash結構中。當然,最直接的做法就是遍歷MySQL數據,一條一條寫入到Redis中。這樣沒什么錯,但是速度會非常慢。如果能夠想法使得MySQL的查詢輸出數據直接能夠與Redis命令行的輸入數據協議相吻合,可以節省很多消耗和縮短時間。
Mysql數據庫名稱為:GAME_DB, 表結構舉例:
1 CREATE TABLE TABLE_MISSION ( 2 playerId int(11) unsigned NOT NULL, 3 missionList varchar(255) NOT NULL, 4 PRIMARY KEY (playerId) 5 );
Redis中的數據結構使用哈希表:
鍵KEY為mission, 哈希域為mysql中對應的playerId, 哈希值為mysql中對應的missionList。 數據如下:
1 [root@iZ23zcsdouzZ ~]# redis-cli 2 127.0.0.1:6379> hget missions 36598 3 "{\"10001\":{\"status\":1,\"progress\":0},\"10002\":{\"status\":1,\"progress\":0},\"10003\":{\"status\":1,\"progress\":0},\"10004\":{\"status\":1,\"progress\":0}}"
快速同步方法:
新建一個后綴.sql文件:mission.sql內容如下:
1 SELECT CONCAT( 2 "*4\r\n", 3 '$', LENGTH(redis_cmd), '\r\n', 4 redis_cmd, '\r\n', 5 '$', LENGTH(redis_key), '\r\n', 6 redis_key, '\r\n', 7 '$', LENGTH(hkey), '\r\n', 8 hkey, '\r\n', 9 '$', LENGTH(hval), '\r\n', 10 hval, '\r' 11 ) 12 FROM ( 13 SELECT 14 'HSET' as redis_cmd, 15 'missions' AS redis_key, 16 playerId AS hkey, 17 missionList AS hval 18 FROM TABLE_MISSION 19 ) AS t
保存退出,在命令行執行命令(或寫成一個shell腳本):
1 mysql GAME_DB --skip-column-names --raw < mission.sql | redis-cli --pipe
上述命令中參數說明:
1 很重要的mysql參數說明: 2 --raw: 使mysql不轉換字段值中的換行符。 3 --skip-column-names: 使mysql輸出的每行中不包含列名。
數據庫中有7條數據,測試成功輸出:
1 All data transferred. Waiting for the last reply... 2 Last reply received from server. 3 errors: 0, replies: 7
Linux系統終端執行命令后,將mysql數據庫GAME_DB的表TABLE_MISSION數據同步到redis中鍵missions中去。mission.sql文件就是將mysql數據的輸出數據格式和redis的輸入數據格式協議相匹配,從而大大縮短了同步時間。
經過測試,同樣一份數據通過單條取出修改數據格式同步寫入到redis消耗的時間為5min, 使用上面的sql文件和shell命令,同步完數據僅耗時3s左右。
關於以上的補充:
1、mysql配置root用戶免密登錄:
執行命令:
1 mysql GAME_DB --skip-column-names --raw < mission.sql | redis-cli --pipe
需要使得mysql處於免密碼登錄的狀態,否則會報如下的錯:
1 ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
具體的配置mysql免密的方式有常用的2種:
【方法1】
1 cd到root用戶家目錄下 創建 .my.cnf 的文件后vim進行編輯 2 普通用戶則是/home目錄下創建 3 輸入命令 4 vim .my.cnf 5 然后在文件中輸入下面的命令 6 [client] 7 host=localhost 8 user="root" #如是普通用戶輸入用戶名即可 9 password="******" #這里填入你的mysql root(普通用戶)用戶對應的密碼 10 保存退出后 重新啟動MySQL數據庫即可 11 systemctl restart mysqld 12 root用戶直接輸入 mysql即可登陸mysql數據庫 13 普通用戶輸入 show mysql
【方法2】(測試)
1 進入到mysql數據庫的配置文件當中 vim /etc/my.cnf 2 在[mysqld]的段中加上一句:skip-grant-tables 3 例如: 4 [mysqld] 5 datadir=/var/lib/mysql 6 socket=/var/lib/mysql/mysql.sock 7 skip-name-resolve 8 skip-grant-tables 9 保存並且退出vim 10 重新啟動mysql數據庫即可
2、可以使用命令而非免密登錄的方式執行shell命令:
1 mysql -h host -uroot -p123456 test --default-character-set=utf8 --skip-column-names --raw < /usr/redis/order.sql | /usr/redis/redis-cli -h host -p 6379 -a 123456 --pipe 2 3 #-h host -uroot -p123456 test 分別為:mysql遠程地址,用戶名,密碼,數據庫名 4 #/usr/redis/order.sql | /usr/redis/redis-cli 分別為sql文件和redis客戶端文件目錄的地址 5 #-h host -p 6379 -a 123456 分別為redis遠程地址,端口,密碼
或者
1 mysql -uroot -proot -Dxfdb --default-character-set=utf8 --skip-column-names --raw < mysql_to_redis.sql | redis-cli -h 127.0.0.1 --pipe
3、編寫sql腳本的編碼問題:
如果直接復制上述sql腳本中的內容到linux終端的vim中,可能會報錯,如下:
ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'oSELECT CONCAT( "*4\r\n", '$', LENGTH(redis_cmd), '\r\n', redis_cmd, '\r\n' at line 1 All data transferred. Waiting for the last reply... Last reply received from server. errors: 0, replies: 0
此時,可以先將sql腳本內容復制到文本編輯器中(例如sublime),然后再復制到vim中。