Chinaunix首页 | 论坛 | 博客
  • 博客访问: 108093
  • 博文数量: 13
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 195
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-23 13:16
个人简介

数据库领域专心吃草

文章分类

全部博文(13)

文章存档

2015年(1)

2014年(4)

2013年(8)

我的朋友

分类: Mysql/postgreSQL

2014-01-20 11:24:27

        mysql的主从是我们用来扩展业务的好帮手,本文主要记录下工作中遇到的mysql主从的一些限制以及一些有意思的特性,记录下对mysql复制系统从使用到内部机理的学习,想到哪里就写到哪里吧。
        
        理解mysql复制最主要的一点就是他是异步的,正因为他是异步的复制,所以很多限制都由异步机制引发。想保持主从数据的实时一致性是不可能的,当然现在也出现了一些同步复制的机制,像galera插件、淘宝mysql分支等。目前大家普遍使用的还是mysql的异步复制功能。
    
        简单概括一下mysql主从搭建步骤:master要开启二进制日志--》设置不同的server_id--》设置复制账号--》授权replication slave权限--》锁表--》记录日志文件位置--》dump主机数据--》主机unlock tables--》拷贝到从机并还原数据--》从机change master--》从机slave start。

        如何正确的配置master?想到master配置,我们肯定会想到server-id、log_bin、binlog_format、sync_binlog、innodb_flush_log_at_trx_commit、binlog_do_db、binlog_ignore_db、replicate_do_db、replicate_ignore_db、replicate_wild_do_table、replicate_wild_ignore_table等参数,当然还有一些参数也比较重要:auto_increment_increment、auto_increment_offset、innodb_autoinc_lock_mode、relay_log_purge等,还有一些参数我也不是很了解,所以就没有列出来。先简单对这些参数分一下类。
     
         必须的参数:server-id,log_bin,这是复制必须的参数,必须进行设置。
         
         重要的参数:binlog_format,sync_binlog,innodb_flush_log_at_trx_commit。binlog_format用来设置二进制日志的格式,有三个值,分别为statement、row和mixed,其中statement表示只记录SQL语句,row表示记录数据被修改后的结果,mixed是前两种方式的混合体,三种方式都有各自的限制以及优点,具体会在以后的文章中介绍。sync_binlog是用来设置刷写二进制日志的方式,有0和1两个值,0表示在事务提交的时候不刷写二进制日志,1表示在事务提交的时候刷写二进制日志,如果为0值,那么在主服务器当机的时候有可能部分事务日志没有刷写到二进制日志文件,那么从机就会丢失部分数据,所以强烈建议在master上设置该值为1。innodb_flush_log_at_trx_commit用来控制innodb redo日志的刷写方式,有3个值,0表示只依赖后台线程异步刷写redo日志,1表示在事务提交的时候调用flush()方法把redo日志刷写到磁盘,2表示在事务调教的时候把redo日志写入redo文件,但是并不会调用flush方法刷写磁盘,所以1是最安全的,并且是推荐在主服务器上进行的设置。假如innodb_flush_log_at_trx_commit为0或者2,sync_binlog为1,那么在服务器当机或者mysql服务崩溃然后重启的时候,会不会出现主服务器丢失部分数据而从服务器却同步了主服务器丢失的那部分数据呢?有兴趣的同学可以做做实验验证一下。
        
        比较有用的参数:auto_increment_increment,auto_increment_offset,innodb_autoinc_lock_mode,relay_log_purge。auto_increment_increment表示自增增加的偏移量,auto_increment_offset表示自增的起始值,设置这两个参数最主要的作用是解决主主自增主键的冲突问题。innodb_autoinc_lock_mode用来控制auto_incr锁的策略。在比较旧的版本中,自增锁的类型是表锁,在5.1版本后增加了innodb_autoinc_lock_mode用来控制自增锁的加锁模式,有3个值,每个值具体的意义会在后面详细解释。relay_log_purge用来控制从机清理relay日志,比较重要的参数,默认为1。
        
        危险的参数:replicate_*,binglog_*,之所以把这些参数定义为危险参数,是因为在某些情况下它不会像你想象的那样工作,尤其是在mixed和statement日志格式下会产生一些比较奇怪的表现,而在row模式下好像不会出现那些奇怪的问题,但是可能是我还没有遇到,因为即使是在row模式下也没有证据证明这些参数是完全安全的。我准备在后面的文章中单独的介绍这些奇怪问题。

        简单理一下mysql主服务器是如何工作的:当slave start的时候,会向master发送COM_BINLOG_DUMP命令,master生成binlog_dump线程,然后根据slave传送来的日志文件名称和位置去查找日志读取位置,然后把日志发送给slave。master读取二进制日志的机制并不是每隔一段时间去读二进制日志,而是通过线程同步的方式,通过等待一个同步参数update_cond,来确定是否去读二进制日志,当有操作修改二进制日志的时候会调用 mysql_cond_broadcast(&update_cond)发送信号,这时候binlog_dump线程(一个或者多个)收到信号开始继续发送二进制日志。这里需要注意的是:如果主服务器一直处于空闲状态,binlog dump线程不会无限等待update_cond同步信号,因为还有一个超时参数来控制同步机制,附上该同步函数调用:

点击(此处)折叠或打开

  1. int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
  2.                                            const struct timespec *timeout)
  3. {
  4.   int ret= 0;
  5.   DBUG_ENTER("wait_for_update_bin_log");

  6.   if (!timeout)
  7.     mysql_cond_wait(&update_cond, &LOCK_log);
  8.   else
  9.     ret= mysql_cond_timedwait(&update_cond, &LOCK_log,
  10.                               const_cast<struct timespec *>(timeout));
  11.   DBUG_RETURN(ret);
  12. }

        再来理一下从机的运行机制,change master to操作会使得mysql记录下master的一些参数,但是并不会启动复制IO线程和复制sql线程。当slave start的时候,slave会收到COM_QUERY(COM_QUERY是一个枚举值,用来标识客户端发来的命令类型)的命令,获取sql_command,该sql_command的值为SQLCOM_SLAVE_START(枚举值,用来标识具体的sql命令)。然后调用方法start_slave(thd,active_mi,1 /* net report*/)来启动IO线程和SQL线程,附上相关的函数和代码:

点击(此处)折叠或打开

  1. case SQLCOM_SLAVE_START:
  2.   {
  3.     mysql_mutex_lock(&LOCK_active_mi);
  4.     start_slave(thd,active_mi,1 /* net report*/);
  5.     mysql_mutex_unlock(&LOCK_active_mi);
  6.     break;
  7.   }
调用start_slave函数:

点击(此处)折叠或打开

  1. int start_slave(THD* thd , Master_info* mi, bool net_report)
  2. {
  3.     .
  4.     .     //此处省略部分代码
  5.     .
  6.     if (!slave_errno)
  7.         slave_errno = start_slave_threads(0 /*no mutex */,
  8.                     1 /* wait for start */,
  9.                     mi,
  10.                     master_info_file,relay_log_info_file,
  11.                     thread_mask);

  12.     .
  13.     .    //此处省略部分代码
  14.     .

  15. }

具体如何启动sql线程和io线程:

点击(此处)折叠或打开

  1. int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
  2.                         Master_info* mi, const char* master_info_fname,
  3.                         const char* slave_info_fname, int thread_mask)
  4. {
  5.   mysql_mutex_t *lock_io=0, *lock_sql=0, *lock_cond_io=0, *lock_cond_sql=0;
  6.   mysql_cond_t* cond_io=0, *cond_sql=0;
  7.   int error=0;
  8.   DBUG_ENTER("start_slave_threads");

  9.   if (need_slave_mutex)
  10.   {
  11.     lock_io = &mi->run_lock;
  12.     lock_sql = &mi->rli.run_lock;
  13.   }
  14.   if (wait_for_start)
  15.   {
  16.     cond_io = &mi->start_cond;
  17.     cond_sql = &mi->rli.start_cond;
  18.     lock_cond_io = &mi->run_lock;
  19.     lock_cond_sql = &mi->rli.run_lock;
  20.   }

  21.   if (thread_mask & SLAVE_IO)
  22.     error= start_slave_thread(
  23. #ifdef HAVE_PSI_INTERFACE
  24.                               key_thread_slave_io,
  25. #endif
  26.                               handle_slave_io, lock_io, lock_cond_io,
  27.                               cond_io,
  28.                               &mi->slave_running, &mi->slave_run_id,
  29.                               mi);
  30.   if (!error && (thread_mask & SLAVE_SQL))
  31.   {
  32.     error= start_slave_thread(
  33. #ifdef HAVE_PSI_INTERFACE
  34.                               key_thread_slave_sql,
  35. #endif
  36.                               handle_slave_sql, lock_sql, lock_cond_sql,
  37.                               cond_sql,
  38.                               &mi->rli.slave_running, &mi->rli.slave_run_id,
  39.                               mi);
  40.     if (error)
  41.       terminate_slave_threads(mi, thread_mask & SLAVE_IO, !need_slave_mutex);
  42.   }
  43.   DBUG_RETURN(error);
  44. }
从代码可以看到,先启动IO线程,如果启动成功然后启动sql线程,最后判断一下两个线程是否都启动成功。

        本文简要记录下了mysql复制的大概的运行机制,对相关流程的运行机制进行了大致的分析。




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