从源码分析 MGR 的流控机制

Group Replication 是一种 Shared-Nothing 的架构 , 每个节点都会保留一份数据 。
虽然支持多点写入 , 但实际上系统的吞吐量是由处理能力最弱的那个节点决定的 。
如果各个节点的处理能力参差不齐 , 那处理能力慢的节点就会出现事务堆积 。
在事务堆积的时候 , 如果处理能力快的节点出现了故障 , 这个时候能否让处理能力慢的节点(存在事务堆积)接受业务流量呢?

  1. 如果不等待堆积事务应用完 , 直接接受业务流量 。
    一方面会读到旧数据 , 另一方面也容易出现写冲突 。
    为什么容易出现写冲突呢?因为基于旧数据进行的写操作 , 它的 snapshot_version 小于冲突检测数据库中对应记录的 snapshot_version , 这个时候 , 冲突检测会失败 。
  2. 如果等待堆积事务应用完才接受业务流量 , 又会影响数据库服务的可用性 。
为了避免出现上述两难场景 , Group Replication 引入了流控机制 。
在实现上 , Group Replication 的流控模块会定期检查各个节点的事务堆积情况 , 如果超过一定值 , 则会触发流控 。
流控会基于上一周期各个节点的事务认证情况和事务应用情况 , 决定当前节点(注意是当前节点 , 不是其它节点)下个周期的写入配额 。
超过写入配额的事务操作会被阻塞 , 等到下个周期才能执行 。
接下来 , 我们通过源码分析下流控的实现原理 。
本文主要包括以下几部分:
  1. 流控触发的条件 。
  2. 配额的计算逻辑 。
  3. 基于案例定量分析配额的计算逻辑 。
  4. 配额作用的时机 。
  5. 流控的相关参数 。
流控触发的条件默认情况下 , 节点的状态信息是每秒发送一次(节点的状态信息是在 flow_control_step 中发送的 , 发送周期由 group_replication_flow_control_period 决定) 。
当接受到其它节点的状态信息时 , 会调用 Flow_control_module::handle_stats_data 来处理 。
下面我们看看 Flow_control_module::handle_stats_data 函数的处理逻辑 。
int Flow_control_module::handle_stats_data(const uchar *data, size_t len,                                           const std::string &member_id) {  DBUG_TRACE;  int error = 0;  Pipeline_stats_member_message message(data, len);  m_flow_control_module_info_lock->wrlock();  // m_info 是个字典 , 定义是 std::map<std::string, Pipeline_member_stats>  // 其中 , key 是节点的地址 , value 是节点的状态信息 。  Flow_control_module_info::iterator it = m_info.find(member_id);  // 如果 member_id 对应节点的状态信息在 m_info 中不存在 , 则插入 。  if (it == m_info.end()) {    Pipeline_member_stats stats;    std::pair<Flow_control_module_info::iterator, bool> ret = m_info.insert(        std::pair<std::string, Pipeline_member_stats>(member_id, stats));    error = !ret.second;    it = ret.first;  }  // 更新节点的统计信息  it->second.update_member_stats(message, m_stamp);  // 检查是否需要流控  if (it->second.is_flow_control_needed()) {    ++m_holds_in_period;#ifndef NDEBUG    it->second.debug(it->first.c_str(), m_quota_size.load(),                     m_quota_used.load());#endif  }  m_flow_control_module_info_lock->unlock();  return error;}

经验总结扩展阅读