LinuxEye - Linux系统教程

LinuxEye - Linux系统教程

当前位置: 主页 > 数据库 >

MySQL数据结构分析

时间:2013-01-17 09:49来源:未知 编辑:admin 点击:
目的 MySQL数据结构Vio是MySQL对网络通信底层的进行封装结构,是MySQL网络通信数据结构NET重要的成员变量。Vio数据结构的封装,屏蔽了跨平台的差异性、统一了不同读写策略的接口等。使
 目的
       MySQL数据结构Vio是MySQL对网络通信底层的进行封装结构,是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;管道描述符hPipe;localhost表示是否为本机;fcntl_mode表示socket文件描述符的模式,通过fcntl()函数(在windows下是fcntlsocket()函数)设置sd描述符的一些特性;local和remote分别表示本地和远端的网络地址,其中sockaddr_storage是通用网络地址数据结构;addrLen远端的网络地址长度;type表示网络连接类型;数组desc是Vio的描述信息;read_buffer、read_pos、read_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_CACHE的io读有相似之处。读取策略分为三种:如果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复用的封装函数,从该封装函数中可以看出,MySQL在IO复用中,如果操作系统是windows,那么采用select()函数(在linux下select()的最大文件描述数目为1024,windows下无限制),在Linux系统中,使用poll()函数。select()和poll()类似,性能比select()略高。然而,这两种方式都存在一个问题,因为他们都需要遍历所有的文件描述符,当监听描述符个数增加时,监听效率降低,并且select和poll每次都要在用户态和内核态拷贝监听的描述符参数。更为高效的解决方案是epoll()方法,可以解决select()和poll()存在的不足。具体详细的分析和差异,参考相应的文档。

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

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

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

转载请保留固定链接: https://linuxeye.com/database/1180.html

------分隔线----------------------------
标签:mysql
栏目列表
推荐内容