热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MySQL源代码管中窥豹(一)_MySQL

MySQL源代码管中窥豹(一)
注意:

1..以下所有讨论都基于mysql 5.5.37版本及官方文档,不保证适用于其他版本。

2.下文中提到的磁盘满,指的是数据文件(数据文件,日志文件,配置文件)所在磁盘分区。

3.由于篇幅问题,最后面的代码部分,只有关键的函数及逻辑判断部分。

前两天同事讨论到一个问题,当mysql从库磁盘满之后,show status及show slave status会被卡住,但其他select操作不受影响,但如果数据库是主库,磁盘满了之后,只有dml会被阻塞,select及show是不会受影响的。于是一群人讨论了一会,最后决定,SMC,以下就是我的结论。

直接查官文档的话,官方作如下表示:

https://dev.mysql.com/doc/refman/5.5/en/full-disk.html

1.实例每分钟检查是否有足够的空间写入。如果已经有空间了,继续执行操作。

2.每十分钟给日志文件写入一条记录,报告磁盘已经写满。

但就我的实验来说,上面的说法存在不少问题。

下面是我对官方文档的测试结果:

1.如果主库上打开binlog,那么当磁盘满之后,每10分钟,数据库会报告一条Disk is full writing './mysql-bin.000001' (Errcode: 28). Waiting for someone to free space... (Expect up to 60 secs delay for server to continue after freeing disk space),也就是说bin log写满了,等待磁盘空间,这与文档描述相同。

2,如果在主库上关闭binlog,当磁盘满了之后,任何插入行为都会失败,报错为[ERROR] ./mysqld: The table 'x' is full,官方文档没有提到这个情况,此处的表x是innodb表。

上面是对主库所在磁盘写满之后,数据库实例的反应,下面讲讲我们遇到的情况:从库磁盘写满之后,show status及show slave status会被卡住,但其他select操作不受影响。

首先,以下是结论:

整个流程涉及3把锁:

1.mi->data_lock

2.LOCK_active_mi

3.LOCK_status

说明如下(以下操作安编号顺序执行):

1.当一个新操作被接收到slave io线程后,如果这时候磁盘写满了,这个写入操作就会被阻塞,然后等待,直到磁盘有空间之后继续写入,这个操作中,会持有mi->data_lock锁,只有操作完成或者操作失败后,这个锁才会被释放,恰好,磁盘满不属于错误,于是操作阻塞,该线程会一直持有mi->data_lock锁。

2.当发起一个show slave status请求的时候,执行的时候,会首先锁住LOCK_active_mi锁,然后锁定mi->data_lock锁,当然,现在的情况下,mi->data_lock不会得到,于是LOCK_active_mi锁就会被该线程持续持有。

3.另外其一个会话发起show global status,执行的时候首先锁定LOCK_status锁,由于show status包括,Slave_heartbeat_period,Slave_open_temp_tables,Slave_received_heartbeats ,Slave_retried_transactions这些状态,于是还需要LOCK_active_mi锁,于是,这个会话也会被阻塞掉。

4.之后如果再另外发起请求,由于LOCK_status已经被锁定,于是所有涉及show status的请求,都会被阻塞到这里。

5.之后所有show slave status请求也都会被阻塞在LOCK_active_mi锁处。

看了以上的结论,是否会想到另外一个操作顺序:磁盘写满-》show status,这种操作的结果是:show status不会被阻塞的。

以下是mysql源代码(5.5.37)涉及到的具体部分:

1.io线程阻塞的相关函数及部分代码

slave.cc

pthread_handler_t handle_slave_io(void *arg)if (queue_event(mi, event_buf, event_len))//写入slave日志函数      {        mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,                   ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),                   "could not queue event from master");        goto err;      }

slave.cc

static int queue_event(Master_info* mi,const char* buf, ulong event_len)mysql_mutex_lock(&mi->data_lock);mysql_mutex_lock(log_lock);if (likely(!(rli->relay_log.appendv(buf,event_len,0))))//写入执行函数    {      mi->master_log_pos+= inc_pos;      DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));      rli->relay_log.harvest_bytes_written(&rli->log_space_total);    }  mysql_mutex_unlock(log_lock);err:  mysql_mutex_unlock(&mi->data_lock);

log.cc

bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...){  bool error= 0;  DBUG_ENTER("MYSQL_BIN_LOG::appendv");  va_list(args);  va_start(args,len);  DBUG_ASSERT(log_file.type == SEQ_READ_APPEND);  mysql_mutex_assert_owner(&LOCK_log);  do  {    if (my_b_append(&log_file,(uchar*) buf,len))    {      error= 1;      goto err;    }    bytes_written += len;  } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint)));  DBUG_PRINT("info",("max_size: %lu",max_size));  if (flush_and_sync(0))//把日志数据刷入磁盘    goto err;  if ((uint) my_b_append_tell(&log_file) > max_size)    error= new_file_without_locking();err:  if (!error)    signal_update();  DBUG_RETURN(error);}

log.cc

bool MYSQL_BIN_LOG::flush_and_sync(bool *synced){  int err=0, fd=log_file.file;  if (synced)    *synced= 0;  mysql_mutex_assert_owner(&LOCK_log);  if (flush_io_cache(&log_file))    return 1;  uint sync_period= get_sync_period();  if (sync_period && ++sync_counter >= sync_period)  {    sync_counter= 0;    err= mysql_file_sync(fd, MYF(MY_WME));//同步写入文件    if (synced)      *synced= 1;  }  return err;}

mf_locache.c

int my_b_flush_io_cache(IO_CACHE *info,                        int need_append_buffer_lock __attribute__((unused)))if (mysql_file_write(info->file,info->write_buffer,length,   info->myflags | MY_NABP))info->error= -1;      elseinfo->error= 0;mysql_file.hstatic inline size_tinline_mysql_file_write(#ifdef HAVE_PSI_INTERFACE  const char *src_file, uint src_line,#endif  File file, const uchar *buffer, size_t count, myf flags){  size_t result;#ifdef HAVE_PSI_INTERFACE  struct PSI_file_locker *locker= NULL;  PSI_file_locker_state state;  if (likely(PSI_server != NULL))  {    locker= PSI_server->get_thread_file_descriptor_locker(&state, file,                                                   if (likely(locker != NULL))      PSI_server->start_file_wait(locker, count, src_file, src_line);  }#endif  result= my_write(file, buffer, count, flags);//写入文件#ifdef HAVE_PSI_INTERFACE  if (likely(locker != NULL))  {    size_t bytes_written;    if (flags & (MY_NABP | MY_FNABP))      bytes_written= (result == 0) ? count : 0;    else      bytes_written= (result != MY_FILE_ERROR) ? result : 0;    PSI_server->end_file_wait(locker, bytes_written);  }#endif  return result;}

my_write.c

size_t my_write(File Filedes, const uchar *Buffer, size_t Count, myf MyFlags){for (;;)  {#ifdef _WIN32    writtenbytes= my_win_write(Filedes, Buffer, Count);#else    writtenbytes= write(Filedes, Buffer, Count);//调用系统函数#endif}} /* my_write */

errors.c

void wait_for_free_space(const char *filename, int errors){  if (!(errors % MY_WAIT_GIVE_USER_A_MESSAGE))  {    my_printf_warning(EE(EE_DISK_FULL),             filename,my_errno,MY_WAIT_FOR_USER_TO_FIX_PANIC);    my_printf_warning("Retry in %d secs. Message reprinted in %d secs",                    MY_WAIT_FOR_USER_TO_FIX_PANIC,                    MY_WAIT_GIVE_USER_A_MESSAGE * MY_WAIT_FOR_USER_TO_FIX_PANIC );  }  DBUG_EXECUTE_IF("simulate_no_free_space_error",                 {                   (void) sleep(1);//直接退出                   return;                 });  (void) sleep(MY_WAIT_FOR_USER_TO_FIX_PANIC);//等待时间}

2.show slave status相关的函数及部分代码

sql_parse.cc

bool dispatch_command(enum enum_server_command command, THD *thd,//转发请求      char* packet, uint packet_length)case COM_QUERY:...mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);

sql_parse.cc

void mysql_parse(THD *thd, char *rawbuf, uint length,                 Parser_state *parser_state)error= mysql_execute_command(thd);sql_parse.cint mysql_execute_command(THD *thd)case SQLCOM_SHOW_SLAVE_STAT://执行  {    /* Accept one of two privileges */    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))      goto error;    mysql_mutex_lock(&LOCK_active_mi);//加锁    if (active_mi != NULL)    {      res = show_master_info(thd, active_mi);//得到信息    }    else    {      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,                   WARN_NO_MASTER_INFO, ER(WARN_NO_MASTER_INFO));      my_ok(thd);    }    mysql_mutex_unlock(&LOCK_active_mi);//解锁    break;  }

slave.cc

bool show_master_info(THD* thd, Master_info* mi)  if (mi->host[0])  {    DBUG_PRINT("info",("host is set: '%s'", mi->host));    String *packet= &thd->packet;    protocol->prepare_for_resend();    /*      slave_running can be accessed without run_lock but not other      non-volotile members like mi->io_thd, which is guarded by the mutex.    */        mysql_mutex_lock(&mi->run_lock);    protocol->store(mi->io_thd ? mi->io_thd->proc_info : "", &my_charset_bin);    mysql_mutex_unlock(&mi->run_lock);    mysql_mutex_lock(&mi->data_lock);//加锁    mysql_mutex_lock(&mi->rli.data_lock);    mysql_mutex_lock(&mi->err_lock);    mysql_mutex_lock(&mi->rli.err_lock);    ...    mysql_mutex_unlock(&mi->rli.err_lock);    mysql_mutex_unlock(&mi->err_lock);    mysql_mutex_unlock(&mi->rli.data_lock);    mysql_mutex_unlock(&mi->data_lock);//解锁    if (my_net_write(&thd->net, (uchar*) thd->packet.ptr(), packet->length()))      DBUG_RETURN(TRUE);  }

3.show status相关的函数及部分代码

mysqld.cc

static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff){  mysql_mutex_lock(&LOCK_active_mi);//加锁  if (active_mi)  {    var->type= SHOW_CHAR;    var->value= buff;    sprintf(buff, "%.3f", active_mi->heartbeat_period);  }  else    var->type= SHOW_UNDEF;  mysql_mutex_unlock(&LOCK_active_mi);//解锁  return 0;}

sql_show.cc

static bool show_status_array(THD *thd, const char *wild,SHOW_VAR *variables,enum enum_var_type value_type,truct system_status_var *status_var,onst char *prefix, TABLE *table,bool ucase_names,COND *cond){    for (var=variables; var->type == SHOW_FUNC; var= &tmp)      ((mysql_show_var_func)(var->value))(thd, &tmp, buff); //此处调用前面的函数show_heartbeat_period}

sql_show.cc

int fill_status(THD *thd, TABLE_LIST *tables, COND *cond){  if (thd->fill_status_recursion_level++ == 0)     mysql_mutex_lock(&LOCK_status);//加锁  if (option_type == OPT_GLOBAL)    calc_sum_of_all_status(&tmp);  res= show_status_array(thd, wild,                         (SHOW_VAR *)all_status_vars.buffer,                         option_type, tmp1, "", tables->table,                         upper_case_names, cond);  if (thd->fill_status_recursion_level-- == 1)     mysql_mutex_unlock(&LOCK_status);//解锁  DBUG_RETURN(res);}mysql_mutex_lock(&mi->data_lock);

推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 推荐一个ASP的内容管理框架(ASP Nuke)的优势和适用场景
    本文推荐了一个ASP的内容管理框架ASP Nuke,并介绍了其主要功能和特点。ASP Nuke支持文章新闻管理、投票、论坛等主要内容,并可以自定义模块。最新版本为0.8,虽然目前仍处于Alpha状态,但作者表示会继续更新完善。文章还分析了使用ASP的原因,包括ASP相对较小、易于部署和较简单等优势,适用于建立门户、网站的组织和小公司等场景。 ... [详细]
  • Final关键字的含义及用法详解
    本文详细介绍了Java中final关键字的含义和用法。final关键字可以修饰非抽象类、非抽象类成员方法和变量。final类不能被继承,final类中的方法默认是final的。final方法不能被子类的方法覆盖,但可以被继承。final成员变量表示常量,只能被赋值一次,赋值后值不再改变。文章还讨论了final类和final方法的应用场景,以及使用final方法的两个原因:锁定方法防止修改和提高执行效率。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 本文介绍了如何在MySQL中将零值替换为先前的非零值的方法,包括使用内联查询和更新查询。同时还提供了选择正确值的方法。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 电销机器人作为一种人工智能技术载体,可以帮助企业提升电销效率并节省人工成本。然而,电销机器人市场缺乏统一的市场准入标准,产品品质良莠不齐。创业者在代理或购买电销机器人时应注意谨防用录音冒充真人语音通话以及宣传技术与实际效果不符的情况。选择电销机器人时需要考察公司资质和产品品质,尤其要关注语音识别率。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
author-avatar
子新宥梅93
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有