mysql网络部分代码

今天终有点空闲(心情?)整理下mysql的网络部分代码了。整体网络部分的代码是非常简单的,mysql几乎没有做过多的封装,很直接的调用系统函数,一目了然。

总体看来,mysql主要使用了一个连接一个线程的模型,对客户端连接的socket使用非阻塞模式。linux版本,甚至包括OSX都用的是poll函数监听客户端请求。中间细节的处理没有太多复杂的操作,可见mysql这一块没有很特别的优化。

先说说Mysql启动网络几个关键函数。

Mysql启动时,首先进入的是mysql_main这个函数,这个函数相当于main函数。在这个main中,主要做了一些列初始化工作,包括初始化环境变量,shutdown线程,最后进入无限循环处理客户端连接请求,当然,无线循环可以被关闭操作或者异常打断,从而退出mysqld。Main首先调用network_init()进行初始化网络socket,在最后,调用handle_connection_sockets()进入循环处理客户端请求连接。简单写出来的伪代码就是如下:

int sql_main()
{
       network_init(); // 初始化socket
       handle_connection_sockets();// 循环处理连接请求
       return 0;
}

以下是network_init()的伪代码,忽略了一些平台,错误处理的代码,只写出主要逻辑:

void network_init() {
       // 地址信息,ai是一个链表,而a则是bind的地址信息
       struct addrinfo *ai, *a;

       // set_port是确定mysql的监听端口,默认下会取编译配置的端口,
    // 若编译配置都没有设置,则用3306
    // 环境变量MYSQL_TCP_PORT会覆盖以上配置
       set_port();

       // 调用getaddrinfo通过主机名获得地址信息,注意这里的hint.ai_flags设置为AI_PASSIVE表示只获取能bind的地址信息。默认下host name是”0.0.0.0”
       hint.ai_flags = AI_PASSIVE;
       getaddrinfo(host name, hint, &ai);

       // 调用create_socket根据地址信息创建出ip_sock,这个ip_sock就是用了accept连接请求的。ip_sock是全局变量。
       ip_sock = create_socket(ai, a);

       // 设置地址可复用,这样mysqld可以从停止中快速重新bind,而不用等待time wait状态
       setsockopt(ip_sock, reuseaddr, 1);
       // 关闭只使用ipv6
       setsockopt(ip_sock, ipv6only, 0);  

       // 以下mysql使用了一个for循环多次尝试bind,这也是为了保证bind的成功率吧,每次尝试若失败,则sleep一段时间。
       // sleep x seconds for every try
       // x: 1, 3, 7, 13, 22, 35, 52, 74, ...
       for(retry = 1;;retry++) {
              ret = bind(ip_sock, a);
              // bind成功出去继续
              if (0 == ret) {
                     break;
              }

              // 失败,若不是因为地址仍然在使用中的错误,就出去继续,因此mysqld就会启动失败了
              if (SOCKET_EADDRINUSE != errno){
                     break;
              }

              // 尝试失败过多,mysqld启动失败
              // 这个可以在mysql启动时用参数--port-open-timeout=#设置启动最多等待时间
              if(wait_time > max_wait_time){
                     break;
              }

              wait_time += retry * retry / 3 + 1;
              sleep(wait_time);
       }

       listen(ip_sock, back_log);
    // 以下,读出mysql.sock这个文件,把本机sock参数加载进来,这样mysql其实就有两个socket来出来客户端连接请求,一个是上面的,一个是下面这个主要给本机使用的,其代码更简单
#ifdef UNIX_SOCK
       unix_sock = socket();
       strmov(UNIXaddr.sun_path, mysqld_unix_port); // load the local address
       setsockopt(unix_sock, reuseaddr, 1);
       bind(unix_sock);
       listen(unix_sock, back_log);
#endif // #ifdef UNIX_SOCK
}

以上代码中,set_port()和create_socket()是很简单的,仅仅是简单的调用getenv来获得环境变量从而设置port,而create_socket()则是调用socket来生成socket套接字,所以就不列出了。

接下来就会进入handle函数了,handle函数主要是处理连接请求,主要实现办法是poll监听请求的到来,然后把accept出new_sock,把new_sock交给一个新建的线程,之后的任务就是这个子线程的事了:

void handle_connection_sockets(){
       int socket_count = 0;
    // 是的就两个,一个ip_sock,一个unix_sock,前者用来accept远程客户端,后者用来accept本机
       pollfd fds[2];

       fds[socket_count].fd = ip_sock;
       fds[socket_count].events= POLLIN;
       int ip_flags = fcntl(ip_sock, F_GETFL, 0);
       socket_count++;

       fds[socket_count].fd = unix_sock;
       fds[socket_count].events= POLLIN;
       int socket_flags=fcntl(unix_sock, F_GETFL, 0);
       socket_count++;

       int sock = 0, flags = 0, new_sock = 0;
       // 这就是mysqld主线程的主循环
       while(!abort_loop){
              // poll监听
              int ret_val = poll(fds, socket_count, -1);
              for (int i = 0; i < socket_count; ++i){
                     if (fds[i].revents & POLLIN){
                            sock = fds[i].fd;
                            flags = fcntl(sock, F_GETFL, 0);
                            break;
                     }
              }

              // 非阻塞的socket
              fcntl(sock, F_SETFL, flags | O_NONBLOCK);

              // accept,这里accept10次,直到成功为止,10次都不成功,则这个连接请求失败
              // MAX_ACCEPT_RETRY = 10
              for (int retry = 0; retry == MAX_ACCEPT_RETRY; ++retry) {
                     new_sock = accept(sock);
                     if (no error) {
                            break;
                     }
                     sleep(1);
              }

              if (new_sock == INVALID_SOCKET){
                     continue;
              }

              // 获得对端的地址信息
              sockaddr_storage dummy;
              if (getsockname(new_sock, &dummy) < 0){
                     shotdown(new_sock, SHUT_RDWR);
                     close(new_sock);
                     continue;

              }

              // 以下创建THD这个对象,THD是mysql线程对象,放了很多信息,还没研究
              // ……
       }
}

今天就研究到这里。

时间: 2024-11-12 11:50:10

mysql网络部分代码的相关文章

Php mysql 常用代码、CURD操作以及简单查询

C/S:Client ServerB/S:Brower Server php主要实现B/S LAMP :Linux系统    A阿帕奇服务器    Mysql数据库   Php语言 mysql常用代码 创建表 1 create table CeShi1 2 ( 3 Uid varchar(50) primary key, 4 Pwd varchar(50), 5 Name varchar(50), 6 Nation varchar(50), 7 foreign key(Nation) refer

mysql内部代码的优缺点

mysql内部代码有四种:存储过程,存储函数,事件,触发器. 存储过程&存储函数: 优点: 内部执行,离数据最近,另外在服务器上执行还可以节省宽带和网络延迟 代码重用,可以方便地统一业务规则,保证某些行为总是一致,也可以为应用提供一定的安全性. 简化代码的维护和版本更新. 帮助提升安全,提供更细颗粒度的权限控制. 缓存执行计划,如果反复调用可以降低消耗. 维护简单,没外部依赖 更好在开发和数据库维护人员间分工. 缺点: mysql没有提供好的开发和调试工具,编写调试困难. 效率差,存储过程使用的

Mysql基础代码(不断完善中)

Mysql基础代码,不断完善中~ 1 //语法错误(syntax error)在语法分析阶段,源代码并未被执行,故不会有任何输出. 2 3 4 /* [命名规则] */ 5 常量名 类常量建议全大写,单词间用下划线分隔 // MIN_WIDTH 6 变量名建议用下划线方式分隔 // $var_name 7 函数名建议用驼峰命名法 // varName 8 定界符建议全大写 // <<<DING, <<<'DING' 9 文件名建议全小写和下划线.数字 // func_n

Python操作Mysql实例代码教程在线版(查询手册)_python

实例1.取得MYSQL的版本 在windows环境下安装mysql模块用于python开发 MySQL-python Windows下EXE安装文件下载 复制代码 代码如下: # -*- coding: UTF-8 -*- #安装MYSQL DB for pythonimport MySQLdb as mdb con = None try:    #连接mysql的方法:connect('ip','user','password','dbname')    con = mdb.connect('

mysql数据字典代码

平时做开发时,查看mysql的表是用了navicat,虽然查看单个表的各个字段时还算方便,但是要一次查看整个数据库各个表的各个字段的详情还是不那么方便,于是就在网上找了一段代码,把数据库的所有表的字段都显示出来,这样查看结构会比较清晰. 显示效果 1 <?php 2 /** 3 * 生成mysql数据字典 4 */ 5 header ( "Content-type: text/html; charset=utf-8" ); 6 7 // 配置数据库 8 $dbserver = &

php 生成mysql数据字典代码

由于项目开发用了比较多的表 ,为了快速获取数据字典,通过php代码的方式来获取表结构和表注释.代码如下: <?php /** * 生成mysql数据字典 */ header ( "Content-type: text/html; charset=utf-8" ); // 配置数据库 $dbserver = "localhost"; $dbusername = "数据库用户名"; $dbpassword = "数据库密码"

Mysql:常用代码

C/S: Client Server B/S: Brower Server Php主要实现B/S .net IIS Jave TomCat LAMP:L Create table ceshi1 ( Uid varchar(50) primary key, Pwd varchar(50), Name varchar(50), Nation varchar(50), foreign key(nation) references nation(code) ) 写查询语句需要注意: 创建表的时候,最后一

Python操作Mysql实例代码

实例1.取得MYSQL的版本在windows环境下安装mysql模块用于python开发 MySQL-python Windows下EXE安装文件下载 代码如下: # -*- coding: UTF-8 -*- #安装MYSQL DB for pythonimport MySQLdb as mdb con = None try:    #连接mysql的方法:connect('ip','user','password','dbname')    con = mdb.connect('localh

检查连接mysql数据库代码

<?php $host="192.168.1.136";//mysql主机地址 $user="root"; //mysql 登录账户 $pwd="password123"; //mysql登录密码 //连接数据库 $conn = mysql_connect($host,$user,$pwd); //判断 if (!$conn) { die('连接数据库失败: ' . mysql_error()); } echo "mysql 连接