GreatSQL連接數(shù)被打滿的三種緊急解決方案
背景
使用數(shù)據(jù)庫時,偶爾會出現(xiàn)數(shù)據(jù)庫連接數(shù)飆升的場景,最嚴(yán)重的情況是連接打滿,root 用戶無法獲取到連接,登陸數(shù)據(jù)庫失敗。這時候無法登錄數(shù)據(jù)庫kill 掉相關(guān)的數(shù)據(jù)連接,影響數(shù)據(jù)庫的穩(wěn)定性。下面將對這類的故障的處理進行詳述。
場景復(fù)現(xiàn)
- 設(shè)置
max_connections
為500
greatsql> SET GLOBAL max_connections=500;
Query OK, 0 rows affected (0.00 sec)
greatsql> SHOW variables LIKE '%max_connections%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| max_connections | 500 |
+------------------------+-------+
1 rows in set (0.01 sec)
2. 使用sysbench 模擬并發(fā)連接
準(zhǔn)備測試數(shù)據(jù)
$ sysbench oltp_read_write --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=yourusername --mysql-password=yourpassword --mysql-db=sysbench_test --tables=10 --table-size=10000 prepare
執(zhí)行測試
$ sysbench oltp_read_write --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=yourusername --mysql-password=yourpassword --mysql-db=sysbench_test --tables=10 --table-size=10000 --time=600 --threads=500 --report-interval=10 run
3. 嘗試登錄數(shù)據(jù)庫報錯
$ /greatsql/svr/greatsql/bin/mysql -h127.0.0.1 -uroot -p
ERROR 1040 (HY000): Too many connections
場景分析
方案1:使用 admin_port 進行故障處理
從GreatSQL 8.0起,GreatSQL 支持在配置文件配置admin_port
和admin_address
,這兩個參數(shù)都是靜態(tài)參數(shù),不支持動態(tài)修改。admin_address
沒有默認(rèn)值,如果沒有顯式開啟,GreatSQL 不會維護任何的管理接口。admin_port
即為管理接口連接TCP/IP的端口號,默認(rèn)值為33062。
注意如果
admin_address
未設(shè)置,admin_port
也將無效。
- 查看配置文件是否配置了
admin_address
和admin_port
$cat /greatsql/conf/greatsql.cnf | grep admin
admin_address='127.0.0.1'
admin_port=3806
- 使用
admin_port
和admin_address
嘗試登錄數(shù)據(jù)庫,登錄成功。
$ /greatsql/svr/greatsql/bin/mysql -h127.0.0.1 -uroot -p -P3806
greatsql > SELECT count(*),User FROM information_schema.processlist GROUP BY 2 ;
+----------+-----------------+
| count(*) | User |
+----------+-----------------+
| 1 | root |
| 500 | test_user |
+----------+-----------------+
1 row in set (0.00 sec)
- 使用kill 將特定條件的連接殺掉,如果處于業(yè)務(wù)高峰期,也可以先使用SET GLOBAL max_connections=[value]臨時調(diào)大連接數(shù),要注意此時資源的負(fù)載情況。 下面表示的是查找出語句正在運行中且執(zhí)行時間超過100s,用戶叫test_user 的id 并拼裝成kill 語句。請結(jié)合實際場景進行條件調(diào)整。
greatsql> SELECT concat("kill ",id,";") FROM information_schema.processlist
WHERE info IS NOT NULL
AND command != 'sleep'
AND time>100
AND USER ='test_user';
方案2:GDB 在線關(guān)閉 TCP SOCKET
上述演示是最為順利的情況,但是不妨假設(shè),如果未設(shè)置admin_address
和admin_port
,難道只有剩下數(shù)據(jù)庫重啟這種方式了嗎?
其實不然,GreatSQL 默認(rèn)使用 TCP/IP 進行網(wǎng)絡(luò)連接,那是否可以把實例上由遠(yuǎn)程機器請求的部分 TCP socket 連接 kill 掉,但保持?jǐn)?shù)據(jù)庫進程的運行,以騰出部分連接數(shù)供 root 用戶登錄呢?
通過查閱資料, gdb attach
剛好能滿足我們的設(shè)想:gdb attach
是 GDB(GNU 調(diào)試器)中的一個命令,用于附加(Attach)到一個正在運行的進程,而關(guān)閉一個 SOCKET,只要調(diào)用 close 函數(shù)就可以了。簡單來說就是使用gdb attach
到進程上下文,然后 call close($fd)
。
不過需要注意的是,gdb attach
會暫停目標(biāo)進程的所有線程。對于生產(chǎn)環(huán)境中需要持續(xù)運行的進程(如服務(wù)、數(shù)據(jù)庫、實時系統(tǒng)等),這種暫??赡軐?dǎo)致服務(wù)中斷或超時。且gdb 需要較大的性能開銷,進程的運行速度會顯著下降,需要經(jīng)過謹(jǐn)慎評估后再使用此方式。
繼續(xù)復(fù)現(xiàn)一個連接數(shù)打滿,但是未啟用admin_address
和 admin_port
的場景,此時嘗試用管理員登錄報錯ERROR 1040 (HY000): Too many connections 。
- 通過 netstat 反查數(shù)據(jù)庫的進程號為18979
$ netstat -nltp | grep 3306
tcp6 0 0 :::3306 :::* LISTEN 18979/mysqld
2. 使用 lsof 找到進程號18979的文件描述,并找到對應(yīng)的 socket
$ lsof -np 18979
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 18979 greatsql cwd DIR 8,2 4096 4971025 /greatsql/dbdata/data3306/data
mysqld 18979 greatsql rtd DIR 8,2 4096 2 /
mysqld 18979 greatsql txt REG 8,2 1241425104 4971271 /greatsql/svr/GreatSQL-8.0.32-27-Linux-glibc2.17-x86_64/bin/mysqld
mysqld 18979 greatsql mem REG 8,2 37216 688174 /usr/lib64/libnss_sss.so.2
...
mysqld 18979 greatsql 371u IPv6 31132193 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40694 (ESTABLISHED)
mysqld 18979 greatsql 372u IPv6 31132194 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40698 (ESTABLISHED)
mysqld 18979 greatsql 373u IPv6 31132195 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40700 (ESTABLISHED)
mysqld 18979 greatsql 374u IPv6 31132196 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40702 (ESTABLISHED)
mysqld 18979 greatsql 375u IPv6 31132197 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40704 (ESTABLISHED)
mysqld 18979 greatsql 376u IPv6 31132198 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40706 (ESTABLISHED)
mysqld 18979 greatsql 377u IPv6 31132201 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40708 (ESTABLISHED)
...
其中NODE NAME 有(ESTABLISHED)表示兩臺主機TCP連接已經(jīng)成功建立。找到對應(yīng)的 FD,并記錄下來
3. 使用 gdb 連接到進程,并關(guān)閉 socket 連接
$ gdb -p 18979
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.el7
Copyright (C) 2013 Free Software Foundation, Inc
....
Loaded symbols for /greatsql/svr/greatsql/lib/mysql/libjemalloc.so
Reading symbols from /lib64/libpthread.so.0...(no debugging symbols found)...done.
$ (gdb) call close(371u)
$1 = 0
$ (gdb) call close(372u)
$1 = 0
....
4. 查看數(shù)據(jù)庫進程,并再次嘗試登錄數(shù)據(jù)庫,登錄成功。
$ ps -ef | grep mysql
greatsql 18979 1 99 13:30 ? 00:48:41 /greatsql/svr/greatsql/bin/mysqld --defaults-file=/greatsql/conf/greatsql.cnf
方案3:預(yù)防性設(shè)置 max_user_connections
以上的情況為出現(xiàn)問題的時候的緊急處理,但作為DBA,保障線上穩(wěn)定性為第一前提,連接數(shù)打滿已經(jīng)是一個較為糟糕的情況了。且前文描述的是數(shù)據(jù)庫大版本為 8.0 后才開始支持admin_address
,admin_port
作為突發(fā)情況的緊急處理。但如果GreatSQL 5.7 或者更低的情況, 如何去保障生產(chǎn)的穩(wěn)定性呢?
GreatSQL 還有一個參數(shù)max_user_connections ,我們來比較max_user_connections
和max_connections
的區(qū)別
- max_connections:代表允許連接數(shù)據(jù)庫的所有用戶的連接數(shù)總和
- max_user_connections:代表允許單個用戶的連接數(shù)最大值,即并發(fā)值
出故障的時候,往往是同一個用戶頻繁的申請連接,那如果我們把單個用戶的最大連接數(shù)調(diào)整到比最大連接數(shù)再小一點的值,確保管理員賬號有足夠的連接數(shù)進行突發(fā)故障的處理。 也可以有效減少連接打滿的情況。且可以動態(tài)調(diào)整,可以使用SET GLOBAL max_user_connections=[value]生效。除此之外,該參數(shù)可針對特定用戶設(shè)置 :如ALTER USER 'test_user'@'%' WITH MAX_USER_CONNECTIONS 100;
總結(jié)
- 無論對于5.7 還是8.0,建議設(shè)置
max_user_connections
降低連接數(shù)打滿的風(fēng)險 - 對于8.0 版本,可以通過配置增加
admin_address
和admin_port
來啟用管理接口,以應(yīng)對突發(fā)的情況。 - 如果出現(xiàn)連接數(shù)打滿且未啟用管理端口的情況,可以使用
gdb attach
在線關(guān)掉部分 socket,以避免數(shù)據(jù)庫的重啟。但需要注意gdb attach
對機器性能的開銷和 gdb 運行時數(shù)據(jù)庫的所有線程都會暫停,請評估后再使用該方式。 - 監(jiān)控可以加上對連接數(shù)的監(jiān)控,達到閾值時告警出來,提前介入處理。
- 事后應(yīng)排查連接數(shù)打滿的原因,和業(yè)務(wù)協(xié)調(diào)優(yōu)化措施,建議采用線程池等技術(shù)提前規(guī)避連接數(shù)飆高的場景。