久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

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

138次閱讀
沒有評論

共計 5680 個字符,預計需要花費 15 分鐘才能閱讀完成。

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

PartⅠ 案例分析

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

1. 登上數據庫,通過 show processlist 查看到的現場截圖如下:

2. MySQL 版本為 5.7,數據庫表結構如下:

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

3. 業務請求 Session 如下:

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

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

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

查看 information_schema 中 innodb_trx 事務鎖等待隊列,發現已經有 6100+ 條鎖等待信息。

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

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

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

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

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

在數據庫中我們可以形象的理解為:

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

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

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

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

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

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

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

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

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

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

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

1. SQL 限流

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

某類 SQL 并發急劇上升,影響正常業務,比如緩存穿透或者異常調用,造成原來并發不大的 SQL 語句突然上升。

有數據傾斜 SQL,影響正常業務,比如大促時拉取某個特別大的數據,造成整體系統繁忙。

未創建索引 SQL,影響正常業務,比如新上線 SQL 調用量特別大,又沒有創建索引,造成整體系統繁忙。

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

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

最大并發數:同一時刻并發數超過設置的閾值的 SQL 將被拒絕

限流時間:支持設定規則持續時間,超時后不再生效

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

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

2. 熱點更新保護

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

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

3. 熱點更新優化建議

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

3.1)基于 MySQL 實現

表結構如下:

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) 函數方法:

請求邏輯:

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

注意點:數據量會持續增大,可以定期低峰刪除或者創建為分區表,定期刪除歷史數據

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

3.2)基于 Redis 實現

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

3.3)服務化實現

表結構:

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 標識業務;

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

step 標識每次 idallocate-server 訪問數據庫時候一次拉走的 id 區間大小。

實現思路:第三方通過調用 idallocate-server 服務獲取 id。idallocate-server 內存至少包含三個值:當前的 mid,最大能發的 id1,最大能發的 id2;id2 和 id1 相差一個 step。初始時候,idallocate-server 服務從數據庫中更新兩次,分別得到初始值 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 是否已經存在,若不存在則訪問數據庫進行獲取。若存在則 mid 達到 id1 大小后,分配 id2 部分,當 mid 達到 id2 的 90% 時候,需檢測 id1 是否存在。依次循環保證 idallocate-server 內存中至少有一個 step 大小的 buffer 號段存在。

上述方案中:

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

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

以上就是如何解決熱點更新導致的雪崩效應,丸趣 TV 小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注丸趣 TV 行業資訊頻道。

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-07-28發表,共計5680字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 平定县| 平凉市| 朝阳县| 沅江市| 金湖县| 凤冈县| 沿河| 锡林郭勒盟| 中阳县| 太仆寺旗| 通州市| 泊头市| 灵武市| 襄城县| 确山县| 都安| 织金县| 潜江市| 延吉市| 德化县| 萨迦县| 宜黄县| 甘泉县| 鞍山市| 仪陇县| 肃北| 陵川县| 永嘉县| 慈利县| 黔西县| 临潭县| 邮箱| 海安县| 浦城县| 上犹县| 淄博市| 嘉兴市| 丹阳市| 甘孜| 湄潭县| 福安市|