當出現memory temporarily unavailable報錯時,首先根據報錯信息確認具體是哪個cn/dn報的,如果報錯信息沒有類似dnxxxx_xxxx這樣的信息,就是cn報的,需要去每個cn的日誌里排查是哪個cn。 ...
本文分享自華為雲社區《DWS臨時記憶體不可用報錯: memory temporarily unavailable》,作者:漫天。
1、定位報錯的DN/CN
當出現memory temporarily unavailable報錯時,首先根據報錯信息確認具體是哪個cn/dn報的,如果報錯信息沒有類似dnxxxx_xxxx這樣的信息,就是cn報的,需要去每個cn的日誌里排查是哪個cn。
2、DWS813以前的版本記憶體報錯定位
通過free -g或者top命令查看操作系統記憶體使用情況,確認是操作系統記憶體耗盡導致,還是cn/dn的記憶體使用達到限制,導致記憶體可不用報錯。如果沒有現場,需要查看操作系統的記憶體監控。
如果是cn/dn的記憶體使用達到限制,可以按照以下步驟定位:
步驟一:分析記憶體視圖 pv_total_memory_detail(實例級別記憶體視圖)
select * from pv_total_memory_detail ;
判斷(1)如果dynamic_peak_memory大於max_dynamic_memory,說明是cn/dn dynamic記憶體使用達到上限,導致記憶體可不用報錯。PS:要求歷史上dynamic_peak_memory 沒有超過max_dynamic_memory,即dynamic_peak_memory 首次超過max_dynamic_memory時,該判斷方式有效。
判斷(2)dynamic_used_memory接近max_dynamic_memory,大概率是cn/dn dynamic記憶體使用達到上限,導致記憶體可不用報錯。
判斷(3)比較dynamic_used_memory、dynamic_used_shrctx、sctpcomm_used_memory大小,如果dynamic_used_shrctx非常大,說明多線程共用的動態記憶體太大,如果sctpcomm_used_memory非常大,說明通信庫使用的記憶體非常大,如果dynamic_used_shrctx和sctpcomm_used_memory都很小,說明session占用的記憶體最多。
步驟二:分析記憶體視圖 pv_session_memory_detail(會話級別記憶體視圖)和活躍會話視圖 pg_stat_activity
執行如下SQL-X,查看每個session占用的記憶體大小:
-- 查看每個session占用的記憶體大小 select split_part(pv_session_memory_detail.sessid,'.',2) pid,pg_size_pretty(sum(totalsize)) total_size,count(*) context_count from pv_session_memory_detail group by pid order by sum(totalsize) desc;
如果SQL-X查詢結果中,某個session占用記憶體特別高,說明該session上執行的SQL占用記憶體過高,可以找到對應的SQL,殺掉該語句併進行整改:
-- 查看語句占用記憶體大小 select b.state, a.sessid, b.query_id, substr(b.query,1,80) as query, sum(totalsize) as totalsize, sum(freesize), sum(usedsize) from pv_session_memory_detail a, pg_stat_activity b where split_part(a.sessid,'.',2) = b.pid group by state,sessid,query_id,query order by totalsize desc limit 100; -- 到對應的CN節點查殺對應的線程 select pg_terminate_backend(pid);
如果SQL-X查詢結果中,每個session占用記憶體都不大,但session總量大,大概是空閑線程太多導致dynamic記憶體較高。
-- 查看idle線程數量 SELECT count(*) FROM pg_stat_activity WHERE state='idle'; -- 查看各個線程狀態的記憶體使用 select b.state, sum(totalsize) as totalsize, sum(freesize) as freesize, sum(usedsize) as usedsize from pv_session_memory_detail a, pg_stat_activity b where split_part(a.sessid,'.',2) = b.pid group by b.state order by totalsize desc limit 100; -- 查詢各個應用的記憶體使用 select b.application_name, sum(totalsize) as totalsize, sum(freesize) as freesize, sum(usedsize) as usedsize from pv_session_memory_detail a, pg_stat_activity b where split_part(a.sessid,'.',2) = b.pid group by b.application_name order by totalsize desc limit 100; -- 查詢各個用戶的記憶體使用 select b.usename, sum(totalsize) as totalsize from pv_session_memory_detail a, pg_stat_activity b where split_part(a.sessid,'.',2) = b.pid group by b.usename order by totalsize desc limit 30; -- 查看各類型等待線程的數量 select wait_status,wait_event,count(*) from pg_thread_wait_status group by 1,2 order by 3 desc;
如果是空閑用戶線程導致dynamic記憶體高,可以臨時清理下空閑用戶線程:
gsql -d postgres -p 25308 -c "clean connection to all for database xxx;"
如果是空閑stream線程導致dynamic記憶體高,可以將參數max_stream_pool改小(stream線程池的作用是緩存stream線程,stream線程是用來進行dn之間數據的傳輸,一般多表join的時候stream線程會較多),減小max_stream_pool的影響是短查詢的性能會降低,對複雜查詢幾乎沒影響。
3、DWS813及以後的版本記憶體報錯定位
可以使用813以前版本的定位方式,也可以使用下麵的方式。
步驟一:查看報錯日誌
813及以上版本會列印出debug的信息,可以通過搜關鍵字abnormal來找到當時使用最高的語句,找到thread id,再查找thread id 找到對應query id
步驟二:查看topsql
上一步可以找到占用記憶體最大的sql,如果該sql占用記憶體確實很大,通過topsql查找對應的query id,從而找到對應的SQL語句,並通過unique_sql_id找到同一類型的SQL,進行分析整改。
如果不是某個sql占用記憶體太高導致,分析方法和813以前的版本一樣。
另外,813及以後的版本可以使用如下方式清理空閑用戶線程:
-- 清理空閑用戶線程,每次清1/4 -- 顯示清理了多少,還剩多少 select * from pgxc_clean_free_conn;