歡迎光臨
每天分享高質量文章

容器中的MySQL遷移RDS,會話卻“爆了”

事件起源

整個事件的起源還要從筆者最近入職了一家區塊鏈金融公司來說起(為了保密性,不便透露公司名字),公司業務發展比較迅猛,突破百萬使用者也是近在眼前。整個系統都在阿裡雲上執行,每天都能看到使用者的不斷增長,即興奮又擔憂,為什麼這麼說呢?
由於筆者過來的時候這裡業務就已經上線了,系統接過來之後,快速瞭解了所有的應用服務都是在Docker Swarm跑起來的,也包括MySQL資料庫,以至於筆者就有了遷庫的想法,按照這種使用者量發展下去,MySQL在容器中執行用不了多久肯定會撐不住。
筆者就開始隱隱的擔憂起來,畢竟不想每天提心吊膽的做運維。所以立馬重新規劃了新的方案和大家一起探討,最終總監和相關技術負責人都敲定用RDS做為資料庫新的方案,周星馳的功夫中也說道過:“天下武功,唯快不破”。就立馬開始動身幹起來。


遷移計劃

原架構圖

分析架構圖:
  1. 從入口層(CDN)—> 到安全層(WAF) —> 最後到達應用層 (ECS叢集);

  2. Docker Swarm打通了ECS叢集中的每臺伺服器,在每臺ECS宿主機安裝Docker engine並部署了公司需要的應用服務和資料庫(Nginx、PHP、Redis、MySQL等);

  3. MySQL容器透過宿主機的本地(目錄)掛載到容器中實現資料持久化;

  4. 業務專案以PHP為主,PHP也是執行在容器中,透過PhP指定的配置檔案連線到MySQL容器中。

筆者就隨便展示一下其中一個庫的yaml檔案,比較簡單:

version: "3"
services:
ussbao:
# replace username/repo:tag with your name and image details
image: 隱藏此映象資訊
deploy:
 replicas: 1
 restart_policy:
   condition: on-failure
environment:
 MYSQL_ROOT_PASSWORD: 隱藏此資訊
volumes:
 - "/data//mysql/db1/:/var/lib/mysql/"
 - "/etc/localtime:/etc/localtime"
 - "/etc/timezone:/etc/timezone"
networks:
default:
external:
 name: 隱藏此資訊
從上面的資訊可以看出來,每個庫只運行了一個MySQL容器,並沒有主從或讀寫分離的方案。而且也沒有對資料庫進行做任何最佳化,資料庫這樣跑下去讓筆者很擔憂,正常來說,都會把資料庫獨立部署執行。
調整後架構圖

從上圖可以看出來,筆者只是把MySQL獨立出來了,開通RDS實體來跑資料庫,當然還開通了其它的一些服務(比如OSS、雲Redis等),這些不是本文的重點,就沒有畫出來。Nginx和PHP服務還是在Docker Swarm中執行。本文只是對遷移後出了問題的庫進行分享,下麵來看看遷移的方案吧。
遷移流程方案
開通RDS實體 —> 備份SQL —-> 匯入到RDS —> 修改資料庫配置檔案 —>測試驗證
  1. 根據業務量規劃開通RDS實體,建立資料庫和使用者;

  2. 提前做好RDS白名單,新增允許訪問RDS的IP地址;

  3. mysqldump備份Docker中的MySQL;

  4. 把備份好的.sql檔案匯入到RDS中;

  5. 修改PHP專案的資料庫配置檔案;

  6. 清空PHP專案的快取檔案或目錄;

  7. 測試驗證;

  8. RDS定時備份;

具體遷移細節就不展示了,筆者是在夜深人靜的時候進行遷移操作的,確定大半夜沒人訪問我們的APP和網站了才開乾的。
我們的業務情況還有點像股市,我們是晚上12點不許操作和交易,第2天早上9點開盤,9點鐘是併發的高峰期,就像朝陽大悅城上午開門一樣,大批的顧客同時併發過來了。
所以那天晚上在12點15分準時開乾,按計劃和提前準備的配置、命令、指令碼進行操作的。把Docker中執行的MySQL遷移到RDS上非常順利,好幾個庫遷移不到半個小時就結束了,並且把網站和APP的流程都跑了一遍,也都是妥妥的,最終把提前準備好備份指令碼放在crontab中定時執行,可以來看下指令碼內容:
#!/bin/bash
資料庫IP
dbserver='*******'
資料庫使用者名稱
dbuser='ganbing'
資料庫密碼
dbpasswd='************'
備份資料庫,多個庫用空格隔開
dbname='db1 db2 db3'
備份時間
backtime=`date +%Y%m%d%H%M`
out_time=`date +%Y%m%d%H%M%S`
備份輸出路徑
backpath='/data/backup/mysql/'
logpath=''/data/backup/logs/'
echo "################## ${backtime} #############################"
echo "開始備份"
日誌記錄頭部
echo "" >> ${logpath}/${dbname}_back.log
echo "-------------------------------------------------" >> ${logpath}/${dbname}_back.log
echo "備份時間為${backtime},備份資料庫 ${dbname} 開始" >> ${logpath}/${dbname}_back.log
正式備份資料庫
for DB in $dbname; do
source=`/usr/bin/mysqldump  -h ${dbserver} -u ${dbuser} -p${dbpasswd} ${DB} > ${backpath}/${DB}-${out_time}.sql` 2>> ${backpath}/mysqlback.log;
#備份成功以下操作
if [ "$?" == 0 ];then
cd $backpath
#為節約硬碟空間,將資料庫壓縮
tar zcf ${DB}-${backtime}.tar.gz ${DB}-${backtime}.sql > /dev/null
#刪除原始檔案,只留壓縮後檔案
rm -f ${DB}-${backtime}.sql
#刪除15天前備份,也就是隻儲存15天內的備份
find $backpath -name "*.tar.gz" -type f -mtime +15 -exec rm -rf {} \; > /dev/null 2>&1
echo "資料庫 ${dbname} 備份成功!!" >> ${logpath}/${dbname}_back.log
else
#備份失敗則進行以下操作
echo "資料庫 ${dbname} 備份失敗!!" >> ${logpath}/${dbname}_back.log
fi
done
echo "完成備份"
echo "################## ${backtime} #############################"
到了1點鐘,確定沒問題後發通知到群裡,發微信給領導表示已遷移完成,進行很順利,然後筆者打車回家,睡覺。
雪崩來臨

其實這一晚筆者睡得也不踏實,到了8點半就醒了,因為我們9點鐘開盤,會有大量的客戶湧進,每天開始產生新的交易(買入和賣出),給大家看下截圖:

果不其然,9點過後,筆者開啟APP,一切正常,點選切換幾個介面後,發現其中一個功能的請求超時了,一直在轉,然後緊接著其它功能也超時了。完了,出問題了。趕緊開筆記本查問題,過了一會兒群裡就開始沸騰了(反映好多客戶開啟APP都顯示請求超時了),我的電話也立馬響了,技術總監打來的,問我怎麼回事,我說正在開筆記本排查。
這個時候,我要說明一下:運維人員此時需要冷靜並且安靜的處理問題,公司領導千萬別催得太急以免打亂處理人的思路。我們總監臨場處理能力做得真是非常到位,馬上跟我說不用擔心上面壓力,有他扛著,叫我只管排查和解決問題。
緊急處理

排查問題
筆記本開啟後,首先想到的就是RDS資料庫出了問題,登入阿裡雲,進入RDS中的DMS資料管理控制檯,一進去就傻眼了 “CPU爆了”,這麼多連線數,如下圖:

進入會話去看看,發現會話“炸鍋了”,發現幾百頁的select都擠在ub_user_calculate這個表中,這個表是資料量相對大一些,這張表目前有200多萬條資料:

自然反應就是去檢視此表的結構,發現此表沒有索引,被驚訝到了,竟然沒有索引,這…… 然後筆者傳回源資料庫檢視這張表,也發現沒有索引,由此可以確定我導過來的這張表就是沒有建立索引,如下圖:

分析:當資料庫中出現訪問表的SQL沒建立索引導致全表掃描,如果表的資料量很大掃描大量的資料,執行效率過慢,佔用資料庫連線,連線數堆積很快達到資料庫的最大連線數設定,新的應用請求將會被拒絕導致故障發生。
解決問題
趕緊把此事反應給開發負責人,表明問題根源找到了,會話鎖死了,由其中的一張表沒有索引而導致的,問詢問需要給哪幾個欄位加索引。然後接著操作增加索引:

點選儲存後,發現建立索引的SQL一直卡死著,如下圖所示:

突然想起來還有一堆會話在那裡,先kill掉所有會話吧,不然索引肯定建立不了,然後又發現會話根本殺不完,如下圖:

怎麼辦呢?會話殺不完…
沒辦法,先把訪問入口切斷吧,反正現在使用者訪問也超時,就毅然決定先把域名停了,訪問入口給切斷了,然後在增加索引,索引加上了。
入口也斷了,索引也加上了,發現CPU還下去,如下圖:

為了快速讓CPU降下去,重啟這個實體吧:

實體重啟完後,CPU下去了,會話也下去了:

開啟入口層的域名訪問吧,在次觀察現在的會話和CPU等況,如下圖:

這就對了,會話也正常了,通知領導業務恢復。
筆者後期補的一張圖:在來看一下伺服器CPU情況(遷移MySQL後的情況),明顯逐漸好轉。

索引使用策略及最佳化

建立索引
  • 在經常查詢而不經常增刪改操作的欄位加索引。

  • order by與group by後應直接使用欄位,而且欄位應該是索引欄位。 一個表上的索引不應該超過6個。

  • 索引欄位的長度固定,且長度較短。

  • 索引欄位重覆不能過多,如果某個欄位為主鍵,那麼這個欄位不用設為索引。

  • 在過濾性高的欄位上加索引。

使用索引註意事項
  • 使用like關鍵字時,前置%會導致索引失效。

  • 使用null值會被自動從索引中排除,索引一般不會建立在有空值的列上。

  • 使用or關鍵字時,or左右欄位如果存在一個沒有索引,有索引欄位也會失效。

  • 使用!=運運算元時,將放棄使用索引。因為範圍不確定,使用索引效率不高,會被引擎自動改為全表掃描。

  • 不要在索引欄位進行運算。

  • 在使用複合索引時,最左字首原則,查詢時必須使用索引的第一個欄位,否則索引失效;並且應儘量讓欄位順序與索引順序一致。

  • 避免隱式轉換,定義的資料型別與傳入的資料型別保持一致。

參考:https://help.aliyun.com/document_detail/52274.html

總結

  1. 此次故障雖然是表沒有索引造成的,但是筆者是有責任的,沒有挨個表檢查一下表的結構;

  2. 透過此次故障也可以看出來開發在設計表的真的要非常的重視,註意細節;

  3. 還有就是之前在容器中執行的MySQL也時不時的出現CPU瓶頸(比如CPU使用率偶爾會達到80%以上),筆者應該就要提前發現這些問題,徹底排查找出問題所在原因在進行遷庫的操作。

原文連結:http://blog.51cto.com/ganbing/2105842

Kubernetes入門與進階實戰培訓

本次培訓內容包括:Docker基礎、容器技術、Docker映象、資料共享與持久化、Docker三駕馬車、Docker實踐、Kubernetes基礎、Pod基礎與進階、常用物件操作、服務發現、Helm、Kubernetes核心元件原理分析、Kubernetes服務質量保證、排程詳解與應用場景、網路、基於Kubernetes的CI/CD、基於Kubernetes的配置管理等,點選瞭解具體培訓內容

5月11日正式上課,點選閱讀原文連結即可報名。
贊(0)

分享創造快樂