叮咚買菜自建MongoDB上騰訊云實(shí)踐
隨著近年來公有云技術(shù)及云基礎(chǔ)設(shè)施的發(fā)展,越來越多的企業(yè)轉(zhuǎn)為使用公有云來托管自己的服務(wù)。云數(shù)據(jù)庫因?yàn)閿?shù)據(jù)可靠性、資源彈性、運(yùn)維便捷行,云上數(shù)據(jù)庫服務(wù)也正成為企業(yè)數(shù)據(jù)管理的較好的選擇。??
本文以叮咚買菜自建MongoDB數(shù)據(jù)庫整體遷移上騰訊云MongoDB為背景,分享叮咚買菜上云過程中的遇到的疑難問題及對應(yīng)的性能優(yōu)化解決方法等,主要包括以下分享內(nèi)容:
? ?·?云上MongoDB版本選型
? ?·?安全上云及切換方案
? ?·?叮咚買菜業(yè)務(wù)側(cè)性能優(yōu)化
? ?·?上云遇到的疑難問題及解決方法
? ?·?自建上云收益
叮咚買菜自建
MongoDB上云背景
叮咚買菜業(yè)務(wù)以生鮮即時配送為核心,兼?zhèn)湫铝闶垭娚毯蜕r供應(yīng)鏈的特點(diǎn),對高并發(fā)和數(shù)據(jù)一致性有硬性要求。在快速擴(kuò)張的過程中,很多服務(wù)技術(shù)選型以MongoDB作為其主要數(shù)據(jù)存儲。相比其他非即時業(yè)務(wù)場景,叮咚買菜對數(shù)據(jù)庫訪問時延、穩(wěn)定性、數(shù)據(jù)一致性、數(shù)據(jù)安全性也有更苛刻的要求。
借助騰訊云MongoDB產(chǎn)品完善的自動化運(yùn)維、數(shù)據(jù)安全備份回檔、云彈性等能力,可以快速補(bǔ)齊叮咚買菜的核心MongoDB數(shù)據(jù)庫基礎(chǔ)技術(shù)能力,確保數(shù)據(jù)團(tuán)隊(duì)可以游刃有余的支撐業(yè)務(wù)開發(fā)。
叮咚數(shù)據(jù)團(tuán)隊(duì)基于自建成本、物理資源不足等原因,經(jīng)過綜合評估,決定把MongoDB數(shù)據(jù)遷移到騰訊云MongoDB上。
云上版本推薦及切換方案
業(yè)務(wù)正式開始遷移前,結(jié)合叮咚DBA和業(yè)務(wù)同學(xué)了解具體場景、MongoDB集群部署方式、業(yè)務(wù)MongoDB用法、內(nèi)核版本、客戶端版本、客戶端driver類型等。提前了解到用戶第一手信息:
? ?·?自建MongoDB版本較低
? ?·?客戶端driver主要包括java和PHP
? ?·?集群部署都帶tag
? ?·?集群存在較頻繁的抖動問題
2.1. 騰訊云MongoDB內(nèi)核版本推薦
叮咚自建MongoDB因歷史原因一致保持在官方MongoDB-3.2版本,在一些場景存在性能瓶頸,例如用戶主從讀寫分離時候會遇到讀超時等問題。考慮到用戶對性能要求較高,同時結(jié)合以下技術(shù)點(diǎn),最終推薦用戶使用騰訊云MongoDB-4.0版本,主要原因如下:
·?非阻塞從節(jié)點(diǎn)讀(叮咚買菜遇到的低版本主要問題)
MongoDB-4.x開始,引入了非阻塞的從節(jié)點(diǎn)讀(Non-Blocking Secondary Reads),徹底解決了3.x版本從節(jié)點(diǎn)批量重放oplog時候加全局ParallelBatchWriterMode類型MODE_X鎖引起的讀從節(jié)點(diǎn)讀阻塞問題。
·?存儲引擎優(yōu)化(低版本的主要問題)
相比3.2版本,4.0對存儲引擎做了很多優(yōu)化,例如cache臟數(shù)據(jù)淘汰、鎖粒度、更全的引擎參數(shù)調(diào)整支持等,極大的解決了低版本后臺加索引、大流量讀寫等引起的客戶端訪問阻塞問題。
·?更好的寫性能
相比3.2版本,除了上面提到的非阻塞從節(jié)點(diǎn)讀引起的讀性能提升外,在寫性能方面4.0也更有優(yōu)勢。
·?更多有用新功能
經(jīng)過幾個大版本迭代,4.0版本相比3.2新增了非常多有用功能,例如Retryable Writes(可重試寫)、Change Streams(變更流操作)、Tunable Consistency(更強(qiáng)的可調(diào)一致性)、Schema Validation(模式檢查)、安全功能增強(qiáng)、事務(wù)支持、更豐富的操作類型等。
·?分片模式集群擴(kuò)容balance效率更高
4.0版本相比3.2版本,增加分片擴(kuò)容后的數(shù)據(jù)遷移采用更好的并發(fā)遷移策略,擴(kuò)容數(shù)據(jù)遷移速率更高。
·?為何不選擇更高的MongoDB版本?
MongoDB版本越高功能越多,例如更高版本支持分布式事務(wù)、多字段hash片建支持等。由于叮咚主要是副本集集群,并且對這些新功能需求不強(qiáng)烈,同時綜合集群穩(wěn)定性考慮,最終選擇4.0版本。
·?客戶端driver版本兼容性,減少用戶客戶端改造成本
由于內(nèi)核版本較低,如果升級到高版本,首先需要考慮對應(yīng)客戶端driver版本是否兼容低版本driver。如果客戶端版本和內(nèi)核不兼容,則需要進(jìn)行driver升級甚至代碼改造,因此客戶端driver兼容性也是MongoDB內(nèi)核版本選擇的一個關(guān)鍵指標(biāo)。叮咚技術(shù)團(tuán)隊(duì)經(jīng)過驗(yàn)證,確認(rèn)客戶端版本完全兼容4.0內(nèi)核,代碼無需任何改造。
2.2. 安全上云遷移方案
叮咚自建MongoDB集群包含部分重要數(shù)據(jù),務(wù)必保證遷移的數(shù)據(jù)一致性。常用通用遷移方案如下:
·?通用遷移方案

步驟1:騰訊云DTS for MongoDb全量+增量方式實(shí)時同步自建數(shù)據(jù)到云上MongoDB
步驟2:選擇凌晨業(yè)務(wù)低峰期判斷DTS延遲進(jìn)度,延遲追上后,業(yè)務(wù)停寫
步驟3:確保源集群最后一條oplog同步到目標(biāo)集群,客戶端IP地址切到目標(biāo)集群
通過上面的操作步驟,最終完成不同版本的MongoDB上云。但是,該上云方案有個風(fēng)險(xiǎn),假設(shè)業(yè)務(wù)切換到目標(biāo)新集群后部分讀寫有問題,這時候就需要回滾到源自建集群。由于切換到新集群過程后,可能部分寫流量到了目標(biāo)集群,這時候目標(biāo)集群相比源集群就會有更多的數(shù)據(jù)。這時候,切回到源集群后,也存在數(shù)據(jù)不一致的情況,即使把目標(biāo)集群增量oplog回寫到源集群,也可能存在亂序?qū)懭胍鸬臄?shù)據(jù)混亂問題。
·?優(yōu)化方案
為了解決極端情況下回滾引起的數(shù)據(jù)丟失、數(shù)據(jù)混亂、數(shù)據(jù)不一致等問題,叮咚業(yè)務(wù)集群采用如下更加安全可回滾切割方案:

當(dāng)業(yè)務(wù)流量從源叮咚自建MongoDB-3.2集群切換到騰訊云MongoDB-4.0后,如果存在版本兼容、業(yè)務(wù)訪問異常等問題,則可直接回滾到騰訊云MongoDB-3.2版本,由于回滾集群和源自建集群版本一致,并且通過DTS實(shí)時同步,因此,可以一定程度保證回滾流程數(shù)據(jù)不混亂、不沖突、不丟失,也可保證切割出問題時候的快速回滾。
由于叮咚業(yè)務(wù)MongoDB存儲了部分重要數(shù)據(jù),不允許數(shù)據(jù)丟失及混亂,對數(shù)據(jù)一致性要求極高。當(dāng)前方案在切換過程中,仍存在向前回滾時數(shù)據(jù)延遲、連接串更換后應(yīng)用寫錯等風(fēng)險(xiǎn)。因此為了確保實(shí)時同步及回滾數(shù)據(jù)一致性萬無一失,除了DTS的回滾方案,叮咚在業(yè)務(wù)側(cè)增加了幾層保護(hù):通過訂閱和掃描,針對核心庫的數(shù)據(jù)進(jìn)行校驗(yàn);通過流量檢測進(jìn)行業(yè)務(wù)反查;如果出現(xiàn)業(yè)務(wù)數(shù)據(jù)不一致,可以通過工具進(jìn)行可灰度、可控速的方式進(jìn)行補(bǔ)齊。
叮咚自建MongoDb上云遇到問題及優(yōu)化解決方法
叮咚不同業(yè)務(wù)從3.2版本上云升級到4.0版本過程中,遇到了一些性能瓶頸問題,主要包括以下問題:
? ?·?騰訊云MongoDb短鏈接性能優(yōu)化
? ?·?叮咚業(yè)務(wù)側(cè)短鏈接優(yōu)化
? ?·?Session定期刷新引起的集群抖動問題
3.1.短鏈接性能優(yōu)化解決方法
以叮咚集群其中某業(yè)務(wù)為例,該業(yè)務(wù)部分接口使用PHP driver,因此會涉及到大量的MongoDB短鏈接訪問,以下分別說明叮咚自建集群短鏈接瓶頸優(yōu)化及騰訊云MongoDB短鏈接優(yōu)化過程。
3.1.1. PHP業(yè)務(wù)短鏈接瓶頸原因
PHP一次請求訪問,需要如下交互過程日志如下:
1.?2021-1104T12:45:36.621+0800?I?NETWORK??[conn6]?received?client?metadata?from?…??
2.?2021-11-04T12:45:36.621+0800?I?COMMAND??[conn6]?command?admin.$cmd?command:?isMaster?…??
3.?2021-11-04T12:45:36.622+0800?I?COMMAND??[conn6]?command?adminxx.users?command:?saslStart??…
4.?2021-11-04T12:45:36.626+0800?I?COMMAND??[conn6]?command?admin.$cmd?command:?saslContinue??…
5.?2021-11-04T12:45:36.627+0800?I?ACCESS???[conn6]?Successfully?authenticated?as?principal??…?
6.?2021-11-04T12:45:36.627+0800?I?COMMAND??[conn6]?command?adminxx.users?command:?saslContinue
7.?2021-11-04T12:45:36.627+0800?I?COMMAND??[conn6]?command?admin.$cmd?command:?ping …????
8.?2021-11-04T12:45:36.627+0800?I?COMMAND??[conn6]?command?x.test??command:?find?{?find:?"test"??
9.?2021-11-04T12:45:37.636+0800?I?NETWORK??[conn6]?end?connection?…??
從上面的簡化日志可以看出,一次find請求預(yù)計(jì)需要下面多個操作步驟,并且多次才能完成,主要流程如下:
? ?·?TCP三次握手鏈接
? ?·?客戶端發(fā)送isMaster命令給服務(wù)端獲取所連節(jié)點(diǎn)
? ? ? 的一些狀態(tài)信息、協(xié)議兼容信息
? ?·?進(jìn)行sasl多次認(rèn)證交互
? ?·?Ping探測獲取往返時延
? ?·?真正的業(yè)務(wù)訪問,例如這里的find查詢請求
? ?·?四次揮手關(guān)閉本次鏈接對應(yīng)請求
上面的流程體現(xiàn)出一次訪問,不僅僅建鏈、斷鏈開銷、還有多次認(rèn)證交互以及其他額外交互開銷,最終有效訪問只占用極少一部分開銷,無效訪問浪費(fèi)了系統(tǒng)大部分開銷。此外,PHP業(yè)務(wù)單次訪問的總時延增加,單次訪問總時延如下:
PHP單次訪問時延=建鏈時間+isMaster()交互時間+認(rèn)證時間+ping時間+數(shù)據(jù)訪問時間
?
3.1.2.叮咚自建MongoDB集群短鏈接部署優(yōu)化
該用戶集群雖然數(shù)據(jù)量不是很大,但是流量較高,讀寫總流量數(shù)萬/秒,如果直接采用普通副本集模式集群,則存在MongoDB存儲節(jié)點(diǎn)因?yàn)槎替溄恿髁扛咭鸬呢?fù)載問題。叮咚自建MongoDB為了解決短鏈接訪問引起的瓶頸問題,采用如下架構(gòu):

上圖為某業(yè)務(wù)集群,業(yè)務(wù)讀寫流量較高,總流量數(shù)萬/S,其中有一部分PHP業(yè)務(wù)。考慮到PHP業(yè)務(wù)短鏈接每次訪問需要多次認(rèn)證交互,會增加mongo內(nèi)核壓力,因此用戶采用分片架構(gòu)集群部署,單個分片。客戶端程序和mongos部署在同一臺服務(wù)器,每個客戶端通過本地mongodb:///var/run/mongodb-order.sock/訪問,通過該架構(gòu)來解決短鏈接帶來的性能瓶頸。
3.1.3.騰訊云MongoDB部署架構(gòu)及優(yōu)化過程
·?MongoDB部署架構(gòu)
騰訊云MongoDB采用叮咚類似架構(gòu),唯一區(qū)別是mongos代理叮咚部署在客戶端機(jī)器本地部署,MongoDB則是獨(dú)立部署,云上MongoDB部署架構(gòu)如下:

·?MongoDB短鏈接優(yōu)化
在業(yè)務(wù)正式上線前,MongoDB團(tuán)隊(duì)對PHP短鏈接進(jìn)行了提前的摸底測試(后端分片無瓶頸,壓一個mongos),測試結(jié)果存在如下現(xiàn)象:
?? ·?服務(wù)器CPU空閑
? ?·?listener線程CPU消耗較高
? ?·?短鏈接并發(fā)越高鏈接報(bào)錯比例越高。
? ?·?后端分片MongoDB無瓶頸
通過分析,netstat查看發(fā)現(xiàn)大量的SYN_RECV 、FIN_WAIT狀態(tài)的鏈接,同時客戶端大量鏈接報(bào)錯,說明mongos代理處理鏈接不及時,這和MongoDB內(nèi)核網(wǎng)絡(luò)線程模型有較大的關(guān)系,MongoDB所有客戶端請求由listener線程進(jìn)行accept處理,accept獲取到一個新鏈接,則創(chuàng)建一個線程,該線程負(fù)責(zé)該鏈接以后的所有數(shù)據(jù)讀寫。
一個mongos進(jìn)程只有一個listener線程,當(dāng)客戶端鏈接并發(fā)較高的時候,很容易造成鏈接排隊(duì),最終造成客戶端鏈接超時。不過,該瓶頸可以通過部署多個mongos來解決,多個mongos就會有多個listener線程,accept處理能力就會增強(qiáng)。
最終,綜合成本及性能考慮,選擇多個最低規(guī)格(2PU/4G)的mongos來解決短鏈接瓶頸。
除了多mongos部署外,還對MongoDB內(nèi)核listen backlog隊(duì)列長度進(jìn)行了適當(dāng)?shù)恼{(diào)整,緩解隊(duì)列滿引起的客戶端鏈接異常問題。

net.listenBacklog配置在3.6版本開始支持,調(diào)研了常用服務(wù)端中間件nginx、redis,這類中間件都支持listen backlog配置,默認(rèn)取值分別如下:
???· Nginx默認(rèn)取值:511
???· Redis默認(rèn)取值:511
???· MongoDB默認(rèn)取值:SOMAXCONN
SOMAXCONN也就是操作系統(tǒng)/proc/sys/net/core/somaxcon文件中的值,線上默認(rèn)取值128。修改somaxcon為10240,配置net.listenBacklog為511,保持和nginx、redis推薦默認(rèn)值511 一致。通過測試,net.listenBacklog調(diào)整后,4C規(guī)格測試,短鏈接異常超時現(xiàn)象會有較大緩解。
listenBacklog配置 | 測試結(jié)果 |
128 | 2000并發(fā)約10%左右請求鏈接報(bào)錯 |
511 | 2000并發(fā)無任何鏈接報(bào)錯 |
3.2.?Session定期刷新業(yè)務(wù)抖動優(yōu)化解決過程
3.2升級到4.0版本上云過程中,除了用戶短鏈接PHP瓶頸外,另外一個就是session會話定期刷新引起的業(yè)務(wù)抖動問題。
3.2.1.業(yè)務(wù)抖動現(xiàn)象
用戶從3.2版本升級到騰訊云4.0版本后,騰訊云MongoDB集群流量監(jiān)控圖如下:
?
如上圖所示,整個現(xiàn)象如下:
? ?·?update周期性流量尖刺,尖刺周期5分鐘
? ?·?delete周期性流量尖刺,尖刺周期30分鐘
? ?·?流量尖刺過程中,時延也對應(yīng)增加,周期性抖動
? ?·?CPU周期性消耗
3.2.2.線下模擬測試
當(dāng)客戶端眾多,連接數(shù)過高的情況下,副本集主節(jié)點(diǎn)有瞬間大量update甚至delete操作,mongostat和mongotop監(jiān)控如下(測試條件:500并發(fā)短鏈+500并發(fā)長鏈接,進(jìn)行insert持續(xù)性寫入測試):
·?mongostat監(jiān)控發(fā)現(xiàn)大量未知update操作,甚至遠(yuǎn)超過正常insert流量

從上面可以看出,存在大量update更新操作,同時該操作類型統(tǒng)計(jì)甚至超過正常的insert寫入。
·?mongotop確定流量來自于哪一個表
500并發(fā)短鏈接及500并發(fā)長鏈接同時進(jìn)行持續(xù)性insert寫入測試,發(fā)現(xiàn)mongostat監(jiān)控中存在大量的update更新操作,而我們的測試中沒有進(jìn)行update更新操作,于是通過mongotop獲取update操作來源,監(jiān)控結(jié)果入下圖所示:

從上圖可以看出,大量的update操作來自于config庫的system.sessions表,這是一個潛在隱患。
·?其他潛在隱患(system.sessions表集中過期)
system.sessions表默認(rèn)30分鐘過期,由于session會話數(shù)據(jù)都是集中刷新到system.sessions表,因此該表還存在集中過期的情況,集中過期將讓update定期更新和過期操作疊加,進(jìn)一步加重集群抖動。
3.2.3.Session模塊內(nèi)核實(shí)現(xiàn)
從MongoDB-3.6版本開始,MongoDB開始逐步支持單文檔事務(wù),從而開始引入session邏輯會話模塊。不論是副本集mongod還是分片集群的mongos,都會啟動一個定時器刷新cache中緩存的session信息到config庫的system.session表中。
通過走讀MongoDB內(nèi)核代碼,確定該問題是對system.sessions表做更新引起,內(nèi)核對應(yīng)主要代碼實(shí)現(xiàn)由LogicalSessionCache模塊負(fù)責(zé)session會話管理操作。下面通過一次完整的客戶端訪問為例,分析session模塊核心原理及主要代碼實(shí)現(xiàn):
·?步驟1:客戶端發(fā)送isMaster命令到服務(wù)端,服務(wù)端通知客戶端是否支持session管理

客戶端通過發(fā)送isMaster命令給服務(wù)端,如果服務(wù)端支持session會話管理模塊,則返回session會話超時時間信息給客戶端。
·?步驟2:客戶端請求攜帶”lsid”信息發(fā)送給服務(wù)端

服務(wù)端收到客戶端”lsid”信息后,檢查本地cache是否有該session 信息,如果本地cache沒有該session信息則添加到本地。一個”lsid”代表一個session會話信息。
如果業(yè)務(wù)沒有創(chuàng)建session信息,則默認(rèn)對一個鏈接創(chuàng)建一個會話信息,鏈接和session一一對應(yīng)。
·?步驟三:啟動定時器,定期把cache中的session信息system.sessions表中

Mongos和mongod實(shí)例都會啟動一個后臺線程,定期把cache中的所有sessions信息同步到config庫的system.sessions表中。同步的session會話信息可以分為以下兩類:
1.activeSessions活躍session
activeSessions會話信息主要包括一個定時器周期活躍的session信息(一個定時器周期該session對應(yīng)的客戶端有訪問mongodb)和新創(chuàng)建的session會話信息。
Mongos或者mongod會定期把a(bǔ)ctiveSessions信息一條一條一次性update到副本集對應(yīng)system.sessions表中。
2.endSessions會話信息
endsessions會話信息主要包括客戶端顯示通過endSession命令結(jié)束的會話信息和空閑時間超過system.sessions表對應(yīng)TTL過期時間的會話信息。
客戶端主動觸發(fā)endSession的session會話信息,mongos也是在同一個定時器中通過remove操作system.sessions表,從而實(shí)現(xiàn)cache和system.sessions表的一致性。Remove操作和活躍session的update操作在同一個定時器完成,update更新完畢后,立馬進(jìn)行remove操作。
如果是短鏈接,并且關(guān)閉鏈接前主動進(jìn)行了endSession結(jié)束會話操作,副本集可能還存在system.sessions表大量remove的操作。
如果session長時間空閑,則通過system.sessions表的TTL索引來觸發(fā)本地cache的清理工作。
·?定時器相關(guān)
定時器默認(rèn)定時時間5分鐘,可以通過logicalSessionRefreshMillis配置。
·?System.sessions表TTL過期時間
默認(rèn)30分鐘過期。
·?會話統(tǒng)計(jì)信息
會話統(tǒng)計(jì)信息通過
“db.serverStatus().logicalSessionRecordCache”命令獲取,如下圖所示:

logicalSessionRecordCache統(tǒng)計(jì)中,可以分為兩類:session類統(tǒng)計(jì)和transaction事務(wù)類統(tǒng)計(jì),核心統(tǒng)計(jì)項(xiàng)功能說明如下:
統(tǒng)計(jì)類型 | 統(tǒng)計(jì)項(xiàng) | 統(tǒng)計(jì)內(nèi)容說明 |
? ? Session統(tǒng)計(jì) ? ? | activeSessionsCount | 當(dāng)前活躍會話數(shù) |
lastSessionsCollectionJobEntriesRefreshed | 上一個周期刷新到system.session表的活躍會話數(shù) | |
lastSessionsCollectionJobEntriesEnded | 上一個周期刷新到system.session表的endSessions會話數(shù) | |
lastSessionsCollectionJobCursorsClosed | 上一個周期內(nèi)清理的 | |
Transaction統(tǒng)計(jì) | lastTransactionReaperJobEntriesCleanedUp | 本周期內(nèi)清理的transactions表數(shù)據(jù)條數(shù) |
3.2.4. Session問題MongoDB內(nèi)核優(yōu)化解決
從上面的原理可以看出,業(yè)務(wù)抖動主要因?yàn)閏ache中的session定期集中式刷新,以分片MongoDB集群為例,內(nèi)核層面可以通過以下方式優(yōu)化緩解、也可以通過增加內(nèi)核配置開關(guān)規(guī)避。
·?無MongoDB內(nèi)核開發(fā)能力:參數(shù)調(diào)優(yōu)、部署優(yōu)化
如果沒有MongoDB內(nèi)核開發(fā)能力,可以通過以下方法進(jìn)行session內(nèi)核參數(shù)調(diào)優(yōu)和部署方式調(diào)整來緩解問題:
方法一:mongos及副本集mongod部署時間打散,避免所有節(jié)點(diǎn)定時器同時生效。
當(dāng)前集群默認(rèn)啟動后,所有代理和mongod幾乎都是同時啟動的,代理啟動時差較小,session定時器可能同一時間觸發(fā),可以手動打散到不同時間點(diǎn)重啟,包括所有mongos和副本集mongod。
方法二:短鏈接業(yè)務(wù)考慮定時刷新周期適當(dāng)調(diào)短
短鏈接默認(rèn)每次請求會生成一個session會話,訪問完畢后不會主動通知MongoDB內(nèi)核釋放session,因此,session在定時周期內(nèi)會大量擠壓,可以考慮縮短定時時間來規(guī)避大量短連接session的定期刷新操作。例如logicalSessionRefreshMillis從默認(rèn)5分鐘調(diào)整為30秒,則集中式的session刷新會散列到多個不同時間點(diǎn)。
方法三:長鏈接業(yè)務(wù)適當(dāng)調(diào)大定時刷新周期
實(shí)際測試驗(yàn)證中,默認(rèn)官方driver長鏈接(包括golang、java、c客戶端測試驗(yàn)證)都是一個鏈接對應(yīng)一個session會話id,也就是同一個請求客戶端攜帶的”lsid”保持不變。MongoDB內(nèi)核實(shí)現(xiàn)中,只要每一個鏈接對應(yīng)的定時周期內(nèi)有一次以上活躍請求訪問,則會再次緩存該session id,session id刷新到config.sessions表中后,cache中會清除。
從上面的長鏈接session cache流程可以看出,一般長鏈接用戶都是連接池配置,總連接數(shù)越高session也會越高。因此長鏈接可以通過以下調(diào)整減少session定期刷新的影響:
1.?控制連接池最大連接數(shù),減少cache中的session總量
2.?適當(dāng)調(diào)大logicalSessionRefreshMillis刷新周期,減少頻繁刷新的影響
·?內(nèi)核增加禁用session會話功能開關(guān)
在3.6以下版本,MongoDB是沒有session會話管理模塊的,為了支持事務(wù)和可重試寫功能而引入。
分析了MongoDB-c-driver客戶端,發(fā)現(xiàn)客戶端第一次報(bào)文交互是否攜帶“l(fā)sid”給服務(wù)端是根據(jù)isMaster的返回內(nèi)容中是否攜帶由” logicalSessionTimeoutMinutes”來決定的,如下:

實(shí)際上用戶從MongoDB-3.2版本升級到4.0,用戶也不需要事務(wù)、可重試寫功能,客戶端完全沒必要默認(rèn)走session id生成流程。如果客戶端不觸發(fā)session id(也就是報(bào)文交互中的”lsid”),則就不會觸發(fā)mongodb服務(wù)端內(nèi)核啟用session定期刷新功能模塊。因此,可以考慮在MongoDb內(nèi)核代碼實(shí)現(xiàn)中,客戶端isMaster獲取相關(guān)信息的時候,不返回“l(fā)ogicalSessionTimeoutMinutes”信息,這樣客戶端就不會生成”lsid”信息發(fā)送給服務(wù)端。

4.0官方代碼默認(rèn)寫死,MongoDB內(nèi)核實(shí)現(xiàn)增加session模塊支持開關(guān),如果業(yè)務(wù)沒有事務(wù)等需求,內(nèi)核不應(yīng)答“l(fā)ogicalSessionTimeoutMinutes”信息給客戶端,這樣客戶端認(rèn)為服務(wù)端不支持該功能,就不會主動生成session id交互。
說明:session會話模塊功能開關(guān)支持,當(dāng)前已驗(yàn)證c driver、java、php,測試驗(yàn)證session問題徹底解決,使用該功能切記對客戶端進(jìn)行提前驗(yàn)證,可能不同客戶端對logicalSessionTimeoutMinutes處理邏輯不一致,特別是使用spring+mongodb整合的客戶端。
3.2.5.叮咚長鏈接session問題用戶側(cè)優(yōu)化
叮咚用戶某長鏈接通過騰訊云MongoDB提供的節(jié)點(diǎn)地址自己對集群也做了詳細(xì)的流量監(jiān)控,監(jiān)控曲線如下圖所示:

在對該長鏈接用戶進(jìn)行MongoDB內(nèi)核優(yōu)化前,可以看出流量周期性尖刺,并且發(fā)現(xiàn)一個歸類:隨著該java長鏈接流量qps越高,定期的sessionupdate尖刺越嚴(yán)重。
利用線上MongoDB進(jìn)行復(fù)現(xiàn),使用MongoDB官方j(luò)ava driver測試結(jié)果和之前的長鏈接分析一致,也就是一個鏈接一個session會話,也就是java服務(wù)定期最大的session update不會超過總連接數(shù)。但是叮咚用戶的java服務(wù)通過MongoDB內(nèi)核日志,看到如下現(xiàn)象:
1.?Thu?Sep??2?18:07:19.833?I?COMMAND??[conn48527]?command?xx.xxx?command:?insert?{?insert:?"xx",?ordered:?true,?$db:?"xx",?$clusterTime:?{?clusterTime:?Timestamp(1630577239,?387),?signature:?{?hash:?BinData(0,?B37C8600CE543BEE2D8085730AADD91CB13C14AF),?keyId:?7002593870005403649?}?},?lsid:?{?id:?UUID("3ce872be-5a8d-4a1b-b656-8ba33b3cb15d")?}?}?ninserted:1?keysInserted:2?numYields:0?reslen:230?locks:{?Global:?{?acquireCount:?{?r:?2,?w:?2?}?},?Database:?{?acquireCount:?{?w:?2?}?},?Collection:?{?acquireCount:?{?w:?1?}?},?oplog:?{?acquireCount:?{?w:?1?}?}?}?protocol:op_msg?0ms??
2.?Thu?Sep??2?18:07:19.921?D?COMMAND??[conn48527]?run?command?xx.$cmd?{?insert:?"xxxx",?ordered:?true,?$db:?"xxx",?$clusterTime:?{?clusterTime:?Timestamp(1630577239,?429),?signature:?{?hash:?BinData(0,?B37C8600CE543BEE2D8085730AADD91CB13C14AF),?keyId:?7002593870005403649?}?},?lsid:?{?id:?UUID("ab3e1cb8-1ba1-4e03-bf48-c388f5fc49d1")?}?}??
3.?Thu?Sep??2?18:07:19.922?I?COMMAND??[conn48527]?command?xxx.xxx?command:?insert?{?insert:?"oxxx",?ordered:?true,?$db:?"maicai",?$clusterTime:?{?clusterTime:?Timestamp(1630577239,?429),?signature:?{?hash:?BinData(0,?B37C8600CE543BEE2D8085730AADD91CB13C14AF),?keyId:?7002593870005403649?}?},?lsid:?{?id:?UUID("ab3e1cb8-1ba1-4e03-bf48-c388f5fc49d1")?}?}?ninserted:1?keysInserted:2?numYields:0?reslen:230?locks:{?Global:?{?acquireCount:?{?r:?2,?w:?2?}?},?Database:?{?acquireCount:?{?w:?2?}?},?Collection:?{?acquireCount:?{?w:?1?}?},?oplog:?{?acquireCount:?{?w:?1?}?}?}?protocol:op_msg?0ms??
4.?Thu?Sep??2?18:07:19.765?I?COMMAND??[conn48527]?command?xxx.xxx?command:?find?{?find:?"xxx",?filter:?{?status:?{?$in:?[?1,?2,?4,?5,?9,?11,?13,?3,?7,?15?]?},?order_number:?"xxx",?is_del:?false?},?sort:?{?_id:?-1?},?$db:?"xxx",?$clusterTime:?{?clusterTime:?Timestamp(1630577239,?375),?signature:?{?hash:?BinData(0,?B37C8600CE543BEE2D8085730AADD91CB13C14AF),?keyId:?7002593870005403649?}?},?lsid:?{?id:?UUID("ed1f0fd8-c09c-420a-8a96-10dfe8ddb454")?}?}?planSummary:?IXSCAN?{?order_number:?1?}?keysExamined:8?docsExamined:8?hasSortStage:1?cursorExhausted:1?numYields:0?nreturned:4?reslen:812?locks:{?Global:?{?acquireCount:?{?r:?1?}?},?Database:?{?acquireCount:?{?r:?1?}?},?Collection:?{?acquireCount:?{?r:?1?}?}?}?protocol:op_msg?0ms??

從上面的日志可以看出,即使是同一個長鏈接上面的多次會話,客戶端也會攜帶多個不同的”lsid”發(fā)送給MongoDB服務(wù)端,因此需要解決為何java服務(wù)同一個鏈接多次訪問會生成多個session id,即”lsid”。
通過排查客戶端,最終定位問題是客戶端的埋點(diǎn)監(jiān)控在升級到MongoDB-4.0后,觸發(fā)每次請求生成一個新的”lsid”。
叮咚買菜自建上云
收益總結(jié)
自建MongoDB遷移騰訊云MongoDB后,帶來了如下收益:
·?通過梳理拆分,把一些核心的復(fù)雜的MongoDB集群,垂直拆分為多個集群,耦合性降低,穩(wěn)定性提高。
·?集群穩(wěn)定性提高,上云前業(yè)務(wù)遇到的各種MongoDB訪問毛刺和抖動問題得到了徹底解決。
·?騰訊云MongoDB相比自建MongoDB性能更好,并能夠充分利用云的彈性擴(kuò)容能力,不用預(yù)留過多的硬件資源,從而節(jié)省了較大成本。
·?騰訊云MongoDB完善的監(jiān)控告警、數(shù)據(jù)備份回檔、跨地域容災(zāi)、實(shí)時巡檢、7x24小時在線服務(wù)等,使得可運(yùn)維性、數(shù)據(jù)安全、故障預(yù)發(fā)現(xiàn)等能力得以增強(qiáng)。
·?遷移到騰訊云,也可以利用騰訊云技術(shù)團(tuán)隊(duì)的技術(shù)優(yōu)勢,幫助分析定位解決一些MongoDB深層次的疑難技術(shù)問題。
?
﹀
﹀
﹀

億級月活全民K歌Feed業(yè)務(wù)在騰訊云MongoDB中的應(yīng)用及優(yōu)化實(shí)踐
↓↓點(diǎn)擊閱讀原文,了解更多優(yōu)惠
