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

概述

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_t	buffer_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读、网络读、网络写*/

  int	seek_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模式。


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: ‘caching_sha2_password’ 是 MySQL 8.0 版本引入的一种插件,用于增强对用户密码的加密保护。在连接数据库时,如果发生 [tc_mysql::connect]: mysql_real_connect: authentication plugin 'caching_sha2_password' 错误,可能是以下两个原因之一: 1. MySQL 8.0 版本之前的客户端与 MySQL 8.0 版本以上的服务器进行连接时,由于默认加密插件不匹配,导致认证失败。解决方法是升级客户端版本或为相应的用户更改默认的加密插件。 2. MySQL 8.0 版本以上的服务器设置了默认使用 'caching_sha2_password' 插件进行认证,但客户端版本较低,并不支持该插件。解决方法是在服务器端修改用户的加密插件为旧版本所支持的插件,如 'mysql_native_password'。 在解决该错误时,可以采取以下步骤: 1. 升级客户端版本,确保与服务器兼容。 2. 在服务器端,找到相应用户并修改其加密插件为旧版本所支持的插件。 3. 如果连接仍然失败,可以尝试更新客户端的 MySQL 驱动程序,或使用其他不同版本的驱动程序。 总的来说,[tc_mysql::connect]: mysql_real_connect: authentication plugin 'caching_sha2_password' 错误是由于客户端与服务器的加密插件不匹配所致,需要升级客户端版本或修改服务器端的用户加密插件来解决。 ### 回答2: 这个错误是由于MySQL数据库连接时使用了"authentication plugin 'caching_sha2_password'",但是相应的驱动程序不支持此插件所导致的。 为了解决这个问题,有以下几种方法: 1. 更新驱动程序:查找并下载最新版本的MySQL驱动程序。新版本的驱动程序通常会支持新的插件。安装更新的驱动程序后,再次尝试连接数据库。 2. 修改MySQL用户的身份验证插件:如果使用的是MySQL 5.7以上版本,可以尝试将用户的身份验证插件修改为旧的插件,例如'mysql_native_password'。这可以通过修改MySQL用户的身份验证插件选项以及刷新权限来完成。 3. 修改MySQL服务配置:在MySQL配置文件中,可以尝试禁用所有新的身份验证插件,只使用旧的插件。找到MySQL配置文件(通常位于/etc/mysql目录下),找到并编辑my.cnf或my.ini文件,在[mysqld]部分添加或修改一行如下配置:default_authentication_plugin = mysql_native_password。保存文件后,重启MySQL服务。 以上是解决"tc_mysql::connect: mysql_real_connect: authentication plugin 'caching_sha2_password'"错误的几种方法。根据具体情况选择合适的方法来解决问题。 ### 回答3: 认证插件 'caching_sha2_password' 导致 MySQL 连接错误的可能原因有以下几点。 首先,该错误可能是因为 MySQL 服务器使用了不同的身份验证插件,而 PHP 中的 MySQL 扩展不支持该插件。在较新版本的 MySQL 中,默认身份验证插件是 'caching_sha2_password',而较旧版本的 PHP MySQL 扩展只支持旧的身份验证插件。 解决这个问题的一种方法是升级 PHP 版本,或者使用支持新身份验证插件的第三方 MySQL 扩展库,例如 MySQLi 或 PDO。这些扩展库兼容较新版本的 MySQL,并能正确地处理 'caching_sha2_password' 插件。 另外,该错误也可能是由于 MySQL 服务器未正确配置所致。您可以检查 MySQL 的配置文件(通常是 my.cnf 或 my.ini)中的设置。确保在 [mysqld] 部分中,有一个正确的默认身份验证插件设置,如下所示: default_authentication_plugin = mysql_native_password 如果该行不存在,或者设置为其他非 'mysql_native_password' 插件,可尝试添加或修改该设置并重启 MySQL 服务器。 最后,如果您无权更改 MySQL 服务器的配置或升级 PHP 版本,同时又无法使用其他支持新身份验证插件的扩展库,您可以联系服务器管理员或开发者,以获取进一步的帮助和解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值