无码人妻精品一区二区三18禁,影音先锋男人AV橹橹色,污污污污污污www网站免费,日韩成人av无码一区二区三区,欧美性受xxxx狂喷水

TDengine 資深研發分享解決思路,長查詢不再成為系統性能瓶頸!

Hongze Cheng

2024-02-27 / ,

長查(cha)詢問題(ti)指的(de)(de)(de)是在(zai)數(shu)據庫寫入和查(cha)詢并存(cun)的(de)(de)(de)日常應用場(chang)景中(zhong),存(cun)在(zai)處理數(shu)據量大且耗時很長的(de)(de)(de)查(cha)詢長時間(jian)占用系(xi)統資(zi)源(yuan),導致寫入可能(neng)被阻塞的(de)(de)(de)問題(ti)。有時,查(cha)詢代碼對于(yu)資(zi)源(yuan)釋放函數(shu)調用的(de)(de)(de)遺(yi)忘也可能(neng)以(yi)長查(cha)詢問題(ti)的(de)(de)(de)形式表現出來。如何(he)在(zai)數(shu)據寫入不被阻塞同時,保證長查(cha)詢的(de)(de)(de)正確進行是一個具有挑(tiao)戰性的(de)(de)(de)問題(ti)。

盡管在(zai)絕(jue)大多數(shu)時序數(shu)據使(shi)用場景(jing)下(xia),用戶不(bu)太可能遇到這個問(wen)題(ti),但一旦出現,也會(hui)讓人頭疼不(bu)已(yi)。為了解決(jue)這一問(wen)題(ti),TDengine 研發團隊一直致力于(yu)不(bu)斷優(you)化系統,提(ti)高查(cha)詢(xun)(xun)性(xing)能和響應速(su)度(du)。本文(wen)將深(shen)入剖析這一挑(tiao)戰(zhan),并探討如(ru)何(he)應對和解決(jue)長查(cha)詢(xun)(xun)問(wen)題(ti),以(yi)提(ti)升 TDengine 在(zai)復雜查(cha)詢(xun)(xun)場景(jing)下(xia)的表現。

在(zai)分析長(chang)查詢問題之(zhi)前(qian),我們首先(xian)需要為大家普(pu)及(ji)一下 TDengine 的(de)寫(xie)入/查詢并發機制(zhi)。

數據寫入/讀取機制

Vnode 是 TDengine 中(zhong)存儲和查詢數據的基本單(dan)元,這(zhe)里主(zhu)要(yao)介紹(shao) Vnode 的寫入/讀(du)取及并發機制(zhi)。

數據寫入機制

TDengine 資深研發分享解決思路,長查詢不再成為系統性能瓶頸! - TDengine Database 時序數據庫

  • 每個 Vnode 在創建時都會根據 DB 參數分配一定數量的內存
  • 這些內存在 Vnode 中被分為三個內存塊
  • 每個 Vnode 只有一個線程寫入
  • Vnode 在寫入時,會從內存塊的空閑列表 (free list) 中分配一個內存塊(mem),供數據寫入使用
  • 當內存塊中數據寫入超過一定量后,開始落盤(imem),同時分配一個新的內存塊供數據寫入使用
  • 當內存塊全部用完,沒有空閑內存塊時,寫入會被阻塞,一直等待有內存塊釋放出來

數據查詢機制

  • 查詢分多批次進行,每次返回部分數據,然后該查詢等待下一次拉取數據的請求
  • 查詢結果是內存(mem/imem)數據和硬盤數據合并的結果
  • 查詢開始時,會先 take snapshot,引用(ref) mem/imem 以及硬盤文件
  • 查詢結束時,unref mem/imem,如果內存塊的引用計數變為 0,則內存塊被回收到空閑鏈表中
TDengine 資深研發分享解決思路,長查詢不再成為系統性能瓶頸! - TDengine Database 時序數據庫

長查詢問題

時(shi)(shi)序數據(ju)絕大部分查(cha)(cha)詢(xun)(xun)(xun)(xun)(xun)持續時(shi)(shi)間(jian)(jian)都比較短(duan),如(ru)查(cha)(cha)詢(xun)(xun)(xun)(xun)(xun)表(biao)/超級表(biao)的(de)(de)最后一(yi)條(tiao)記錄、做 count 或 sum 類的(de)(de)聚(ju)合查(cha)(cha)詢(xun)(xun)(xun)(xun)(xun)等(deng)。這(zhe)類查(cha)(cha)詢(xun)(xun)(xun)(xun)(xun)持續時(shi)(shi)間(jian)(jian)較短(duan),對于 mem/imem 的(de)(de)占用(yong)(yong)時(shi)(shi)間(jian)(jian)也較短(duan),查(cha)(cha)詢(xun)(xun)(xun)(xun)(xun)很(hen)快會釋放(fang) MemTable 供 Vnode 回(hui)收再次利(li)用(yong)(yong),從而(er)不(bu)影響寫(xie)入的(de)(de)進行。但是(shi),如(ru)果存(cun)在一(yi)個(ge)(ge)持續時(shi)(shi)間(jian)(jian)很(hen)長(chang)(chang)(chang)的(de)(de)查(cha)(cha)詢(xun)(xun)(xun)(xun)(xun),如(ru)超過 1 小時(shi)(shi)或 1 天的(de)(de)查(cha)(cha)詢(xun)(xun)(xun)(xun)(xun),這(zhe)時(shi)(shi)候(hou)就會出現此(ci)類問題。當然,只有一(yi)個(ge)(ge)長(chang)(chang)(chang)查(cha)(cha)詢(xun)(xun)(xun)(xun)(xun)的(de)(de)情況(kuang)下,問題也不(bu)大,因為 Vnode 的(de)(de)內存(cun)池默認(ren)被(bei)分成了 3 個(ge)(ge)內存(cun)塊,一(yi)個(ge)(ge)長(chang)(chang)(chang)查(cha)(cha)詢(xun)(xun)(xun)(xun)(xun)最多占用(yong)(yong)兩個(ge)(ge)內存(cun)塊,還(huan)剩余一(yi)個(ge)(ge)內存(cun)塊可(ke)以用(yong)(yong)來進行持續寫(xie)入。但是(shi)如(ru)果存(cun)在多個(ge)(ge)長(chang)(chang)(chang)查(cha)(cha)詢(xun)(xun)(xun)(xun)(xun),Vnode 中的(de)(de)所有內存(cun)塊都可(ke)能被(bei)長(chang)(chang)(chang)時(shi)(shi)間(jian)(jian)占用(yong)(yong),無法進行回(hui)收,從而(er)導致寫(xie)入停止的(de)(de)情況(kuang)。

另外,如果查詢(xun)(xun)部分(fen)的代碼存在 BUG,忘記(ji)關閉查詢(xun)(xun)句柄,也會導致 mem/imem 被長期占用(yong),阻塞寫(xie)入。這個(ge)問題在 TDengine 訂(ding)閱(yue)和(he)流計算功能中曾經(jing)就出(chu)現過。

TDengine 資深研發分享解決思路,長查詢不再成為系統性能瓶頸! - TDengine Database 時序數據庫

長查詢問題解決方案

我們需要一(yi)個(ge)方(fang)案(an),可(ke)以在長查詢(xun)大量存在、或用(yong)戶應用(yong)代碼有問題(ti)沒有及時關閉查詢(xun)句柄、甚至產品代碼有問題(ti)的情(qing)況下,也(ye)能達到既不(bu)阻塞寫入且不(bu)讓長查詢(xun)失敗。該(gai)方(fang)案(an)如下:

  1. 查詢在獲取數據快照時,將查詢句柄注冊到它所占用的內存塊上,同時注冊一個 reseek 函數
  2. 當查詢結束關閉句柄時,該查詢將句柄從它所占用的所有內存塊上注銷
  3. 當寫入發現沒有可用內存塊時,嘗試回收已經 COMMIT 但是仍舊被查詢占用的最老的內存塊
  4. 寫入線程回收內存塊時,遍歷所有注冊在該內存塊上的注冊句柄,調用 reseek 函數
  5. reseek 函數會嘗試鎖查詢句柄,如果鎖句柄成功,則將查詢句柄設置為 RESEEK 狀態,同時將查詢的狀態保存下來并 untake snapshot,歸還占用的所有內存塊
  6. 查詢線程在查詢活躍周期開始時鎖查詢句柄,然后檢查是否為 RESEEK 狀態,如果為 RESEEK 狀態,重新 take snapshot,并根據保存的查詢狀態,恢復各種查詢變量,接著進行查詢

從上述方(fang)案中我們(men)可(ke)以看到:

  1. 寫入在發現內存不足的條件下,可以主動回收非激活狀態的查詢占用的內存塊,從而不會長時間阻塞寫入
  2. 長查詢在寫入回收其所占用的內存塊后,可以根據自己保存的查詢狀態,重新 take snapshot,繼續查詢,從而不會讓長查詢失敗
TDengine 資深研發分享解決思路,長查詢不再成為系統性能瓶頸! - TDengine Database 時序數據庫

TDengine 資深研發分享解決思路,長查詢不再成為系統性能瓶頸! - TDengine Database 時序數據庫

答疑解惑

Q:如何解決死鎖問題?

A:在寫入回收(shou)內存(cun)(cun)塊時,需(xu)要(yao)遍歷查詢(xun)句柄(bing)注冊(ce)鏈表,因此需(xu)要(yao)對(dui)鏈表進(jin)行加鎖(suo)操作(zuo),即需(xu)要(yao)鎖(suo)定(ding)鏈表的(de)(de)鎖(suo)。在調(diao)用 reseek 回調(diao)時,也需(xu)要(yao)鎖(suo)定(ding)查詢(xun)句柄(bing)的(de)(de)鎖(suo)。這樣就(jiu)存(cun)(cun)在寫入線程出現(xian) lock(mutex_list)–>lock(mutex_qhandle) 的(de)(de)情況。而在查詢(xun)結束(shu)時,需(xu)要(yao)將句柄(bing)從注冊(ce)鏈表中移除,導致出現(xian) lock(mutex_qhandle)–>lock(mutex_list) 的(de)(de)情況。如果兩個線程不(bu)按(an)照(zhao)相同(tong)的(de)(de)順序對(dui)兩個鎖(suo)進(jin)行加鎖(suo),就(jiu)會(hui)產生死鎖(suo)的(de)(de)問題。

為了解決這(zhe)個問題,可以(yi)采(cai)用(yong)以(yi)下優化方案:

  1. 使用 trylock 替代 lock:對于寫入回收調用 reseek 函數的場景,可以嘗試使用 trylock 而不是直接使用 lock 來獲取查詢句柄的控制權。通過 trylock 嘗試獲取鎖,可以避免線程因等待鎖而被阻塞,從而減少死鎖的風險。
  2. 多次嘗試機制:在使用 trylock 的基礎上,可以結合多次嘗試的機制。如果一次 trylock 未成功獲取鎖,可以進行多次嘗試,直至成功獲取鎖或達到嘗試次數上限為止。這樣能夠增加獲取鎖的機會,降低死鎖風險。
Q:長查詢會不會一直被寫入 reseek,從而導致查詢一直恢復?

A:不會(hui)(hui)存在這種情況。查(cha)詢(xun)在每次打開句柄時,會(hui)(hui)給(gei)一個版(ban)(ban)(ban)本(ben)號(hao),這個版(ban)(ban)(ban)本(ben)號(hao)是已經寫入(ru) Vnode 中(zhong)(zhong)的(de)最新(xin)(xin)數據(ju)的(de)版(ban)(ban)(ban)本(ben)號(hao),查(cha)詢(xun)只能看到版(ban)(ban)(ban)本(ben)號(hao)小于(yu)等(deng)于(yu)該版(ban)(ban)(ban)本(ben)號(hao)的(de)數據(ju)。當 take snapshot 時,會(hui)(hui)根據(ju) mem/imem 中(zhong)(zhong)的(de)數據(ju)是否被(bei)(bei)查(cha)詢(xun)版(ban)(ban)(ban)本(ben)號(hao)覆蓋而決定 mem/imem 是否被(bei)(bei)該查(cha)詢(xun)引用(yong)(yong)。隨著數據(ju)的(de)寫入(ru),新(xin)(xin)的(de) mem/imem 中(zhong)(zhong)的(de)數據(ju)肯定都大于(yu)該查(cha)詢(xun)創建時給(gei)的(de)版(ban)(ban)(ban)本(ben)號(hao),因此(ci),新(xin)(xin)的(de) mem/imem 不會(hui)(hui)被(bei)(bei)長(chang)查(cha)詢(xun)引用(yong)(yong)。這樣(yang)一來(lai),一個長(chang)查(cha)詢(xun)最多會(hui)(hui)被(bei)(bei)寫入(ru) RESEEK 兩次。

TDengine 資深研發分享解決思路,長查詢不再成為系統性能瓶頸! - TDengine Database 時序數據庫

以上就是 TDengine 資深研發人員對解決長查詢問題進行的深入探討。如果你還有關于查詢的更多問題想要討論或交流,歡迎添加小T vx:tdengine 尋求(qiu)幫助,也可(ke)以(yi)在下方留言區(qu)進行相關評(ping)論,靜待回復(fu)即可(ke)~