問題描述:
平臺升級后,用戶登陸平臺成功后沒多久就自動退出,HTTP Request返回401 session expired。
問題定位:
token expired的原因:
由于memcached在環境中是3副本做了HA,所以首先通過tcpdump抓包排除了由于endpoint發生變化導致讀寫的memcached pod不一致的情況。
后通對memcached的pod抓包分析用戶登陸成功到session expired期間數據包發現用戶登陸后session set成功,但是當get session時剛開始成功,后面就無法get到session數據。
經過比對session數據大小并cachedump slab item信息發現session數據成功寫入slab 26,slab 26 每頁容量為38,而slab26 只有1頁所以slab 26總容量為38。session數據寫入后,由于一直有keystone的token數據(數據大小和session數據大小接近,所以也存放與slab 26)寫入,所以根據LRU策略,session數據被剔除,導致session expired。
slab 26只有1頁,沒申請新的page原因:
stats slabs查看slab信息發現slab 24, 25分別占用了277M和710M內存,而memcached總內存為1G,加上其他slab內存使用已經沒有足夠的內存給slab 26來申請新的page。
其中slab 24, 25中雖然有不少過期數據,但是由于page劃分給slab后不會被回收,導致內存無法釋放,所以沒有足夠的內存給slab 26擴充新page限制了slab 26的容量。
memcached內存分配方式
memcached里抽象了slab的概念,一個slab是1個或多個page的集合,每一個page又根據chunk_size劃分出多個chunk用于存儲數據。
其中一個page默認大小是1M,最小chunk_size默認為88 bytes,40 bytes用于存放value,48 bytes用于存放key。當往memcache里面set數據時
- 根據數據找到合適大小的slab,并選擇slab中的page將數據寫入page中的chunk中。(eg:slab的chunk_size為110 bytes,48 bytes用于存放key,后面62 bytes用于存放value,當數據key大小1byte value大小為60 bytes時就會放入這個slab的page中,其中就有49 bytes內存浪費了,所以就是為什么給了memcache 1G內存,但是實際還沒存1G的數據就會發生evicated)。
- 如果這個slab里面沒有剩余的chunk,那會新申請一個page給這個slab(page申請后就屬于這個slab不會被釋放,即使page中沒有任何數據),后將數據寫入這個page。
- 如果當前沒有合適大小的slab,則根據chunk_size依次乘以chunk size的增長系數,直到chunk size大小合適后,新建一個slab,并申請一個page到這個slab,將數據寫入。
- 如果修改key對應的value,當value大小發生變化時,會重新找到合適大小的slab來存儲數據。
memcached數據過期機制:
數據過期后,memcache不會立即將數據刪除,而是當訪問數據時發現如果數據過期后才將數據刪除(因此當數據過期后cachedump后可以看見數據的item,但是get不到任何數據)。
參考文檔:
memcache 內存分配
memcache 過期機制