mysql源码学习笔记:文件读写模块IO_CACHE

来源:互联网 发布:防arp攻击软件 编辑:程序博客网 时间:2024/05/21 17:39

概述

IO_CACHE为mysql读写文件的模块,在mysql操作文件时,通过该模块与文件进行交互。该模块类似于mysql的文件缓存,通过对文件的整读、整写来提高mysql的io效率。读写binlog、读写relay log等文件操作均是通过该模块进行。


IO_CACHE


基本结构

IO_CACHE的基础结构体如下

typedef struct st_io_cache{  uchar  *buffer;              /*文件中读取的文件内容*/  my_off_t pos_in_file;                       /*buffer在整个文件中的偏移量*/  uchar  *read_pos;                           /*读取到的当前位置*/  uchar  *read_end;                           /*最大允许读取的位置*/  size_tbuffer_length;                /*申请的buffer大小*/  uchar  *write_buffer;                       /*用在WRITE 或者 SEQ_READ_APPEND中的写缓存*/  uchar *write_pos;                           /*指向写缓存中的位置*/  uchar *write_end;                           /*最大允许写入位置*/  my_off_t end_of_file;  uchar *append_read_pos;                     /*SEQ_READ_APPEND使用,用作在写缓存中读取到的位置*/            File file                                   /*对应文件的文件描述符*/  enum cache_type type;                       /*缓存类型,包括读、写、顺序读、FIFO读、网络读、网络写*/  intseek_not_done                        /*标志在执行读写操作之前,是否需要执行seek*/  ulong disk_writes;                          /*缓存刷入磁盘的次数*/}IO_CACHE


其中cache_type决定io_cache的使用方法,cache_type包括 TYPE_NOT_SET、READ_CACHE、WRITE_CACHE、SEQ_READ_APPEND、READ_FIFO、READ_NET、WRITE_NET,这里主要对READ_CACHE模式、WRITE_CACHE模式和SEQ_READ_APPEND模式做主要说明。


READ_CACHE模式

read_cache模式为只读模式,每次读取文件中的一块内容,存放到缓存中。当发生读文件请求时,首先从缓存中读取文件内容。这种读文件的方式可以间接的将读文件合并,达到减少IO操作的目的,提升IO性能。

在读取当前不会被修改的文件时,使用这种模式来读取文件,dump线程、SQL线程在读取非正在使用的文件时,均使用这种模式的IO_CACHE。


初始化

使用函数init_io_cache_ext来对IO_CACHE结构体进行初始化。输入参数cache_type为READ_CACHE。

int init_io_cache_ext(IO_CACHE *info, File file, size_t cachesize, ...cache_myflags... | min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2;                           /*最小单位为IO_SIZE的倍数*/| if (!(cache_myflags & MY_DONT_CHECK_FILESIZE))                         /*文件大小不会再增长了*/    end_of_file= mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0));       /*计算文件大小*/| cachesize= ((cachesize + min_cache-1) & ~(min_cache-1));               /*对cachesize以最小单位min_cache做对齐*/| for (;;)    if (cachesize < min_cache)       cachesize = min_cache;                                                                /*最小的cachesize为min_cache*/    info->buffer= (uchar*) my_malloc(......,buffer_block, flags)    /*如果malloc失败,缩小cachesize到原大小的3/4,知道申请成功*/    cachesize= (cachesize*3/4 & ~(min_cache-1));| info->read_length=info->buffer_length=cachesize;| info->request_pos= info->read_pos = info->buffer| info->read_end=info->buffer                                                             /*end 等于 buffer 表示buffer里面没有任何内容*/| init_functions(info);                                                                            /*初始化读取函数*/    info->read_function = _my_b_read

在READ_CACHE模式下,初始化后缓存中是没有内容的。


读取文件


使用宏my_b_read来读取

#define my_b_read(info,Buffer,Count) ((info)->read_pos + (Count) <= (info)->read_end ?      /*如果缓存buffer中有足够的长度,直接从buffer中读取*/(memcpy(Buffer,(info)->read_pos,(size_t) (Count)), ((info)->read_pos+=(Count)),0) :/*如果缓存buffer中没有足够的长度(或者刚初始化),使用读取函数读取*/(*(info)->read_function)((info),Buffer,Count))



读取函数首先将缓存中内容全部读出,然后从文件中以IO_SIZE为单位读取到足够的内容,剩余的内容读取到缓存的buffer中之后,再读取出。

int _my_b_read(IO_CACHE *info, uchar *Buffer, size_t Count)  /*将IO_CACHE中剩余的byte copy到buffer中*/| left_length= (size_t) (info->read_end-info->read_pos)| memcpy(Buffer,info->read_pos, left_length);  /*计算文件的偏移量,保证读取的文件内容是以IO_SIZE为单位*/| diff_length= (size_t) (pos_in_file & (IO_SIZE-1));  /*如果需要读取的文件内容超出2个IO_SIZE*/  /*直接从文件中读取内容到buffer中,读取IO_SIZE * 2的倍数,剩余的仍然从缓存中读取*/| if (Count >= (size_t) (IO_SIZE+(IO_SIZE-diff_length)))    |  mysql_file_read(info->file,Buffer, length, info->myflags)   /*再次读取一个IO_CACHE_SIZE的文件内容到IO_CACHE中*/| max_length= info->read_length-diff_length;    | mysql_file_read(info->file,info->buffer, max_length,......  /*更新变量read_pos、read_end、pos_in_file的值*/| info->read_pos=info->buffer+Count;| info->read_end=info->buffer+length;| info->pos_in_file=pos_in_file;  /*将剩余的内容copy到buffer中*/| memcpy(Buffer, info->buffer, Count);


需要注意的是,每次读取均是以IO_CACHE为单位,如果读取的起始位置不为文件头,则通过diff_length进行对齐。

info->buffer读取到的buffer的起始位置,info->read_pos为读取到的当前,info->read_end为buffer的结束位置。info->pos_in_file为当前读取的buffer在文件中的位置,下次读取文件的起始位置。

通过info->read_end - info->read_pos计算buffer中剩余的数据长度。


WRITE_CACHE模式

write_cache为写入模式,IO_CACHE申请一块buffer作为写入缓存,将需要写入文件的内容先写入到缓存buffer中,然后统一写入到文件中,间接达到合并IO操作的目的,提高IO的性能。

在需要写入文件时,使用这种模式来操作文件,binlog的默认打开方式为WRITE_CACHE。

初始化

使用函数init_io_cache_ext来对IO_CACHE结构体进行初始化。输入参数cache_type为WRITE_CACHE。

初始化大体流程同READ_CACHE模式相同,需要注意的代码如下

int init_io_cache_ext(IO_CACHE *info, File file, size_t cachesize, ...cache_myflags... | info->pos_in_file= seek_offset;                                                      /*写入的起始点*/| info->write_pos = info->buffer;                                                      /*写入的起始位置,表示没有任何信息写入*/| info->write_end = info->buffer+info->buffer_length- (seek_offset & (IO_SIZE-1));| init_functions                                                                       /*初始化读取函数*/     info->write_function = _my_b_write;


写入文件

使用宏my_b_write来读取

#define my_b_write(info,Buffer,Count) ((info)->write_pos + (Count) <=(info)->write_end ?/*如果剩余空间足够写入的空间,使用当前buffer做缓存*/(memcpy((info)->write_pos, (Buffer), (size_t)(Count)), ((info)->write_pos+=(Count)),0) : /*如果剩余空不足,则调用函数进行写入*/(*(info)->write_function)((info),(uchar *)(Buffer),(Count)))

写入函数,首先将需要写入的内容全部写入剩余的缓存中,将缓存中的内容全部写入文件,

 _my_b_write(IO_CACHE *info, const uchar *Buffer, size_t Count)   /*将部分buffer中的内容写入缓存,将缓存中的内容刷新到文件中*/| memcpy(info->write_pos,Buffer,(size_t) (info->write_end - info->write_pos));| my_b_flush_io_cache(info,1)                            /*将缓存中的内容全部刷入文件*/  /*如果剩余的长度大于IO_SIZE,以IO_SIZE为单位,将所有大于IO_SIZE的内容直接写入文件*/| if (Count >= IO_SIZE)    | length=Count & (size_t) ~(IO_SIZE-1);         /*以IO_SIZE对齐*/    | mysql_file_write(info->file, Buffer, length, info->myflags | MY_NABP)    | info->pos_in_file+=length;  /*将剩余的buffer内容copy到IO_CACHE中*/| memcpy(info->write_pos,Buffer,(size_t) Count);| info->write_pos+=Count;

刷新缓存进入文件

my_b_flush_io_cache| length=(size_t) (info->write_pos - info->write_buffer)            /*计算缓存中的buffer大小*/|  if (!append_cache && info->seek_not_done)    if (mysql_file_seek(info->file, pos_in_file, MY_SEEK_SET, MYF(0)) ==MY_FILEPOS_ERROR)     /*定位文件中的位置*/| info->pos_in_file+=length/*保证整个缓存以IO_SIZE对齐,即如果写满,一定为IO_SIZE的整数倍*/| info->write_end= (info->write_buffer+info->buffer_length- ((pos_in_file+length) & (IO_SIZE-1)));          | mysql_file_write(info->file,info->write_buffer,length, info->myflags | MY_NABP)  /*写入文件*/| set_if_bigger(info->end_of_file,(pos_in_file+length));               | info->write_pos=info->write_buffer;   /*重置info->write_pos*/


info->write_buffer为写入缓存的起始位置,info->write_pos为写入到缓存中的当前位置,info->write_end为可以写入缓存的最大位置


SEQ_READ_APPEND模式

SEQ_READ_APPEND模式为一种读写共用模式,对同一个文件,同时申请两块buffer用作文件缓存,一块用作读文件的buffer,另一块用作写文件的buffer。当读取到文件位置与写入文件的位置发生重合时,可以直接在写缓存中直接读取,减少读操作读取文件的io操作,以提高IO效率。


IO线程在写入relay log时使用这种模式来打开文件,如果SQL线程与IO线程读取相同的文件时,IO线程和SQL线程使用相同的IO_CACHE。


初始化

使用函数init_io_cache_ext来对IO_CACHE结构体进行初始化。输入参数cache_type为SEQ_READ_APPEND。

初始化大体流程同READ_CACHE模式相同,但是需要申请两块buffer分别用于读写。

int init_io_cache_ext(IO_CACHE *info, File file, size_t cachesize, ...cache_myflags... | if (type == SEQ_READ_APPEND)    buffer_block *= 2;                                                                   /*将bufer_size 扩大到双倍*/| info->buffer= (uchar*) my_malloc(......,buffer_block, flags)| if (type == SEQ_READ_APPEND)     /*info->buffer和info->write_buffer分别指向两块buffer的起始位置*/     info->write_buffer = info->buffer + cachesize;                      | if (type == SEQ_READ_APPEND)    info->append_read_pos = info->write_pos = info->write_buffer;            /*append_read_pos为读取时使用*/    info->write_end = info->write_buffer + info->buffer_length;    mysql_mutex_init(key_IO_CACHE_append_buffer_lock,......               /*读取和写入同一块buffer使用的锁*/| init_functions    info->read_function = _my_b_seq_read;    info->write_function = 0;                                                 /*不使用*/




写入文件

不使用宏来写入文件,使用单独的append函数来写入,所有的写入均append到文件的结尾。buffer的使用同WRITE_CACHE模式下的写入是相同的,在buffer充足的情况下,首先写入buffer,如果buffer空间不足的情况下,先将buffer中的内容append到文件结尾,再继续使用buffer。


int my_b_append(IO_CACHE *info, const uchar *Buffer, size_t Count)| lock_append_buffer(info);| rest_length= (size_t) (info->write_end - info->write_pos);                   /*计算buffer剩余空间*//*如果剩余空间充足*/| memcpy(info->write_pos,Buffer,(size_t) Count);                               /*直接写入缓存buffer中*/| info->write_pos+=Count;/*如果剩余空间不足*/  | memcpy(info->write_pos, Buffer, rest_length);                                 /*先将剩余的内容先写入到buffer中*/| length=Count & (size_t) ~(IO_SIZE-1);                                            /*以IO_SIZE对齐*/| my_b_flush_io_cache(info,0)                                                           /*将缓存中的内容append到文件中,重置写buffer*/| memcpy(info->write_pos,Buffer,(size_t) Count);                              /*将对齐后剩余的内容写入新的buffer*/| info->write_pos+=Count;| unlock_append_buffer(info);


读取文件

使用宏my_b_read来读取,如果buffer不足使用读取函数读取,读取函数与READ_CACHE模式使用的读取函数不同。

_my_b_seq_read| if ((left_length=(size_t) (info->read_end-info->read_pos)))     DBUG_ASSERT(Count > left_length);               读取的长度一定大于read_buffer中剩余的| lock_append_buffer(info);| if ((pos_in_file=info->pos_in_file + (size_t) (info->read_end - info->buffer)) >= info->end_of_file)                                                                /*判断是否读取到了尾端,开始读取write_buffer*/|      goto read_append_buffer;没有读取到write_buffer| if (mysql_file_seek(info->file, pos_in_file, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR)| 正常读取,与READ_CACHE模式的读取方式相同读取到write_buffer  /*初始时info->append_read_pos 等于info->write_pos, 写入后info->write_pos会增加 */| len_in_buff = (size_t) (info->write_pos - info->append_read_pos);  | copy_len= MY_MIN(Count, len_in_buff);                      /*取较小的copy到buffer中*//*将需要读取长度copy到buffer中*/| memcpy(Buffer, info->append_read_pos, copy_len);| info->append_read_pos += copy_len;/*将write_buffer中剩余的字节读取到read_buffer中*/| memcpy(info->buffer, info->append_read_pos,   (size_t) (transfer_len=len_in_buff - copy_len));         /*将剩余的字节copy到read buffer中*/| info->read_pos= info->buffer;  info->read_end= info->buffer+transfer_len;                  /*剩余的所有字节作为 read_end*/          info->append_read_pos=info->write_pos;                        /*置append_read_pos到write_pos*/  info->pos_in_file=pos_in_file+copy_len;                             /* info->buffer在文件中的起始位置*/   info->end_of_file+=len_in_buff;                                          /*可读的最大长度*/


info->read_pos、info->read_end、info->buffer的使用与READ_CACHE模式下使用的相同。

info->write_pos、info->write_end、info->write_buffer的使用与WRITE_CACHE模式下的使用的相同。

info->append_read_pos为读取IO_CACHE时,读取到了write_buffer(文件末尾)时使用的变量,通过info->write_pos - info-> append_read_pos来计算可以从write_buffer中读取到的字节长度。


总结

IO_CACHE模块是mysql用来提升IO性能的模块,对于长度小于IO_SIZE(默认为4096字节)的文件操作(读取或者写入),该模块可以达到合并小的IO操作,提升IO性能的目的。如果每次读取或者写入文件的长度如果超过IO_SIZE * 2,那么每次IO_CACHE模块会直接操作文件,并不会达到提升IO性能的目的。


另外需要注意的是SEQ_READ_APPEND模式,该模式为IO线程和SQL线程操作同一文件时来提升性能的模式,在5.7以及5.7版本前均是使用该模式来打开IO线程和SQL线程共同使用的relay log。但是由于锁的原因,在mysql-8版本中,IO线程和SQL线程分别使用独自的IO_CACHE,不再使用同一个IO_CACHE的SEQ_READ_APPEND模式。


0 0
原创粉丝点击