本文主要描述下MySQL8.0在網絡子產品的幾個小優化, 由于本人對server層代碼不熟悉,是以隻是列出自己的了解和相關的patch以及worklog,不做深入詳細實作的解釋,感興趣的可自行從連接配接中找到對應的代碼
admin Port
運維大并發負載資料庫的同學經常會碰到的情況是,max_connection被占滿,甚至root賬戶都無法登陸上去,kill掉這些連結來讓執行個體恢複正常。
Alibaba RDS MySQL的做法是把connection的個數拆分成不同的使用目的,例如系統維護賬戶占用一部分,使用者賬戶占用一部分,兩者不互相影響。
另外一種方式是比較高危的,通過gdb的方式直接進入程序去修改max_connection的值,但注意符号表要編譯到mysqld裡面,不然無法識别。
此外在mariadb/percona server的線程池實作裡,也引入了extra port,當線程池用滿無法登陸時,可以使用extra port來連上執行個體。
在MySQL8.0裡,則引入了admin port的概念,顧名思義,就是單獨開一個端口給管理者用,該特性從8.0.14開始引入。可以說這是個對運維非常有用,關鍵時候可以救命的特性。這個feature由facebook貢獻給上遊
主要包含幾個配置參數:
admin_address: 用于指定管理者發起tcp連接配接的主機位址,可以是ipv4,ipv6, 或者Host name等等,他類似bind-address,但不同的是隻能接受一個ip位址
admin_port: 顧名思義,就是管理者用來連接配接的端口号,注意如果admin_address沒有設定的話,這個端口号是無效的
create_admin_listener_thread: 是否建立一個單獨的listener線程來監聽admin的連結請求,預設值是關閉的,facebook的建議是打開,否則其會使用已有的監聽線程去監聽admin連接配接。該參數同樣需要admin_address打開, 否則沒有任何影響
注意必須要有權限
SERVICE_CONNECTION_ADMIN才能登陸該端口,否則會報錯
根據文檔描述
2,admin port的連接配接個數不受max_connection或者Max_user_connection的限制。
參考文檔
官方文檔 Administrative Connection Management WL#12138: Add Admin Port 相關代碼Multiple addresses for the --bind-address
通常在大規模允許的執行個體上我們不會去設定bind-address, 但在特定場景下還是有用的。從MySQL8.0.13開始,可以通過bind-address設定多個網絡位址,對應release note:
To enable the server to listen on a set of addresses, the bind_address system variable now permits a list of comma-separated IP addresses or host names, not just a single address or name. For details, see Server System Variables.
也就是說如果你想通過bind-address綁定多個位址,需要使用8.0.13及之後的版本, 當然在之前的版本你也可以指定為使用 * 來比對多個位址。
可以混合指定Ipv4和ipv6的位址,例如:
bind_address=198.51.100.20,2001:db8:0:f101::1
bind address參數說明 WL#11652: Support multiple addresses for the --bind-address command option Performance for connect/disconnect
這是一個性能優化,尤其是針對頻繁斷開連結的短連接配接。這是因為MySQL裡是使用一個全局大鎖來保護LOCK_thd_list和LOCK_thd_remove來保護連結連結清單的。
優化的思路其實很簡單直接:就是分區。所有的包括鎖,連結連結清單,COND_thd_list都被分成8個分區(hardcode, 無法配置)來減少沖突, 根據thread id來分區。唯一的負面影響就是出于監控目的,可能performance schema需要擷取全部分區來獲得線程資訊,但通常這是可以容忍的。
WL#9250: Split LOCK_thd_list and LOCK_thd_remove mutexesRemove metadata from resultset
這是個老話題了,我們知道在mysql傳回的結果集了除了使用者的資料外,還包含了庫,表名,列名,甚至表列的别名等資訊,這些資訊占據了傳回值的很大一部分網絡包開銷,特别的,當你需要是點查詢時,可能你的傳回包的中繼資料要遠遠大于你需要的資料,而多數情況下,你并不需要這些中繼資料
例如當你傳回n個列時,中繼資料包含:
- column count (n);
- n * column definitions
而每個column definition包含:
- catalog
- schema
- table alias
- table
- column alias
- column name
etc.
8.0版本裡,你可以選擇的移除resultset的metadata,通過參數resultset_metadata來控制,不過當我登陸終端,想設定這個參數時 卻報錯:
root@(none) 10:15:27>set session resultset_metadata = 'none';
ERROR 3640 (HY000): The client doesn't support optional metadata transfer
這是因為标準用戶端的連接配接沒有打開選項CLIENT_OPTIONAL_RESULTSET_METADATA,如果您使用C API,可以通在調用mysql_real_connect時把該flag設定到參數client_flag中,這樣你就可以可選的設定這個session級别參數來關閉metadata了.
實際上在大概2012年左右,twitter mysql也做過類似的嘗試,我在14年也做過類似的嘗試,當時的測試結果如下:
After porting twitter’s patch ( Great thanks to Davi Arnaut) to MySQL5.6.16, I slightly changed it to make protocol_mode support more options:
0/METADATA_FULL: return all metadata, default value.
1/METADATA_REAL_COLUMN: only column name;
2/METADATA_FAKE_COLUMN: fake column name ,use 1,2...N instead of real column name
3/METADATA_NULL_COLUMN: use NULL to express the metadata information
4/METADATA_IGNORE: ignore metadata information, just for test..
CREATE TABLE `test_meta_impact` (
`abcdefg1` int(11) NOT NULL AUTO_INCREMENT,
`abcdefg2` int(11) DEFAULT NULL,
`abcdefg3` int(11) DEFAULT NULL,
`abcdefg4` int(11) DEFAULT NULL,
……
……
`abcdefg40` int(11) DEFAULT NULL,
PRIMARY KEY (`abcdefg1`)
) ENGINE=InnoDB AUTO_INCREMENT=229361 DEFAULT CHARSET=utf8
mysqlslap --no-defaults -uxx --create-schema=test -h$host -P $port --number-of-queries=1000000000 --concurrency=100 --query='SELECT * FROM test.test_meta_impact where abcdefg1 = 2'
METADATA_FULL : 3.48w TPS, Net send 113M
METADATA_REAL_COLUMN: 7.2W TPS, Net send 111M
METADATA_FAKE_COLUMN: 9.2W TPS , Net send 116M
METADATA_NULL_COLUMN: 9.6w TPS , Net send 115M
METADATA_IGNORE: 13.8w TPS, Net send 30M
可以看到去掉中繼資料後,不但網絡傳輸少了至少三倍多, tps也上升了不少.
WL#8134: Make metadata information transfer optional resultset_metadata C API異步query
從最新的8.0.16版本開始,新的C API開始支援異步的無阻塞的送出查詢,相關的API包括:
mysql_real_connect_nonblocking()
mysql_real_query_nonblocking()
mysql_store_result_nonblocking()
mysql_next_result_nonblocking()
mysql_fetch_row_nonblocking()
mysql_free_result_nonblocking()
函數的名字就是原有阻塞性api加上字尾_nonblocking,比如說如果query的執行時間比較長,你可以先去幹别的事情,然後再回來查詢結果集。當然啦你必須要使用8.0.16或之後的client api
WL#11381: Add asynchronous support into the mysql protocol C API Asynchronous Interface