如何解決熱點更新導致的雪崩效應

本篇文章給大家分享的是有關如何解決熱點更新導致的雪崩效應,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

成都創(chuàng)新互聯(lián)公司是一家專注于成都做網(wǎng)站、成都網(wǎng)站制作與策劃設計,開封網(wǎng)站建設哪家好?成都創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設10余年,網(wǎng)設計領域的專業(yè)建站公司;建站業(yè)務涵蓋:開封等地區(qū)。開封做網(wǎng)站價格咨詢:18980820575

PartⅠ 案例分析

這個故障的場景比較簡單,當時業(yè)務出現(xiàn)了大量的請求失敗,幾乎處于不可用狀態(tài)。同時對應的MySQL數(shù)據(jù)庫也存在大量的CPU使用率高的告警。

1. 登上數(shù)據(jù)庫,通過show processlist 查看到的現(xiàn)場截圖如下:

2. MySQL 版本為5.7,數(shù)據(jù)庫表結構如下:

CREATE TABLE `docid_generator` (`id` int(4) NOT NULL AUTO_INCREMENT,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2

3. 業(yè)務請求Session如下:

connectupdate docid_generator set id=last_insert_id(id+1); select last_insert_id() exit

通過初步排查,了解到:表中只有一個字段、一行記錄,該段業(yè)務邏輯是通過mysql中l(wèi)ast_insert_id(expr)函數(shù)特性實現(xiàn)id分配功能;按照processlist執(zhí)行耗時倒序查看,耗時最長的sql也是該類update請求;innodb status可以看到大量的事務在等待該條記錄的X鎖;update的X鎖使得請求只能串行進行,導致響應很慢,可是最先到來的一批update請求是什么原因卡住了呢?

通過pref分析,顯示lock_deadlock_recursive函數(shù)占據(jù)了cpu recycle事件的近50%時間。該函數(shù)是通過深度優(yōu)先算法進行遞歸調(diào)用,檢測是否滿足死鎖條件,再進行最小代價的事務回滾。

查看information_schema中innodb_trx事務鎖等待隊列,發(fā)現(xiàn)已經(jīng)有6100+條鎖等待信息。

通過查閱文檔發(fā)現(xiàn),InnoDB監(jiān)控器輸出的最近死鎖檢信息中包含“TOO DEEP OR LONG SEARCH IN THE LOCK TABLE WAITS-FOR GRAPH, WE WILL ROLL BACK FOLLOWING TRANSACTION”,表示處于等待的事務列表長度已達到限制200。超過200個事務的等待列表被視為死鎖,并且將回滾嘗試檢查等待列表的事務。如果鎖定線程必須查看等待列表上的事務擁有的超過1,000,000個鎖,則也可能發(fā)生相同的錯誤。

每個請求維護自己的鎖隊列,在這個案例中,業(yè)務的并發(fā)為200個,因為單條記錄X鎖,只能串行執(zhí)行,按照先后順序依次維護自己的鎖隊列,極限情況記錄阻塞的鎖隊列長度為(1+199)*200/2!所以這一階段耗時較長。

知道耗時長的原因就好辦了。因為業(yè)務場景是單一的id分配,只有一條記錄,邏輯上不會出現(xiàn)死鎖情況,所以完全可以關閉死鎖檢測功能。很幸運,5.7版本innodb_deadlock_detect可以關閉死鎖檢測。關閉后,我們再次200并發(fā)測試,從原來的10s降低到0.2s,性能提升50倍。

分析到這里,相信大家對這個故障案例也一定有了比較深刻的了解。在之前到的介紹里為了不打斷故障分析的連貫性,略過了一些數(shù)據(jù)庫概念的介紹,下面挑選幾個給大家詳細介紹下。

“死鎖”可以理解為兩個或兩個以上的線程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進下去。此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。

在數(shù)據(jù)庫中我們可以形象的理解為:

如上圖所示,事務A在等待事務B釋放id=2的鎖,事務B在等待事務A釋放id=1的鎖。

這種情況就是死鎖,發(fā)生死鎖有兩種方法解決:

1)直接進入等待,直到超時。這個超時時間可以通過參數(shù)innodb_lock_wait_timeout來設置

2)發(fā)起死鎖檢測,發(fā)現(xiàn)死鎖后,主動回滾死鎖鏈條中的某一個事務,讓其他事務得以執(zhí)行。將參數(shù)innodb_deadlock_detect設置為on,表示開啟這個邏輯。

innodb_deadlock_detect=on,該選項使用了禁用MySQL的死鎖檢測功能的。在高并發(fā)系統(tǒng)上,當許多線程等待同一個鎖時,死鎖檢測可能導致速度減慢。當發(fā)生死鎖時,如果禁用了死鎖檢測則可能會更有效,這樣可以依賴innodb_lock_wait_timeout的設置進行事務回滾。

MySQL默認情況下是開啟了死鎖檢測的,InnoDB自動檢測發(fā)送死鎖的事務,并回滾其中的一個事務或所有導致死鎖的事務。InnoDB會在導致死鎖的事務中選擇一個權重比較小的事務來回滾,這個權重值可能是由該事務insert, updated, deleted的行數(shù)決定的。

如果innodb_table_locks = 1(默認值)并且autocommit = 0,則InnoDB能感知到表鎖的存在,并且上層的MySQL層知道行級鎖。否則,InnoDB無法檢測到由MySQL LOCK TABLES語句設置的表鎖或由除InnoDB之外的存儲引擎設置的鎖定的死鎖。通過設置innodb_lock_wait_timeout系統(tǒng)變量的值來解決這些情況。

Part Ⅱ 切實有效的應急降級解決方案

如果電商業(yè)務在大促和秒殺場景、在線教育業(yè)務在報名和簽到、游戲業(yè)務開服等高并發(fā)場景中遇到了類似的熱點更新故障,相信大家一定不會有太多時間理性的梳理和挖掘問題的根因,在較短做出最合理優(yōu)化方案的難度也較大。而此時用戶或者業(yè)務方對數(shù)據(jù)庫的要求必然是不管用什么方法,先讓業(yè)務跑起來(恢復)再說。

那么對于熱點更新類的故障,DBA常用的應急預案:重啟、切換、kill(不論是使用pt-kill還是自己的kill腳本,顯然都很難解決,而且會加劇阻塞)、權限控制(極可能誤傷一些正常的核心業(yè)務邏輯,導致業(yè)務依然失?。蟾怕薀o法完成業(yè)務恢復。即使有損降低如果不依靠業(yè)務側介入都很難完成。

騰訊云數(shù)據(jù)庫智能管家DBbrain,為了防止在熱點更新時,用戶數(shù)據(jù)庫不被大壓力打掛,提供了“SQL限流”和“熱點數(shù)據(jù)防護”這兩大功能,幫助用戶可以在數(shù)據(jù)庫端實現(xiàn)切實有效的降級和防護,保障用戶核心業(yè)務能正常運行。

1. SQL限流

DBbrain提供了“SQL限流”功能,能夠幫助用戶在數(shù)據(jù)庫側實現(xiàn)優(yōu)雅的臨時降級。通過在SQL進入數(shù)據(jù)庫內(nèi)核之前拒絕的方式,能解決更多高并發(fā)故障中,通過kill無法快速恢復的場景,除了上文介紹的“熱點更新引發(fā)死鎖檢測阻塞的場景”之外,還適用于:

  • 某類SQL并發(fā)急劇上升,影響正常業(yè)務,比如緩存穿透或者異常調(diào)用,造成原來并發(fā)不大的SQL語句突然上升。

  • 有數(shù)據(jù)傾斜SQL,影響正常業(yè)務,比如大促時拉取某個特別大的數(shù)據(jù),造成整體系統(tǒng)繁忙。

  • 未創(chuàng)建索引SQL,影響正常業(yè)務,比如新上線SQL調(diào)用量特別大,又沒有創(chuàng)建索引,造成整體系統(tǒng)繁忙。

用戶可以通過在DBbrain控制臺中,設置目標SQL的特性。

  • SQL類型:select、update、delete、insert、replace

  • 最大并發(fā)數(shù):同一時刻并發(fā)數(shù)超過設置的閾值的SQL將被拒絕

  • 限流時間:支持設定規(guī)則持續(xù)時間,超時后不再生效

  • SQL關鍵詞:關鍵字的匹配是無序的,匹配時遍歷關鍵字,看SQL中是否有這個關鍵字,有幾個關鍵字就匹配幾遍

DBbrain會根據(jù)SQL樣本的關鍵字自動拒絕請求,保證業(yè)務核心服務的正常運行,并且統(tǒng)計在開啟“SQL限流”時間段內(nèi)被拒絕的SQL請求數(shù)量。

2. 熱點更新保護

DBbrain針對于秒殺場景,大幅度優(yōu)化對于單行數(shù)據(jù)的update操作的性能。當開啟熱點更新自動探測時,系統(tǒng)會自動探測是否有單行的熱點更新(同一數(shù)據(jù)行上面等待的行鎖數(shù)量超過32個后續(xù)的事務就會開始等待),如果有,則會讓大量的并發(fā)update排隊執(zhí)行,以減少大量行鎖或觸發(fā)大量死鎖檢測造成的并發(fā)性能下降。

DBbrain提供的“熱點更新保護”功能,支持自動結束和手動關閉兩種模式,設置自動結束時間可實現(xiàn)靈活控制。

3. 熱點更新優(yōu)化建議

在上面的案例中,5.7.15以上的版本可以通過關閉死鎖檢測方式提升性能,也可以通過 騰訊云數(shù)據(jù)庫智能管家DBbrain提供的“SQL限流”和“熱點更新保護”來緩解大量熱點更新對數(shù)據(jù)庫帶來的負載壓力。那么接下來的章節(jié)將從業(yè)務實現(xiàn)的角度分享一些啟發(fā)建議。

3.1)基于MySQL實現(xiàn)

表結構如下:

CREATE TABLE `id_allocate` (`id` bigint NOT NULL AUTO_INCREMENT,business_tag varchar(20) not null,PRIMARY KEY (`id`),UNIQUE KEY `name` (business_tag)) ENGINE=InnoDB AUTO_INCREMENT=2;

3.1.1)類似上文例子,通過mysql last_insert_id(expr)函數(shù)方法:

請求邏輯:

connectupdate id_allocate set id=last_insert_id(id+1) where business_tag='test1'; select last_insert_id() exit注意點:5.7以上關閉死鎖檢測innodb_deadlock_detect;

3.1.2)通過mysql auto_increment字段,去掉business_tag字段,只保留id字段,請求邏輯:

connectinsert into id_allocate value(null); select last_insert_id() exit

注意點:數(shù)據(jù)量會持續(xù)增大,可以定期低峰刪除或者創(chuàng)建為分區(qū)表,定期刪除歷史數(shù)據(jù)

純依賴MySQL實現(xiàn),第一種方法更簡單易用。高可用上,常見的思路是存在2個MySQL實例中,設置自增的步長和起始值,比如兩個數(shù)據(jù)庫,設置auto-increment-increment=2,分別設置auto-increment-offset為1和2,業(yè)務請求這兩個DB依次獲取到1,3,5,7和2,4,6,8。該方法可避免單MySQL故障的影響,但同時系統(tǒng)的嚴格單調(diào)遞增也變成了趨勢遞增(若單機故障,可能還有id變小的情況)。

3.2)基于redis實現(xiàn)

利用redis的incr和incrby方式,能支撐的qps更高。同樣若擔心高可用問題,可以設置兩個key分別存儲在兩個redis實例上,通過控制初始值和incrby的offset來保障。這里顯著的弊端是 redis數(shù)據(jù)不能持久化,但目前騰訊云redis支持了主備同步、雙機房容災和備份功能,對于項目開發(fā)緊急,性能要求高的場景也可以嘗試使用。

3.3)服務化實現(xiàn)

表結構:

CREATE TABLE `id_allocate` (`id` bigint NOT NULL AUTO_INCREMENT,business_tag varchar(20) not null,max_id bigint not null,step int not null,PRIMARY KEY (`id`),UNIQUE KEY `name` (business_tag)) ENGINE=InnoDB AUTO_INCREMENT=2;

business_tag標識業(yè)務;

max_id標識目前分配出去的最大id;

step標識每次idallocate-server訪問數(shù)據(jù)庫時候一次拉走的id區(qū)間大小。

實現(xiàn)思路:第三方通過調(diào)用idallocate-server服務獲取id 。idallocate-server內(nèi)存至少包含三個值:當前的mid,最大能發(fā)的id1,最大能發(fā)的id2;id2和id1相差一個step。初始時候,idallocate-server服務從數(shù)據(jù)庫中更新兩次,分別得到初始值mid、id1和id2:

beginselect max_id from id_allocate where business_tag='test1' for update; #得到midupdate id_allocate set max_id=max_id+step  where business_tag='test1';select max_id from id_allocate where business_tag='test1';#得到id1commit
beginupdate id_allocate set max_id=max_id+step  where business_tag='test1';select max_id from id_allocate where business_tag='test1';#得到id2commit

隨著第三方請求idallocate-server獲取id,mid一直增大,當達到id1的90%時候,需檢測id2是否已經(jīng)存在,若不存在則訪問數(shù)據(jù)庫進行獲取。若存在則mid達到id1大小后,分配id2部分,當mid達到id2的90%時候,需檢測id1是否存在。依次循環(huán)保證idallocate-server內(nèi)存中至少有一個step大小的buffer號段存在。

上述方案中:

1. 可用性:idallocate-server服務可以橫向擴展,避免單點;MySQL層面可以通過主備集群半同步或者強一致性同步來保證,且短時間內(nèi)MySQL故障也不會影響服務。

2. 性能:將更新MySQL的請求降低為純MySQL id分配 方式的 1/step(沒step個id大小 更新一次db),降低數(shù)據(jù)庫的壓力;同時通過id2和id1雙號段的設計,避免了當單獨id1分配完全,需等待idallocate-server實時去db更新獲取最新數(shù)據(jù) 這種延時毛刺

以上就是如何解決熱點更新導致的雪崩效應,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學到更多知識。更多詳情敬請關注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

網(wǎng)頁標題:如何解決熱點更新導致的雪崩效應
網(wǎng)頁網(wǎng)址:http://muchs.cn/article44/jpiohe.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護、自適應網(wǎng)站、定制網(wǎng)站移動網(wǎng)站建設、電子商務、企業(yè)建站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設