Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1745377
  • 博文数量: 107
  • 博客积分: 1715
  • 博客等级: 上尉
  • 技术积分: 3168
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-18 18:42
个人简介

阿里巴巴DBA,原去哪儿网DBA。专注于MySQL源码研究、DBA运维、CGroup虚拟化及Linux Kernel源码研究等。 github:https://github.com/HengWang/ Email:king_wangheng@163.com 微博 :@王恒-Henry QQ :506437736

文章分类

全部博文(107)

文章存档

2014年(2)

2013年(38)

2012年(67)

分类: Mysql/postgreSQL

2013-01-15 11:11:36


目的

       MySQL数据结构VioMySQL对网络通信底层的进行封装结构,是MySQL网络通信数据结构NET重要的成员变量。Vio数据结构的封装,屏蔽了跨平台的差异性、统一了不同读写策略的接口等。使得网络通信过程可以不考虑具体实现的细节,而仅考虑算法和处理逻辑,从而处理MySQL的通信。

数据结构

       MySQL数据结构Vio的定义在源码中/include/violate.h/vio/vio_priv.h,具体定义如下所示:

enum enum_vio_type

{

  VIO_CLOSED, VIO_TYPE_TCPIP, VIO_TYPE_SOCKET, VIO_TYPE_NAMEDPIPE,

  VIO_TYPE_SSL, VIO_TYPE_SHARED_MEMORY

};

/* This structure is for every connection on both sides */

struct st_vio

{

  my_socket  sd/* my_socket - real or imaginary */

  HANDLE hPipe;

  my_bool  localhost/* Are we from localhost? */

  int  fcntl_mode/* Buffered fcntl(sd,F_GETFL) */

  struct sockaddr_storage  local/* Local internet address */

  struct sockaddr_storage  remote/* Remote internet address */

  int addrLen/* Length of remote address */

  enum enum_vio_type  type/* Type of connection */

  char  desc[30];        /* String description */

  char  *read_buffer;  /* buffer for vio_read_buff */

  char  *read_pos/* start of unfetched data in the read buffer */

  char  *read_end/* end of unfetched data */

  /* function pointers. They are similar for socket/SSL/whatever */

  void  (*viodelete)(Vio*);

  int  (*vioerrno)(Vio*);

  size_t  (*read)(Vio*, uchar *, size_t);

  size_t  (*write)(Vio*, const uchar *, size_t);

  int  (*vioblocking)(Vio*, my_bool, my_bool *);

  my_bool  (*is_blocking)(Vio*);

  int  (*viokeepalive)(Vio*, my_bool);

  int  (*fastsend)(Vio*);

  my_bool (*peer_addr)(Vio*, char *, uint16*, size_t);

  void  (*in_addr)(Vio*, struct sockaddr_storage*);

  my_bool  (*should_retry)(Vio*);

  my_bool  (*was_interrupted)(Vio*);

  int  (*vioclose)(Vio*);

  void  (*timeout)(Vio*, unsigned int which, unsigned int timeout);

  my_bool  (*poll_read)(Vio *vio, uint timeout);

  my_bool  (*is_connected)(Vio*);

  my_bool  (*has_data) (Vio*);

#ifdef HAVE_OPENSSL

  void  *ssl_arg;

#endif

#ifdef  HAVE_SMEM

  HANDLE  handle_file_map;

  char  *handle_map;

  HANDLE  event_server_wrote;

  HANDLE  event_server_read;

  HANDLE  event_client_wrote;

  HANDLE  event_client_read;

  HANDLE  event_conn_closed;

  size_t  shared_memory_remain;

  char  *shared_memory_pos;

#endif /* HAVE_SMEM */

#ifdef _WIN32

  OVERLAPPED pipe_overlapped;

  DWORD read_timeout_ms;

  DWORD write_timeout_ms;

#endif

};

typedef struct st_vio Vio;

       枚举类型enum_vio_type定义了Vio的几种不同的网络连接类型,根据不同的类型,在网络通信时有相应不同的处理逻辑。

       Vio数据结构的定义分为几个部分:成员变量部分、接口部分、SSL部分、共享内存部分和windows特有成员变量。其中成员变量包括:socket描述符sd;管道描述符hPipelocalhost表示是否为本机;fcntl_mode表示socket文件描述符的模式,通过fcntl()函数(在windows下是fcntlsocket()函数)设置sd描述符的一些特性;localremote分别表示本地和远端的网络地址,其中sockaddr_storage是通用网络地址数据结构;addrLen远端的网络地址长度;type表示网络连接类型;数组descVio的描述信息;read_bufferread_posread_end分别表示读buffer缓冲的指针地址、读取的当前位置以及结束地址,socket数据读取采用缓冲机制,提高读的性能。接口部分是socket的基本操作的函数指针,根据不同的平台和不同的读写策略,分别指向不同的处理函数。SSL部分是指当定义了HAVE_OPENSSL宏时,定义SSL相关的成员变量ssl_arg。共享内存部分是指当定义了HAVE_SMEM宏时,定义共享内存相关的成员变量。如果定义了_WIN32宏时,那么需要定义windows操作的成员变量。SSL部分、共享内存部分和windows特有成员变量都是根据编译时定义的宏及平台决定的,是不同读写策略时,使用的成员变量。

源码实现

       MySQL数据结构Vio对网络通信的封转,在源码的/vio/vio.c/vio/viosocket.c/vio/viossl.c/vio/viosslfactories.c中实现。由于大多数的实现是根据不同的连接类型,对底层函数的调用,基本不涉及复杂的算法和处理逻辑。因此,以下内容中,仅对几个比较核心的处理过程进行简要的分析。

vio_init函数

       vio_init()函数是Vio的内部初始化函数,对外接口vio_new*()初始化函数,都调用vio_init()函数进行初始化。该函数根据不同的连接类型,初始化相应类型的处理函数指针。参考源码/vio/vio.c

vio_read_buffer函数

       vio_read_buffer()函数是网络缓冲写方法,该方法同IO_CACHEio读有相似之处。读取策略分为三种:如果read_buffer中有数据,直接从read_buffer中读取数据;如果读取的数据长度小于16K,那么调用vio_read()函数读取16K数据到read_buffer中,然后再从read_buffer中读取数据;如果数据长度大于16K,直接通过vio_read()读取制定长度的数据,不需要写入缓冲read_buffer。这种读写策略,可以有效的提高小数据量读取的性能。例如readline这种方式,需要通过逐个字符判断是否为换行符,如果直接调用vio_read()进行读取,会每次产生一次寻址、读取等物理操作。而采用缓冲读策略,所有的操作从read_buffer中获取数据,避免频繁的物理操作。

vio_close函数

       vio_close()函数是关闭网络连接的操作,在该过程中,有一个问题需要特别说明。在调用close()windows下使用closesocket()函数)进行关闭socket连接之前,调用shutdown()函数将读写关闭。这是因为,在多个进程共享一个socket套接字,调用close()只是引用数减1,其他进程仍然可以通信,直到计数为0,才将socket套接字释放。而调用shutdown(2)则使得其他进程也无法进行通信。具体close()[1]shutdown()[2]的区别,可以参考Linux manual中相应的解释。

socket_poll_read函数

       socket_poll_read()函数是实现IO复用的封装函数,从该封装函数中可以看出,MySQLIO复用中,如果操作系统是windows,那么采用select()函数(在linuxselect()的最大文件描述数目为1024windows下无限制),在Linux系统中,使用poll()函数。select()poll()类似,性能比select()略高。然而,这两种方式都存在一个问题,因为他们都需要遍历所有的文件描述符,当监听描述符个数增加时,监听效率降低,并且selectpoll每次都要在用户态和内核态拷贝监听的描述符参数。更为高效的解决方案是epoll()方法,可以解决select()poll()存在的不足。具体详细的分析和差异,参考相应的文档。

       SSL相关的处理函数在源码的/vio/viossl.c/vio/viosslfactories.c,具体的实现主要是对SSL相关函数的封装,不再赘述。

结论

       通过以上分析可知,MySQL数据结构Vio主要封装了不同连接方式的网络通信接口,从而使得网络通信可以忽略平台差异和连接方式的不同,并且可以支持共享内存、SSL安全连接方式等通信方式。

       然而在IO复用方面,MySQL数据结构Vio存在一定的不足,使用了poll()方式。对于数据库这种高并发系统来说,会随着连接数的增加,使得poll()的系统调用次数严重下降,处理能力也随之降低。因此,对MySQL来说,一般的优化方案是限制连接数,而在应用层使用连接池的策略,来解决这个问题。然而当多个不同系统同时使用一个数据库实例时,仍然会导致连接数增加,如果该值设置较小的话,会导致连接失败。最佳的方式是使用epoll()方式代替poll()方式,从本质上提高处理能力。

参考

1

2

3

4http://www.cnblogs.com/xuxm2007/archive/2011/08/15/2139809.html

阅读(5052) | 评论(2) | 转发(1) |
给主人留下些什么吧!~~

king_wangheng2013-04-23 23:43:37

gpfeng_cs:其实使用poll还好了,因为连接数上去了,数据库本身性能会首先达到瓶颈

poll在IO密集型的多线程并发处理的环境下,poll会占用更多的CPU资源遍历IO handler,CPU的开销会很大。这个曾经做过实验,实验结果还是很惊人的。

回复 | 举报

gpfeng_cs2013-04-22 18:32:37

其实使用poll还好了,因为连接数上去了,数据库本身性能会首先达到瓶颈