五、MyCat 的连接池模型—— jdbc 模式

MyCat 采用基于 MySQL 实例的连接池模式,不同于传统基于 Database 的连接池,解决了资源浪费的问题。核心对象包括 ConMap、ConQueue、PhysicalDatasource 和 PhysicalDBPool。在创建、获取和释放数据库连接的过程中,MyCat 通过高效的数据结构和策略确保最佳吞吐量和并发能力。文章详细介绍了 MyCat 连接池的工作原理和关键步骤。
摘要由CSDN通过智能技术生成

MyCat 为了最高效的利用后端的 MySQL 连接,采取了不同于 Cobar 也不同于传统 JDBC 连接池的做法,**传统的做法是基于 Database 的连接池,即一个 MySQL 服务器上有 5 个 Database,则每个 Database 独占最大200 个连接。**这种模式的最大问题在于,将一个数据库所具备的最大 1000 个连接,隔离成了更新小的连接池,于是可能产生一个应用的连接不够,但其他应用的连接却很空闲的资源浪费情况,而对于分片这种场景,这个缺陷则几乎是致命的,因为每个分片所对应的 Database 的连接数量被限制在了一个很小的范围内,从而导致系统并发能力的大幅降低。而 Mycat 则采用了基于 MySQL 实例的连接池模式,每个 Database 都可以用现有的 1000 个连接中的空闲连接

5.1 核心对象

5.1.1 ConMap 和 ConQueue

在 MyCat 的连接池里,当前可用的、空闲的 MySQL 连接是放到一个 ConcurrentHashMap 的数据结构里,Key 为当前连接对应的 database 名,另外还有二级分类 ConQueue,按照连接是自动提交模式还是手动提交模式进行区分,这个设计是为了高效的查询匹配的可用连接。ConMapConQueue 包含的关键对象有:

  • ConcurrentHashMap<String, ConQueue> items:可用的 MySQL 连接容器,key 为当前连接对应的 database 名,value 为 ConQueue 对象,里面包含了两个存储数据库连接的队列
  • ConcurrentLinkedQueue<BackendConnection> autoCommitCons:自动提交的数据库连接
  • ConcurrentLinkedQueue<BackendConnection> manCommitCons:手动提交的数据库连接
public class ConMap {
   
    /**
    * key:当前连接对应的 Database
    * ConQueue:数据库连接队列(按照连接是自动提交模式还是手动提交模式进行区分,这个设计是为了高效的查询匹配的可用连接)
    */
    private final ConcurrentHashMap<String, ConQueue> items = new ConcurrentHashMap<String, ConQueue>();
}

public class ConQueue {
   
    private final ConcurrentLinkedQueue<BackendConnection> autoCommitCons = new ConcurrentLinkedQueue<BackendConnection>();
    private final ConcurrentLinkedQueue<BackendConnection> manCommitCons = new ConcurrentLinkedQueue<BackendConnection>();
    private long executeCount;
}

BackendConnection 为后端数据库连接,其实现有 JDBCConnectionMySQLConnection

在这里插入图片描述

5.1.2 PhysicalDatasource

对应于 <dataHost> 节点下的 <writeHost><readHost> 子节点,表示一个物理数据库实例。每个数据库实例中保存了多个可用的数据库连接(BackendConnection),MyCat 初始化时,根据 <dataHost> 节点的 minCon 属性值初始化多个可用的数据库连接。其关键对象有:

  • name<writeHost> 的 host 属性值
  • size:读或写连接池的最大连接数
  • conMap:存放当前可用的数据库连接
  • DataHostConfig<dataHost> 节点对应的配置
  • DBHostConfig<writeHost> 节点配置
public abstract class PhysicalDatasource {
   
    private final String name;
    private final int size;
    private final DBHostConfig config;
    private final ConMap conMap = new ConMap();
    private final boolean readNode;
    private final DataHostConfig hostConfig;
    private PhysicalDBPool dbPool;
}

PhysicalDatasource 的实现类有:
在这里插入图片描述

5.1.3 PhysicalDBPool

对应于 <dataHost name="localhost1" > 节点,表示物理数据库实例池。由于 <datahost> 节点可包含多个 <writeHost> 节点,因此 PhysicalDBPool 可以包含多个物理数据库实例,其关键对象有:

  • hostName<dataHost> 标签的 name 属性
  • writeSourcesreadSources:可写和可读的多个物理数据库实例,对应于 <writeHost><readHost>
  • activedIndex:表明了当前是哪个写节点的数据源在生效
public class PhysicalDBPool {
   
    private final String hostName;

    protected PhysicalDatasource[] writeSources;
    protected Map<Integer, PhysicalDatasource[]> readSources;

    protected volatile int activedIndex;
    private final DataHostConfig dataHostConfig;
}

5.1.4 PhysicalDBNode

对应于 <dataNode /> 节点,表示一个数据库分片,PhysicalDBNode 包含的关键对象有:

  • name :dataNode 名称,对应于 <dataNode> 标签的 name 属性
  • database :数据库名称,对应于 <dataNode> 标签的 database 属性
  • dbPool :MySQL 连接池,里面包含了多个数据库实例 PhysicalDatasource,并将其按照读节点和写节点分类,实现读写分类和节点切换的功能。其中 activedIndex 属性表明了当前是哪个写节点的数据源在生效
public class PhysicalDBNode {
   
    protected final String name;
    protected final String database;
    protected final PhysicalDBPool dbPool;
}

若 schema.xml 中配置了一下分片节点:

<dataNode name="dn1" dataHost="localhost1" database="db_demo_01"/>
<dataNode name="dn2" dataHost="localhost1" database="db_demo_02"/>
<dataNode name="dn3" dataHost="localhost1" database="db_demo_03"/>

当某个用户会话需要一个自动提交的,到分片 <dataNode name="dn1" dataHost="localhost1" database="db_demo_01"/> 的 SQL 连接的时候,分片节点 dn1 首先在连接池 dbPool 中查找是否有数据库 db_demo_01 (对应于 PhysicalDatasource)上的可用连接,若有则看是否有自动提交模式的连接,找到就返回,否则返回 db_demo_01 上的手动提交模式的连接;若没有 db_demo_01 的可用连接,则随机返回一个其他数据库(db_demo_02db_demo_03)对应的可用连接;若没有其他数据库也没有可用连接,并且连接池还没达到上限,则创建一个新连接并返回

上述获取数据库连接的逻辑有一种情况是:用户会话得到的数据库连接可能不是来自于 db_demo_01 的,因此在执行具体的 SQL 之前,还有一个自动同步数据库连接的过程:包括事务隔离级别、事务模式、字符集、database 等四个指标。同步完成以后,才会执行具体的 SQL 请求

通过共享一个 MySQL 上的所有数据库的可用连接,并结合连接状态同步的特性,MyCat 的连接池做到了最佳的吞吐量,也在一定程度上提升了整个系统的并发支撑能力

5.2 创建数据库连接

5.2.1 创建新数据库连接时机

创建新数据库连接的方法为 PhysicalDatasource#createNewConnection(io.mycat.backend.mysql.nio.handler.ResponseHandler, java.lang.Object, java.lang.String),其有两个创建连接的触发时机:

在这里插入图片描述

  • io.mycat.backend.datasource.PhysicalDatasource#createByIdleLitte

    执行空闲检测时触发,若当前数据库连接总数(空闲连接数和活动链接数之和)小于连接池的最大连接数,且空闲连接数小于连接池最小连接数,则调用 PhysicalDatasource#createByIdleLitte 方法创建新数据库连接

    if ((createCount > 0) && (idleCons + activeCons < size) && (idleCons < hostConfig.getMinCon())) {
         
       createByIdleLitte(idleCons, createCount);
    }
    
  • io.mycat.backend.datasource.PhysicalDatasource#getConnection

    首先调用 ConMap#tryTakeCon(java.lang.String, boolean) 获取当前 database 的可用连接,若有则立即返回,否则从其他的 database 上找一个可用连接返回。若 ConMap#tryTakeCon 返回 null,表示数据库连接池中没有空闲连接,则调用 PhysicalDatasource#createNewConnection 创建新连接

    public void getConnection(String schema, boolean autocommit, final ResponseHandler handler, final Object attachment) throws IOException {
         
        // 从当前连接 map 中拿取已建立好的后端连接
        BackendConnection con = this.conMap.tryTakeCon(schema, autocommit);
        if (con != null) {
         
            //如果不为空,则绑定对应前端请求的 handler
            takeCon(con, handler, attachment, schema);
        } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值