最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

從 MySQL源碼分析網(wǎng)絡(luò)IO模型

 更新時(shí)間:2023年06月02日 14:48:43   作者:無(wú)毀的湖光-Al  
這篇文章主要為大家介紹了從 MySQL源碼分析網(wǎng)絡(luò)IO模型,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

MySQL 是當(dāng)今最流行的開(kāi)源數(shù)據(jù)庫(kù),閱讀其源碼是一件大有裨益的事情 (雖然其代碼感覺(jué)比較凌亂)。而筆者閱讀一個(gè) Server 源碼的習(xí)慣就是先從其網(wǎng)絡(luò) IO 模型看起。于是,便有了本篇博客。

MySQL 啟動(dòng) Socket 監(jiān)聽(tīng)

看源碼,首先就需要找到其入口點(diǎn),mysqld 的入口點(diǎn)為 mysqld_main, 跳過(guò)了各種配置文件的加載 之后,我們來(lái)到了 network_init 初始化網(wǎng)絡(luò)環(huán)節(jié),如下圖所示:

下面是其調(diào)用棧:

mysqld_main (MySQL Server Entry Point)
	|-network_init (初始化網(wǎng)絡(luò))
		/* 建立tcp套接字 */
		|-create_socket (AF_INET)
		|-mysql_socket_bind (AF_INET)
		|-mysql_socket_listen (AF_INET)
		/* 建立UNIX套接字*/
		|-mysql_socket_socket (AF_UNIX)
		|-mysql_socket_bind (AF_UNIX)
		|-mysql_socket_listen (AF_UNIX)

值得注意的是,在 tcp socket 的初始化過(guò)程中,考慮到了 ipv4/v6 的兩種情況:

// 首先創(chuàng)建ipv4連接
ip_sock= create_socket(ai, AF_INET, &a);
// 如果無(wú)法創(chuàng)建ipv4連接,則嘗試創(chuàng)建ipv6連接
if(mysql_socket_getfd(ip_sock) == INVALID_SOCKET)
 	ip_sock= create_socket(ai, AF_INET6, &a);

如果我們以很快的速度 stop/start mysql, 會(huì)出現(xiàn)上一個(gè) mysql 的 listen port 沒(méi)有被 release 導(dǎo)致無(wú)法當(dāng)前 mysql 的 socket 無(wú)法 bind 的情況,在此種情況下 mysql 會(huì)循環(huán)等待,其每次等待時(shí)間為當(dāng)前重試次數(shù) retry * retry/3 +1 秒,一直到設(shè)置的 --port-open-timeout (默認(rèn)為 0) 為止,如下圖所示: 

MySQL 新建連接處理循環(huán)

通過(guò) handle_connections_sockets 處理 MySQL 的新建連接循環(huán),根據(jù)操作系統(tǒng)的配置通過(guò) poll/select 處理循環(huán) (非 epoll, 這樣可移植性較高,且 mysql 瓶頸不在網(wǎng)絡(luò)上)。
MySQL 通過(guò)線(xiàn)程池的模式處理連接 (一個(gè)連接對(duì)應(yīng)一個(gè)線(xiàn)程,連接關(guān)閉后將線(xiàn)程歸還到池中), 如下圖所示: 


對(duì)應(yīng)的調(diào)用棧如下所示:

handle_connections_sockets
	|->poll/select
	|->new_sock=mysql_socket_accept(...sock...) /*從listen socket中獲取新連接*/
	|->new THD 連接線(xiàn)程上下文 /* 如果獲取不到足夠內(nèi)存,則shutdown new_sock*/
	|->mysql_socket_getfd(sock) 從socket中獲取
		/** 設(shè)置為NONBLOCK和環(huán)境有關(guān) **/
	|->fcntl(mysql_socket_getfd(sock), F_SETFL, flags | O_NONBLOCK);
	|->mysql_socket_vio_new
		|->vio_init (VIO_TYPE_TCPIP)
			|->(vio->write = vio_write)
			/* 默認(rèn)用的是vio_read */
			|->(vio->read=(flags & VIO_BUFFERED_READ) ?vio_read_buff :vio_read;)
			|->(vio->viokeepalive = vio_keepalive) /*tcp層面的keepalive*/
			|->.....
	|->mysql_net_init
		|->設(shè)置超時(shí)時(shí)間,最大packet等參數(shù)
	|->create_new_thread(thd) /* 實(shí)際是從線(xiàn)程池拿,不夠再新建pthread線(xiàn)程 */
		|->最大連接數(shù)限制
		|->create_thread_to_handle_connection
			|->首先看下線(xiàn)程池是否有空閑線(xiàn)程
				|->mysql_cond_signal(&COND_thread_cache) /* 有則發(fā)送信號(hào) */
			/** 這邊的hanlde_one_connection是mysql連接的主要處理函數(shù) */
			|->mysql_thread_create(...handle_one_connection...)

MySQL 的 VIO

如上圖代碼中,每新建一個(gè)連接,都隨之新建一個(gè) vio (mysql_socket_vio_new->vio_init), 在 vio_init 的過(guò)程中,初始化了一堆回掉函數(shù),如下圖所示: 


我們關(guān)注點(diǎn)在 vio_read 和 vio_write 上,如上面代碼所示,在筆者所處機(jī)器的環(huán)境下將 MySQL 連接的 socket 設(shè)置成了非阻塞模式 (O_NONBLOCK) 模式。所以在 vio 的代碼里面采用了 nonblock 代碼的編寫(xiě)模式,如下面源碼所示:

vio_read

size_t vio_read(Vio *vio, uchar *buf, size_t size)
{
  while ((ret= mysql_socket_recv(vio->mysql_socket, (SOCKBUF_T *)buf, size, flags)) == -1)
  {
    ......
    // 如果上面獲取的數(shù)據(jù)為空,則通過(guò)select的方式去獲取讀取事件,并設(shè)置超時(shí)timeout時(shí)間
    if ((ret= vio_socket_io_wait(vio, VIO_IO_EVENT_READ)))
        break;
  }
}

即通過(guò) while 循環(huán)去讀取 socket 中的數(shù)據(jù),如果讀取為空,則通過(guò) vio_socket_io_wait 去等待 (借助于 select 的超時(shí)機(jī)制), 其源碼如下所示:

vio_socket_io_wait
	|->vio_io_wait
		|-> (ret= select(fd + 1, &readfds, &writefds, &exceptfds, 
              (timeout >= 0) ? &tm : NULL))

筆者在 jdk 源碼中看到 java 的 connection time out 也是通過(guò)這,select (...wait_time) 的方式去實(shí)現(xiàn)連接超時(shí)的。
由上述源碼可以看出,這個(gè) mysql 的 read_timeout 是針對(duì)每次 socket recv (而不是整個(gè) packet 的),所以可能出現(xiàn)超過(guò) read_timeout MySQL 仍舊不會(huì)報(bào)錯(cuò)的情況,如下圖所示: 

vio_write

vio_write 實(shí)現(xiàn)模式和 vio_read 一致,也是通過(guò) select 來(lái)實(shí)現(xiàn)超時(shí)時(shí)間的判定,如下面源碼所示:

size_t vio_write(Vio *vio, const uchar* buf, size_t size)
{
  while ((ret= mysql_socket_send(vio->mysql_socket, (SOCKBUF_T *)buf, size, flags)) == -1)
  {
    int error= socket_errno;
    /* The operation would block? */
    // 處理EAGAIN和EWOULDBLOCK返回,NON_BLOCK模式都必須處理
    if (error != SOCKET_EAGAIN && error != SOCKET_EWOULDBLOCK)
      break;
    /* Wait for the output buffer to become writable.*/
    if ((ret= vio_socket_io_wait(vio, VIO_IO_EVENT_WRITE)))
      break;
  }
}

MySQL 的連接處理線(xiàn)程

從上面的代碼:

mysql_thread_create(...handle_one_connection...)

可以發(fā)現(xiàn),MySQL 每個(gè)線(xiàn)程的處理函數(shù)為 handle_one_connection, 其過(guò)程如下圖所示:

代碼如下所示:

for(;;){
	// 這邊做了連接的handshake和auth的工作
	rc= thd_prepare_connection(thd);
	// 和通常的線(xiàn)程處理一樣,一個(gè)無(wú)限循環(huán)獲取連接請(qǐng)求
	while(thd_is_connection_alive(thd))
	{
		if(do_command(thd))
			break;
	}
	// 出循環(huán)之后,連接已經(jīng)被clientdu端關(guān)閉或者出現(xiàn)異常
	// 這邊做了連接的銷(xiāo)毀動(dòng)作
	end_connection(thd);
end_thread:
	...
	// 這邊調(diào)用end_thread做清理動(dòng)作,并將當(dāng)前線(xiàn)程返還給線(xiàn)程池重用
	// end_thread對(duì)應(yīng)為one_thread_per_connection_end
	if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0))
		return;	
	...
	// 這邊current_thd是個(gè)宏定義,其實(shí)是current_thd();
	// 主要是從線(xiàn)程上下文中獲取新塞進(jìn)去的thd
	// my_pthread_getspecific_ptr(THD*,THR_THD);
	thd= current_thd;
	...
}

mysql 的每個(gè) woker 線(xiàn)程通過(guò)無(wú)限循環(huán)去處理請(qǐng)求。

線(xiàn)程的歸還過(guò)程

MySQL 通過(guò)調(diào)用 one_thread_per_connection_end (即上面的 end_thread) 去歸還連接。

MYSQL_CALLBACK_ELSE(...end_thread)
	one_thread_per_connection_end
		|->thd->release_resources()
		|->......
		|->block_until_new_connection

線(xiàn)程在新連接尚未到來(lái)之前,等待在信號(hào)量上 (下面代碼是 C/C++ mutex condition 的標(biāo)準(zhǔn)使用模式):

static bool block_until_new_connection()
{	
	mysql_mutex_lock(&LOCK_thread_count);
	......
    while (!abort_loop && !wake_pthread && !kill_blocked_pthreads_flag)
      mysql_cond_wait(&x1, &LOCK_thread_count);
   ......
   // 從等待列表中獲取需要處理的THD
   thd= waiting_thd_list->front();
   waiting_thd_list->pop_front();
   ......
   // 將thd放入到當(dāng)前線(xiàn)程上下文中
   // my_pthread_setspecific_ptr(THR_THD,  this)    
   thd->store_globals();
   ......
   mysql_mutex_unlock(&LOCK_thread_count);
   .....
}

整個(gè)過(guò)程如下圖所示:


由于 MySQL 的調(diào)用棧比較深,所以將 thd 放入線(xiàn)程上下文中能夠有效的在調(diào)用棧中減少傳遞參數(shù)的數(shù)量。

總結(jié)

MySQL 的網(wǎng)絡(luò) IO 模型采用了經(jīng)典的線(xiàn)程池技術(shù),雖然性能上不及 reactor 模型,但好在其瓶頸并不在網(wǎng)絡(luò) IO 上,采用這種方法無(wú)疑可以節(jié)省大量的精力去專(zhuān)注于處理 sql 等其它方面的優(yōu)化。

以上就是從 MySQL源碼分析網(wǎng)絡(luò)IO模型的詳細(xì)內(nèi)容,更多關(guān)于 MySQL網(wǎng)絡(luò)IO模型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 在EF中使用MySQL的方法及常見(jiàn)問(wèn)題

    在EF中使用MySQL的方法及常見(jiàn)問(wèn)題

    這篇文章主要介紹了在EF中使用MySQL的方法及常見(jiàn)問(wèn)題 的相關(guān)資料,需要的朋友可以參考下
    2016-06-06
  • mysql 多個(gè)字段實(shí)現(xiàn)逗號(hào)拼接

    mysql 多個(gè)字段實(shí)現(xiàn)逗號(hào)拼接

    在MySQL數(shù)據(jù)庫(kù)中,有時(shí)候我們需要將多個(gè)字段的值連接在一起,本文主要介紹了mysql 多個(gè)字段實(shí)現(xiàn)逗號(hào)拼接,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • 解決Navicat導(dǎo)入DBF中文亂碼的問(wèn)題

    解決Navicat導(dǎo)入DBF中文亂碼的問(wèn)題

    在使用Navicat導(dǎo)入DBF文件到Oracle數(shù)據(jù)庫(kù)時(shí),如果遇到中文亂碼問(wèn)題,需要在導(dǎo)入時(shí)指定正確的編碼格式,通常是GBK,否則,可能會(huì)出現(xiàn)部分字符亂碼的情況
    2025-12-12
  • count(1)、count(*)與count(列名)的執(zhí)行區(qū)別詳解

    count(1)、count(*)與count(列名)的執(zhí)行區(qū)別詳解

    這篇文章主要介紹了count(1)、count(*)與count(列名)的執(zhí)行區(qū)別詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • MySQL強(qiáng)制索引中USE/FORCE INDEX用法與避坑

    MySQL強(qiáng)制索引中USE/FORCE INDEX用法與避坑

    本文主要介紹了MySQL強(qiáng)制索引中USE/FORCE INDEX用法與避坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2026-03-03
  • 在DataGrip中操作MySQL完整流程步驟(從登錄到數(shù)據(jù)查詢(xún))

    在DataGrip中操作MySQL完整流程步驟(從登錄到數(shù)據(jù)查詢(xún))

    DataGrip是JetBrains公司出品的一款現(xiàn)代化數(shù)據(jù)庫(kù)管理工具,支持多種數(shù)據(jù)庫(kù)系統(tǒng),包括MySQL,這篇文章主要介紹了在DataGrip中操作MySQL完整流程步驟的相關(guān)資料,需要的朋友可以參考下
    2025-11-11
  • MySQL視圖的概念和操作函數(shù)詳解

    MySQL視圖的概念和操作函數(shù)詳解

    對(duì)MySQL數(shù)據(jù)庫(kù)的查詢(xún),除了基本的查詢(xún)外,有時(shí)候需要對(duì)查詢(xún)的結(jié)果集進(jìn)行處理,下面這篇文章主要給大家介紹了關(guān)于MySQL數(shù)據(jù)庫(kù)基本SQL語(yǔ)句教程之高級(jí)操作中視圖的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • MySQL提示表不存在的解決error:1146:Table doesn‘t exist的原因和解決方法

    MySQL提示表不存在的解決error:1146:Table doesn‘t exist的原因和解決

    在使用MySQL的過(guò)程中,有時(shí)會(huì)遇到“Table doesn't exist”(表不存在)的錯(cuò)誤,錯(cuò)誤代碼通常為1146,這個(gè)問(wèn)題可能由多種原因引起,本文將幫助你診斷和解決這個(gè)問(wèn)題,如果遇到同樣問(wèn)題的小伙伴跟著小編一起來(lái)看看吧
    2024-12-12
  • 遠(yuǎn)程連接mysql數(shù)據(jù)庫(kù)注意點(diǎn)記錄

    遠(yuǎn)程連接mysql數(shù)據(jù)庫(kù)注意點(diǎn)記錄

    有時(shí)候我們需要遠(yuǎn)程連接mysql數(shù)據(jù)庫(kù),那么就需要注意如下問(wèn)題,需要的朋友可以參考下
    2012-08-08
  • MySQL大小寫(xiě)敏感的注意事項(xiàng)

    MySQL大小寫(xiě)敏感的注意事項(xiàng)

    MySQL 在 Windows 下不區(qū)分大小寫(xiě),但在 Linux 下默認(rèn)是區(qū)分大小寫(xiě)。如果你稍加不注意就會(huì)出現(xiàn)在本機(jī)開(kāi)發(fā)的程序運(yùn)行一切正常,發(fā)布到服務(wù)器行就出現(xiàn)表名找不到的問(wèn)題,一頭霧水。
    2021-05-05

最新評(píng)論

历史| 府谷县| 巩留县| 拉孜县| 泗阳县| 义乌市| 元谋县| 南川市| 永昌县| 常州市| 长子县| 铜梁县| 施秉县| 汝城县| 赣榆县| 黑龙江省| 永德县| 阿尔山市| 吉林省| 宣武区| 张掖市| 云霄县| 鄂温| 上虞市| 惠州市| 盘锦市| 兴国县| 阜城县| 同心县| 文水县| 通化县| 噶尔县| 赤壁市| 洪洞县| 江西省| 浏阳市| 大埔区| 河北省| 乌审旗| 石林| 枣阳市|