从MySQL源码看其网络IO模型「终于解决」

从MySQL源码看其网络IO模型「终于解决」从MySQL源码看其网络IO模型 前言 MySQL是当今最流行的开源数据库,阅读其源码是一件大有裨益的事情(虽然其代码感觉比较凌乱)。而笔者阅读一个Server源码的习惯就是先从其网络IO模型看起。…

	从MySQL源码看其网络IO模型[数据库教程]

从MySQL源码看其网络IO模型

前言

MySQL是当今最流行的开源数据库,阅读其源码是一件大有裨益的事情(虽然其代码感觉比较凌乱)。而笔者阅读一个Server源码的习惯就是先从其网络IO模型看起。于是,便有了本篇博客。

MySQL启动Socket监听

看源码,首先就需要找到其入口点,mysqld的入口点为mysqld_main,跳过了各种配置文件的加载 之后,我们来到了network_init初始化网络环节,如下图所示:
技术图片
下面是其调用栈:

mysqld_main (MySQL Server Entry Point) |-network_init (初始化网络) /* 建立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) 

代码100分

值得注意的是,在tcp socket的初始化过程中,考虑到了ipv4/v6的两种情况:

代码100分// 首先创建ipv4连接 ip_sock= create_socket(ai, AF_INET, &a); // 如果无法创建ipv4连接,则尝试创建ipv6连接 if(mysql_socket_getfd(ip_sock) == INVALID_SOCKET) ip_sock= create_socket(ai, AF_INET6, &a); 

如果我们以很快的速度stop/start mysql,会出现上一个mysql的listen port没有被release导致无法当前mysql的socket无法bind的情况,在此种情况下mysql会循环等待,其每次等待时间为当前重试次数retry * retry/3 +1秒,一直到设置的–port-open-timeout(默认为0)为止,如下图所示: 技术图片

MySQL新建连接处理循环

通过handle_connections_sockets处理MySQL的新建连接循环,根据操作系统的配置通过poll/select处理循环(非epoll,这样可移植性较高,且mysql瓶颈不在网络上)。
MySQL通过线程池的模式处理连接(一个连接对应一个线程,连接关闭后将线程归还到池中),如下图所示: 技术图片
对应的调用栈如下所示:

handle_connections_sockets
	|->poll/select
	|->new_sock=mysql_socket_accept(...sock...) /*从listen socket中获取新连接*/ |->new THD 连接线程上下文 /* 如果获取不到足够内存,则shutdown new_sock*/ |->mysql_socket_getfd(sock) 从socket中获取 /** 设置为NONBLOCK和环境有关 **/ |->fcntl(mysql_socket_getfd(sock), F_SETFL, flags | O_NONBLOCK); |->mysql_socket_vio_new |->vio_init (VIO_TYPE_TCPIP) |->(vio->write = vio_write) /* 默认用的是vio_read */ |->(vio->read=(flags & VIO_BUFFERED_READ) ?vio_read_buff :vio_read;) |->(vio->viokeepalive = vio_keepalive) /*tcp层面的keepalive*/ |->..... |->mysql_net_init |->设置超时时间,最大packet等参数 |->create_new_thread(thd) /* 实际是从线程池拿,不够再新建pthread线程 */ |->最大连接数限制 |->create_thread_to_handle_connection |->首先看下线程池是否有空闲线程 |->mysql_cond_signal(&COND_thread_cache) /* 有则发送信号 */ /** 这边的hanlde_one_connection是mysql连接的主要处理函数 */ |->mysql_thread_create(...handle_one_connection...) 

MySQL的VIO

如上图代码中,每新建一个连接,都随之新建一个vio(mysql_socket_vio_new->vio_init),在vio_init的过程中,初始化了一堆回掉函数,如下图所示: 技术图片
我们关注点在vio_read和vio_write上,如上面代码所示,在笔者所处机器的环境下将MySQL连接的socket设置成了非阻塞模式(O_NONBLOCK)模式。所以在vio的代码里面采用了nonblock代码的编写模式,如下面源码所示:

vio_read

代码100分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) { ...... // 如果上面获取的数据为空,则通过select的方式去获取读取事件,并设置超时timeout时间 if ((ret= vio_socket_io_wait(vio, VIO_IO_EVENT_READ))) break; } } 

即通过while循环去读取socket中的数据,如果读取为空,则通过vio_socket_io_wait去等待(借助于select的超时机制),其源码如下所示:

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

笔者在jdk源码中看到java的connection time out也是通过这,select(…wait_time)的方式去实现连接超时的。
由上述源码可以看出,这个mysql的read_timeout是针对每次socket recv(而不是整个packet的),所以可能出现超过read_timeout MySQL仍旧不会报错的情况,如下图所示: 技术图片

vio_write

vio_write实现模式和vio_read一致,也是通过select来实现超时时间的判定,如下面源码所示:

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的连接处理线程

从上面的代码:

mysql_thread_create(...handle_one_connection...) 

可以发现,MySQL每个线程的处理函数为handle_one_connection,其过程如下图所示:

技术图片
代码如下所示:

for(;;){ // 这边做了连接的handshake和auth的工作 rc= thd_prepare_connection(thd); // 和通常的线程处理一样,一个无限循环获取连接请求 while(thd_is_connection_alive(thd)) { if(do_command(thd)) break; } // 出循环之后,连接已经被clientdu端关闭或者出现异常 // 这边做了连接的销毁动作 end_connection(thd); end_thread: ... // 这边调用end_thread做清理动作,并将当前线程返还给线程池重用 // end_thread对应为one_thread_per_connection_end if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0)) return; ... // 这边current_thd是个宏定义,其实是current_thd(); // 主要是从线程上下文中获取新塞进去的thd // my_pthread_getspecific_ptr(THD*,THR_THD); thd= current_thd; ... } 

mysql的每个woker线程通过无限循环去处理请求。

线程的归还过程

MySQL通过调用one_thread_per_connection_end(即上面的end_thread)去归还连接。

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

线程在新连接尚未到来之前,等待在信号量上(下面代码是C/C++ mutex condition的标准使用模式):

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放入到当前线程上下文中 // my_pthread_setspecific_ptr(THR_THD, this) thd->store_globals(); ...... mysql_mutex_unlock(&LOCK_thread_count); ..... } 

整个过程如下图所示:

技术图片
由于MySQL的调用栈比较深,所以将thd放入线程上下文中能够有效的在调用栈中减少传递参数的数量。

总结

MySQL的网络IO模型采用了经典的线程池技术,虽然性能上不及reactor模型,但好在其瓶颈并不在网络IO上,采用这种方法无疑可以节省大量的精力去专注于处理sql等其它方面的优化。

推荐:中华文学网

从MySQL源码看其网络IO模型

原文地址:https://www.cnblogs.com/1994jinnan/p/13396477.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/6998.html

(0)
上一篇 2023-03-29 13:30
下一篇 2023-03-29 14:30

相关推荐

  • Postgresql中文参考手册「建议收藏」

    Postgresql中文参考手册「建议收藏」http://www.postgres.cn/docs/9.3/index.html http://www.postgres.cn/docs/9.4/index.html http://www.po…

    2023-02-08
    146
  • pyenv安装教程

    pyenv安装教程Python是一门非常流行的脚本语言,用于解决各种问题。由于Python的版本管理问题,往往需要安装多个版本的Python,供不同的应用使用。在地道的Linux环境下,如何高效地管理多个Python版本呢?pyenv是一款非常优秀的Python版本管理工具。本文将带领大家了解pyenv的安装方法和使用技巧。

    2024-07-06
    46
  • stonebase_矿渣社区

    stonebase_矿渣社区我们又把近期的一些社区热点问题做了一次汇总,同步给所有关注StoneDB的同学们~ 提问Qustions & 解答Answers Q:现在StoneDB单机什么硬件规格部署能分析100TB级别

    2023-06-01
    227
  • Python入门指南:学习Python语言基础知识及应用

    Python入门指南:学习Python语言基础知识及应用Python是一种高级编程语言,由Guido van Rossum于1989年底开发,于1991年发布。Python语法简洁清晰,具有丰富和强大的内置数据结构、函数和模块,是一个强大的面向对象编程语言。

    2024-03-20
    79
  • Linux Python版本历史

    Linux Python版本历史在很多操作系统中,Python 都是预装的一种编程语言,因为这门语言已经非常普及了。Linux 是这样一个典型的例子。随着时间的推移,Python 的版本也不断更新,以适应不断变化的编程环境。在本文中,我们将为您介绍 Linux Python 版本历史,并分享 Python 在Linux 中的重要用途。

    2024-08-19
    33
  • 用Python中的beautifulsoup4(bs4)库进行网页解析

    用Python中的beautifulsoup4(bs4)库进行网页解析现代互联网中,网站数量多达数十亿,每一个网站都包含了数量巨大的信息,大部分的信息都被储存在网页中。如果我们希望从网站中获取自己所需要的信息,就需要使用网页解析的技术。在Python中,beautifulsoup4(bs4)是一个强大的网页解析库,它可以轻松地从HTML或XML文件中提取并筛选信息。

    2024-07-03
    60
  • oracle 删除用户后释放表空间

    oracle 删除用户后释放表空间有个用户占用大量存储。删除之后表空间没有释放。。。。 网上百度一番后: 先使用语句查询表空间大小 HWM。HWM说明参考:https://www.cnblogs.com/husam/p/660443…

    2023-04-04
    144
  • Python Coursepoint Plus: 为你的编程技能升级提供一站式解决方案

    Python Coursepoint Plus: 为你的编程技能升级提供一站式解决方案随着信息技术的发展,编程已经成为一个非常重要的技能。编程不仅在IT行业中得到广泛应用,而且在其他行业中也已经变得非常重要。学习编程不仅可以提升个人技能,而且可以帮助人们更好地理解和掌握计算机科学,这对未来的职业和事业发展都是非常有帮助的。

    2023-12-05
    107

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注