偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

MySQL 連接怎么?;睿?/h1>

數(shù)據(jù)庫 MySQL
MySQL 系統(tǒng)變量 wait_timeout,默認(rèn)值是 28800 秒(8 小時),用于控制客戶端多長時間沒有給 MySQL 發(fā)送請求,MySQL 就自動斷開連接。

多年前開發(fā)過一個異步發(fā)送訂單短信、郵件通知的??守護(hù)??程序,每次程序啟動時會創(chuàng)建數(shù)據(jù)庫連接,后續(xù)讀寫數(shù)據(jù)庫操作就一直復(fù)用這個連接。

某一天,用戶反饋下單后收不到通知了,我們登錄服務(wù)器看到程序還在運行。

經(jīng)過排查確認(rèn),發(fā)生問題的這天,距離上一次有用戶下單超過了 8 小時,MySQL 服務(wù)端已經(jīng)自動斷開連接了。

解決這個問題的辦法比較簡單,程序只要定期給 MySQL 發(fā)送請求,表示自己還活著,MySQL 就不會觸發(fā)斷開連接的操作了,這就是數(shù)據(jù)庫連接?;畹膽?yīng)用場景。

今天我們來聊聊數(shù)據(jù)庫連接?;畹脑砗头绞?。

本文內(nèi)容基于 MySQL 8.0.29 源碼。

正文

1、概述

MySQL 系統(tǒng)變量 wait_timeout,默認(rèn)值是 28800 秒(8 小時),用于控制客戶端多長時間沒有給 MySQL 發(fā)送請求,MySQL 就自動斷開連接。

如果我們的業(yè)務(wù)系統(tǒng)不那么閑,能隔三差五的給 MySQL 發(fā)送一些請求,數(shù)據(jù)庫連接會一直處于活躍狀態(tài),也就不需要專門?;盍?。

有一些業(yè)務(wù)系統(tǒng),低峰期可能很長時間都不會有讀寫請求,一旦間隔時間超過 wait_timeout,數(shù)據(jù)庫連接就斷開了,連接保活自然不可避免。

接下來我們聊聊 2 種連接?;罘绞?,以及它們之間有什么不一樣,在這之前,我們先來看看 wait_timeout 是怎么控制超時邏輯的。

2、 wait_timeout 超時邏輯

客戶端和 MySQL 建立連接之后,MySQL 每次開始等待客戶端發(fā)送數(shù)據(jù)之前,都會根據(jù)系統(tǒng)變量 ??wait_timeout?? 的值設(shè)置最長等待時間:

bool do_command(THD *thd){
……
net = thd->get_protocol_classic()->get_net();
my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
……
}

上面代碼中的 net_wait_timeout 就是系統(tǒng)變量 wait_timeout 的化身。

設(shè)置最長等待時間之后,接下來就是安靜的等待了,執(zhí)行等待操作的方法是 vio_io_wait():

int vio_socket_io_wait(Vio *vio, enum enum_vio_io_event event){
int timeout, ret;
……
timeout = vio->read_timeout;
……
switch (vio_io_wait(vio, event, timeout)) {
……
case 0:
/* The wait timed out. */
ret = -1;
break;
……
}

return ret;
}

如果達(dá)到了最長等待時間,客戶端一直沒有發(fā)送數(shù)據(jù),vio_io_wait() 會返 0 表示超時。

然后,程序會沿著調(diào)用棧一路返回到 net_read_raw_loop() 方法中,設(shè)置返回給客戶端的錯誤碼 ER_CLIENT_INTERACTION_TIMEOUT(4031),對應(yīng)的錯誤信息為:

The client was disconnected by the server because of inactivity.
See wait_timeout and interactive_timeout for configuring this behavior.

準(zhǔn)備好返回給客戶端的錯誤碼和錯誤信息之后,就會進(jìn)行一系列斷開連接相關(guān)的操作,最后把錯誤碼和錯誤信息發(fā)送給客戶端。

如果我們用的是 MySQL 自帶的交互式客戶端 mysql,發(fā)生超時之后,等下次再執(zhí)行 SQL 語句時,就會看到這樣的錯誤了:

mysql> SET wait_timeout = 10;
10 秒之后......
mysql> SELECT * FROM t1 LIMIT 1;
ERROR 4031 (HY000): The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.
No connection. Trying to reconnect...

對 MySQL 服務(wù)端主動斷開連接過程大概介紹之后,接下來看看 2 種連接?;罘绞健?/p>

3、ping

站在客戶端的視角看,使用 ping 命令是為了判斷 MySQL 服務(wù)端是否還活著。

換一個角度,在 MySQL 服務(wù)端看來,一個客戶端給它發(fā)送了 ping 命令,說明這個客戶端連接還活著,它就不會把這個客戶端的連接關(guān)閉。

所以,ping 命令不但可以用于數(shù)據(jù)庫連接探活,還可以用于?;?。

MySQL 沒有提供 ping 語句,如果想測試發(fā)送 ping 命令,可以使用 mysqladmin:

# 發(fā)送 ping 命令
mysqladmin -h127.0.0.1 -P 3307 -uroot ping
# 收到的結(jié)果(表示 MySQL 服務(wù)端還活著)
mysqld is alive

在數(shù)據(jù)庫連接池或者業(yè)務(wù)系統(tǒng)中,通過程序提供的 API 也能很方便地發(fā)送 ping 命令給 MySQL 服務(wù)端。

在業(yè)務(wù)低峰期,客戶端定時給 MySQL 服務(wù)端發(fā)送 ping 命令,就能給連接?;盍恕?/p>

4、select

另一種連接?;罘绞绞菆?zhí)行 SQL 語句,一般都是 select 語句,可以有各種花樣:

SELECT 1;
SELECT version();
SELECT @@version;
……

執(zhí)行 select 語句?;?,和正常執(zhí)行業(yè)務(wù) SQL 沒什么區(qū)別,這里不展開了。

5、兩種?;罘绞綄Ρ?/h4>

既然 ping 和 select 都能實現(xiàn)數(shù)據(jù)庫連接保活,那它們之間有什么不一樣?

在MySQL 源碼的實現(xiàn)中,體現(xiàn)了 2 點區(qū)別:

區(qū)別 1:ping 是命令,我們只能通過 MySQL 提供的 API,或 mysqladmin 這樣的工具發(fā)送 ping 命令給 MySQL 服務(wù)端。

select 是 SQL 語句,通過 MySQL API 或 mysql 交互式客戶端都能執(zhí)行 select 語句。

區(qū)別 2:ping 的執(zhí)行流程比 select 更短,效率更高,通過對比兩者的調(diào)用棧,我們能更直觀的看到這一點。

兩種方式都會響應(yīng)客戶端請求,后面給出的調(diào)用棧中,把這部分省略了。

ping 命令的主要調(diào)用棧如下:

| > pfs_spawn_thread(void*)
| | > handle_connection(void*)
| | | > do_command(THD*)
| | | | > dispatch_command(THD*, COM_DATA const*, enum_server_command)

ping 命令的調(diào)用棧很簡單,連詞法解析、語法解析過程都不需要,進(jìn)入 dispatch_command() 方法之后,判斷是 ping 命令,就直接給客戶端返回 OK 狀態(tài),整個流程就結(jié)束了:

bool dispatch_command(THD *thd, const COM_DATA *com_data,
enum enum_server_command command){
......
switch (command) {
......
case COM_PING:
thd->status_var.com_other++;
my_ok(thd); // Tell client we are alive
break;
......
}
......
}

接下來是 select 的調(diào)用棧,以最簡單的 SELECT 1 為例,主要調(diào)用棧如下:

SELECT 1 的調(diào)用棧比較長,把主要調(diào)用棧都列出來是為了大家對 SELECT 1 的執(zhí)行過程有更直觀的了解。

| > pfs_spawn_thread(void*)
| | > handle_connection(void*)
| | | > do_command(THD*)
| | | | > dispatch_command(THD*, COM_DATA const*, enum_server_command)
| | | | | > dispatch_sql_command(THD*, Parser_state*)
| | | | | | > parse_sql(THD*, Parser_state*, Object_creation_ctx*)
| | | | | | > mysql_execute_command(THD*, bool)
| | | | | | | > Sql_cmd_dml::execute(THD*)
| | | | | | | | > Sql_cmd_dml::prepare(THD*)
| | | | | | | | | > open_tables_for_query(THD*, TABLE_LIST*, unsigned int)
| | | | | | | | | | > open_tables(...)
| | | | | | | | | | | > lock_table_names(...)
| | | | | | | | | | > open_secondary_engine_tables(THD*, unsigned int)
| | | | | | | | | > Sql_cmd_select::prepare_inner(THD*)
| | | | | | | | | | > Query_block::prepare(THD*, mem_root_deque<Item* > *)
| | | | | | | | | | | > Query_block::setup_tables(THD*, TABLE_LIST*, bool)
| | | | | | | | | | | > setup_fields(...)
| | | | | | | | | | | > Query_block::setup_conds(THD*)
| | | | | | | | | | | > Query_block::resolve_limits(THD*)
| | | | | | | | | | | > Query_block::apply_local_transforms(THD*, bool)
| | | | | | | | | | | | > Query_block::simplify_joins(...)
| | | | | | | | > lock_tables(THD*, TABLE_LIST*, unsigned int, unsigned int)
| | | | | | | | > Sql_cmd_dml::execute_inner(THD*)
| | | | | | | | | > Query_expression::optimize(THD*, TABLE*, bool, bool)
| | | | | | | | | | > Query_block::optimize(THD*, bool)
| | | | | | | | | | | > JOIN::optimize(bool)
| | | | | | | | | | | | > JOIN::make_tmp_tables_info()
| | | | | | | | | | | | > count_field_types(...)
| | | | | | | | | | | | > JOIN::create_access_paths()
| | | | | | | | | | | | | > JOIN::create_root_access_path_for_join()
| | | | | | | | | | | | | > JOIN::attach_access_paths_for_having_and_limit(AccessPath*)
| | | | | | | | | | | | | > JOIN::attach_access_path_for_delete(AccessPath*)
| | | | | | | | | > optimize_secondary_engine(THD*)
| | | | | | | | | > Query_expression::execute(THD*)
| | | | | | | | | | > Query_expression::ExecuteIteratorQuery(THD*)
| | | | | | | | | | | > Query_result_send::send_result_set_metadata(...)
| | | | | | | | | | > Query_expression::ExecuteIteratorQuery(THD*)
| | | | | | | | | | | > FakeSingleRowIterator::Read()
| | | | | | | | | | | > Query_result_send::send_eof(THD*)
| | | | | | | > trans_commit_stmt(THD*, bool)
| | | | | | | | > MYSQL_BIN_LOG::commit(THD*, bool)
| | | | | | | | | > ha_commit_low(THD*, bool, bool)
| | | | | > log_slow_statement(THD*, System_status_var*)

從上面的調(diào)用??梢钥吹?,SELECT 1 雖然不需要從表里查詢數(shù)據(jù),但是詞法解析、語法解析、查詢準(zhǔn)備、查詢優(yōu)化、查詢執(zhí)行、事務(wù)提交、記錄慢 SQL 等等這些流程一個都沒落下,雖然很多方法進(jìn)去之后,并不需要執(zhí)行復(fù)雜的操作,但是各種 if ... else 判斷是少不了要執(zhí)行的。

SELECT 1 是 select 語句最簡單的形式了,如果用其它 select 語句?;?,調(diào)用棧只會更長。

通過上面 ping 命令 和 SELECT 1 的調(diào)用棧對比,相信大家對這兩種保活方式的執(zhí)行效率已經(jīng)有了直觀的了解。

6. 總結(jié)

本文寫作的初衷就是為了對比 ping 和 select 兩種數(shù)據(jù)庫連接?;罘绞降膱?zhí)行效率。

經(jīng)過前面的介紹,我們就可以得出結(jié)論了:
ping 命令的執(zhí)行效率比 select 語句高,對于追求極致性能的應(yīng)用來說,使用 ping 命令給數(shù)據(jù)庫連接?;钍歉玫姆绞健?/p>

本文轉(zhuǎn)載自微信公眾號「一樹一溪」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系一樹一溪公眾號。

責(zé)任編輯:姜華 來源: 一樹一溪
相關(guān)推薦

2019-09-23 08:27:15

TCP長連接心跳

2020-03-19 10:13:13

OkHttpWebSocket

2020-02-25 16:48:35

AndroidGoogle 移動系統(tǒng)

2023-01-26 23:44:41

C++代碼生命周期

2024-10-11 16:57:18

2019-12-31 09:11:01

后臺Android系統(tǒng)

2012-09-27 09:23:34

Google

2016-08-11 09:19:36

AndroidService通訊應(yīng)用

2013-11-06 10:35:34

2019-03-18 10:32:33

容災(zāi)雙活同城

2025-02-07 10:34:26

JAVA程序Java 9

2015-07-28 10:35:02

編程程序員加班

2015-07-28 09:17:47

健康編程

2020-02-12 11:34:56

架構(gòu)平滑上云機(jī)房遷移

2024-08-12 08:04:00

2017-09-11 19:30:44

MySQLCmd命令連接數(shù)據(jù)庫

2019-10-08 13:21:15

MySQL連接數(shù)數(shù)據(jù)庫

2013-07-29 10:10:40

TCP協(xié)議TCP定時器TCP

2020-10-09 18:37:53

等保測評師等保2.0網(wǎng)絡(luò)安全

2015-10-15 09:38:48

TCP網(wǎng)絡(luò)協(xié)議定時器
點贊
收藏

51CTO技術(shù)棧公眾號