通过mysql实现leader election

通过mysql实现leader election

当我们将同一个服务部署到多个节点(或者多个容器),以保证服务的高可用,但我们希望在同一时间,只有一个服务是active状态,其他服务处于inactive状态。或者说,要在这些服务中选出一个主节点。

Why not use zookeeper or etcd

zk和etcd确实能解决上述需求。这也是etcd和zk的主要用法之一。但有时候,我们已经依赖了mysql,而不想仅仅为了选主而引入新的组件。可以直接通过mysql来实现选主机制了。

Why not use GET_LOCK()

首先,要提一点,该方案不是基于锁来实现的。mysql的GET_LOCK()语句可以获取一把自定义名字的锁,只要连接不断开,该锁就会一直保持。同一把锁只能被一个连接持有。

SELECT GET_LOCK("leader_lock1", 0);

不使用锁来实现的主要原因是:

  • 你必须在程序内保持一个单独的mysql连接,并时刻维持其连接。这对于ORM或者mysql连接池是不太友好的。
  • 在Mysql 5.7之前,你无法查知当前是哪个连接持有这把锁。
  • 如果你的程序hang住了,这个锁仍然被持有,但此时服务是不可用的。

本方案的特性

  • 在多节点中,选出一个作为leader节点
  • leader节点定时主动的重申自己的地位
  • 基于超时的重选举机制,当旧leader节点超过x秒没有重申自己的地位,则开启重选举过程
  • 能够强制指定某节点为leader
  • 能够强制开始重选举过程
  • 某节点可以判断自己是否是leader
  • 任何人都可以查询当前leader是哪个节点

SQL方案

这个方案包含一个table,和一系列sql语句来实现上述的特性。

service_id标识一个包含了多个节点的服务,leader_id标识当前的leader是哪个节点。不同节点的sql语句中,service_id必须保持一致,leader_id则为能标识节点身份的值。一种简单的方法是,不同node将leader_id设置成自己 机器hostname 加 进程pid 。

CREATE TABLE leader_election (
  service_id varchar(128) NOT NULL,
  leader_id varchar(128) NOT NULL,
  last_seen_active timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (service_id)
) ENGINE=InnoDB

选举&重申

所有的节点,需要周期性执行下面的语句。比如每秒执行一次。

insert ignore into leader_election (
    service_id, leader_id, last_seen_active
) values (
    'controller', 'node1_1234', now()
) on duplicate key update
    leader_id = if(last_seen_active < now() - interval 20 second, values(leader_id), leader_id),
    last_seen_active = if(leader_id = values(leader_id), values(last_seen_active), last_seen_active)
;
  • 使用时,需要把controller替换成你的service_id,把10.17.0.12_1234替换成每个节点的hostname_pid
  • 该语句假设重选举的超时时间为20s
  • insert ignore的用法,参考这里
  • on duplicate key update的用法,参考这里

强制指定某节点为leader


replace into leader_election (
        service_id, leader_id, last_seen_active
    ) values (
        'controller', 'node2_2333', now()
    )
;

强制开始重选举

delete from leader_election;

判断自己是否是leader

select count(*) as is_leader from leader_election where service_id='controller' and leader_id='node1_1234';

如果结果等于0,那么自己不是leader,否则,代表自己是leader。

查询当前leader是谁

select leader_id as leader from leader_election where service_id='controller';

你可能感兴趣的:(通过mysql实现leader election)