PolarDB提供了三种一致性级别:最终一致性、会话一致性和全局一致性,满足您在不同场景下对一致性级别的要求。

问题与解决方案

MySQL的主从复制功能,能够将主库的Binlog异步传输到备库并在备库中实时应用,这样既实现了备库可查询,减轻主库的压力,又保证了高可用。

虽然实现了备库可查询,但存在如下问题:

  • 主库和备库一般提供两个不同的访问地址,在访问不同库时,需要在应用程序上修改成对应库的地址,对应用有侵入。
  • MySQL的复制是异步的,因此备库的数据并不是最新的而是有延迟的,无法保证查询的一致性。

为了解决第一个问题,MySQL引入了读写分离代理功能。一般的实现是,代理会伪造成MySQL与应用程序建立好连接,解析发送进来的每一条SQL,如果是UPDATE、DELETE、INSERT、CREATE等写操作则直接发往主库,如果是SELECT则发送到备库。

3

但读写分离还是无法解决由于延迟导致的查询不一致问题。当数据库负载很高时,例如对大表执行DDL(如加字段)操作或大批量插入数据的时候,延迟会非常严重,从而导致无法从只读节点中读取最新数据。

PolarDB采用了异步物理复制方式实现了主节点和只读节点间的数据同步。主节点的数据更新后,相关的更新会应用到只读节点,具体的延迟时间与写入压力有关(一般在毫秒级别),通过异步复制的方式确保了主节点和只读节点间数据的最终一致。PolarDB提供了如下三种一致性级别,满足您在不同场景下对一致性级别的要求:

说明 如何修改一致性级别,请参见修改和释放集群地址

最终一致性

  • 功能介绍
    PolarDB是读写分离的架构,传统的读写分离只提供最终一致性的保证,主从复制延迟会导致从不同节点查询到的结果不同,例如在一个会话内连续执行如下查询,最后的SELECT结果可能会不同(具体的访问结果由主从复制的延迟决定)。
    INSERT INTO t1(id, price) VALUES(111, 96);
    UPDATE t1 SET price = 100 WHERE id=111;
    SELECT price FROM t1;
  • 适用场景

    若需要减轻主节点压力,让尽量多的读请求路由到只读节点,您可以选择最终一致性。

会话一致性

  • 功能介绍

    针对最终一致性导致查询结果不同的问题,通常需要将业务进行拆分,将一致性要求高的请求直接发往主节点,而可以接受最终一致性的请求则通过读写分离发往只读节点。这既增加了主节点的压力,影响读写分离的效果,又增加了应用开发的负担。

    为解决上述问题,PolarDB提供了会话一致性(也称因果一致性)。会话一致性保证了同一个会话内,一定能够查询到读请求执行前已更新的数据,确保了数据单调性。

    PolarDB的链路中间层做读写分离的同时,中间层会追踪各个节点已经应用的Redo日志位点,即日志序号(Log Sequence Number,简称LSN)。同时每次数据更新时PolarDB会记录此次更新的位点为Session LSN。当有新请求到来时,PolarDB会比较Session LSN和当前各个节点的LSN,仅将请求发往LSN大于或等于Session LSN的节点,从而保证了会话一致性。表面上看该方案可能导致主节点压力大,但是因为PolarDB是物理复制,速度极快。

    4

    在上述场景中,当更新完成后,返回客户端结果时复制就同步在进行,而当下一个读请求到来时,主节点和只读节点之间的数据复制极有可能已经完成。且大多数应用场景都是读多写少,所以经验证在该机制下既保证了会话一致性,又保证了读写分离负载均衡的效果。

  • 适用场景

    PolarDB的一致性级别越高,对主库的压力越大,集群性能也越低。推荐使用会话一致性,该级别对性能影响很小而且能满足绝大多数应用场景的需求。

全局一致性

  • 功能介绍

    在部分应用场景中,除了会话内部有逻辑上的因果依赖关系,会话之间也存在依赖关系,例如在使用连接池的场景下,同一个线程的请求有可能通过不同连接发送出去。对数据库来说这些请求属于不同会话,但是业务逻辑上这些请求有前后依赖关系,此时会话一致性便无法保证查询结果的一致性。因此PolarDB提供了全局一致性来解决该问题。

    会话间存在的依赖关系

    每个读请求到达PolarDB数据库代理时,代理都会先去主节点确认当前最新的LSN位点,假设为LSN0(为了减少每次读请求都去获得主节点的最新LSN,内部做了批量优化), 然后等待所有只读节点的LSN都更新至主节点的最新LSN(即LSN0)后,代理再将读请求发送至只读节点。这样就能保证该读请求能够读到至请求发起时刻为止,任意一条已完成更新的数据。

    全局一致性提供如下两个配置参数:
    参数 说明
    ConsistTimeout 全局一致性读超时时间,即允许用于只读节点的LSN更新至主节点最新LSN的时间。若超出该时间,PolarDB代理将根据ConsistTimeoutAction参数设置进行对应操作。

    取值范围0~300000,默认值为20,单位为毫秒。

    ConsistTimeoutAction 全局一致性读超时策略,若未能在参数ConsistTimeout设置的时间内将只读节点的LSN更新至主节点最新LSN,PolarDB代理将根据ConsistTimeoutAction参数设置进行对应操作。

    取值范围如下:

    • 0:将读请求发往主节点(默认)。
    • 1:代理返回一个错误报文wait replication compelete timeout, please retry给应用端。
    说明 如何修改全局一致性读超时时间全局一致性读超时策略,请参见修改集群地址
  • 适用场景

    当主从延迟较高时,使用全局一致性可能会导致更多的请求被路由到主节点,造成主节点压力增大,业务延迟也可能增加。因此建议在读多写少的场景下选择全局一致性。

一致性级别选择最佳实践

  • PolarDB一致性级别越高,集群性能越低。推荐使用会话一致性,该级别对性能影响很小而且能满足绝大多数应用场景的需求。
  • 若对不同会话间的一致性需求较高,可以选择如下方案之一:
    • 使用HINT将特定查询强制发送至主节点执行。
      /*FORCE_MASTER*/ select * from user;
      说明
      • 若您需要通过MySQL官方命令行执行上述Hint语句,请在命令行中加上-c参数,否则该Hint会被MySQL官方命令行过滤导致Hint失效,具体请参见MySQL官方命令行
      • Hint的路由优先级最高,不受一致性级别和事务拆分的约束,使用前请进行评估。
      • Hint语句里不要有改变环境变量的语句,例如/*FORCE_SLAVE*/ set names utf8;等,这类语句可能导致后续的业务出错。
    • 选择全局一致性。