問題描述 公司項目測試環境調用某些介面的時候,伺服器立即崩潰,並一定時間內無法提供服務。 問題排查 伺服器配置不夠 第一反應是伺服器需要升配啦,花錢解決一切!畢竟測試伺服器配置確實不高,2CPU + 4Gib,能幹啥?不過問題是今天突然發生的,而且說崩就崩。憑著嚴謹的態度,還是要刨根問底地找下問題。 ...
問題描述
公司項目測試環境調用某些介面的時候,伺服器立即崩潰,並一定時間內無法提供服務。
問題排查
伺服器配置不夠
第一反應是伺服器需要升配啦,花錢解決一切!畢竟測試伺服器配置確實不高,2CPU + 4Gib,能幹啥?不過問題是今天突然發生的,而且說崩就崩。憑著嚴謹的態度,還是要刨根問底地找下問題。
查看伺服器負載
free -m
記憶體占用並不大,忘記截圖了,反正看下來不是記憶體過高導致的崩潰
top
資料庫占用CPU過高
連接數過多
業務高峰活躍連接陡增,活躍的連接數是否比平時多很多
SELECT
COUNT(*)
FROM
pg_stat_activity
WHERE
STATE NOT LIKE '%idle';
查詢下來只有3個連接,所以不是連接數導致的CPU過高
慢SQL
如果活躍連接數的變化處於正常範圍,則可能是當時有性能很差的SQL被大量執行。
select
datname,
usename,
client_addr,
application_name,
state,
backend_start,
xact_start,
xact_stay,
query_start,
query_stay,
replace(
query,
chr(10),
' '
) as query
from
(
select
pgsa.datname as datname,
pgsa.usename as usename,
pgsa.client_addr client_addr,
pgsa.application_name as application_name,
pgsa.state as state,
pgsa.backend_start as backend_start,
pgsa.xact_start as xact_start,
extract(
epoch
from
(now() - pgsa.xact_start)
) as xact_stay,
pgsa.query_start as query_start,
extract(
epoch
from
(now() - pgsa.query_start)
) as query_stay,
pgsa.query as query
from
pg_stat_activity as pgsa
where
pgsa.state != 'idle'
and pgsa.state != 'idle in transaction'
and pgsa.state != 'idle in transaction (aborted)'
) idleconnections
order by
query_stay desc
limit
5;
可以看到,確實有一條慢SQL,而且屬於奇慢無比,執行了接近1分鐘還沒執行完畢,基本可以定位,是慢SQL導致的CPU占用陡增。
問題解決
對於上面的方法查出來的慢SQL,首先需要做的是Kill掉他們,使業務先恢復。
select pg_cancel_backend(pid) from pg_stat_activity where query like '%<query text>%' and pid != pg_backend_pid();
select pg_terminate_backend(pid) from pg_stat_activity where query like '%<query text>%' and pid != pg_backend_pid();
如果這些SQL確實是業務上必需的,則需要對他們做如下優化:
- 對查詢涉及的表,執行
ANALYZE <table>
或VACUUM ANZLYZE <table>
,更新表的統計信息,使查詢計劃更準確。為避免對業務影響,最好在業務低峰執行。 - 執行
explain <query text>
或explain (buffers true, analyze true, verbose true) <query text>
命令,查看SQL的執行計劃(前者不會實際執行SQL,後者會實際執行而且能得到詳細的執行信息),對其中的Table Scan涉及的表,建立索引。 - 重新編寫SQL,去除掉不必要的子查詢、改寫UNION ALL、使用JOIN CLAUSE固定連接順序等,都是進一步深度優化SQL的手段,這裡不再深入說明。
總結
在查詢語句中,儘量減少不必要的子查詢,公司使用的ORM框架是Spring JPA,針對一些特別慢的HQL,可以採用直接執行SQL的方式來優化查詢效率。
@Query(value = "select count(*) from example_table where example_id = :exampleId", nativeQuery = true)
int exampleNativeQuery(@Param("exampleId") Long exampleId);
參考
PostgreSQL/PPAS CPU使用率高的原因及解決辦法