当前位置: 首页 > article >正文

mysql 实现分库分表之 --- 基于 MyCAT 的分片策略详解

引言

在我们日常工作的项目中,特别是面向 C 端用户的产品,随着业务量的逐步扩大,数据量也呈指数级增长。为了应对日益增长的数据库压力,数据库优化已成为项目中不可或缺的一环,而分库分表则是海量数据优化方案中的重要手段。在面试中,有关分库分表的知识点也经常被提及,可见其在实际业务中的关键地位。

今天,我将详细介绍为什么我们需要进行分库分表,以及常见的分库分表方案。同时,我将使用其中一种方案——MyCAT,来演示分库分表的实际操作,帮助大家更好地理解和掌握这一技术。

为什么要分库分表?

随着互联网的快速发展和用户数量的激增,现代应用程序的数据量也在以指数级增长。无论是电商平台的交易数据、社交网络的用户数据,还是金融系统的交易日志,数据规模的持续扩展给数据库带来了极大的存储和处理压力。

在早期,传统单库架构(Monolithic Database Architecture)能够满足中小规模的业务需求,其简单的结构和集中式管理使得数据维护相对方便。然而,随着数据量的迅速膨胀和访问频率的增加,单一数据库开始面临一些关键的性能瓶颈:

  1. 存储瓶颈:单一数据库服务器的存储空间和性能是有限的。随着数据量的增加,单库架构会逐渐耗尽存储资源,进而影响数据库性能,甚至导致系统崩溃。
  2. I/O 性能问题:数据库查询操作需要在大规模数据中进行索引扫描和磁盘 I/O 操作,数据量增加会导致查询速度明显下降。此外,频繁的写操作会造成数据库的写锁争用问题,使系统的并发处理能力受限。
  3. 扩展性受限:单库架构在扩展性上存在瓶颈,难以满足高并发和高可用的需求。即使通过垂直扩展(增加服务器硬件配置)来提升性能,成本依然高昂,并且物理硬件的扩展性有上限。

面对这些挑战,分库分表作为一种水平扩展方案应运而生。通过将数据分散到多个数据库或多个表中,不仅可以减轻单个数据库的压力,还可以提高数据的读写性能,从而为高并发、高可用的系统提供支持。分库分表的核心思想在于“化整为零”:将一个大库分成多个小库,将一张大表分成多张小表,使得数据更加均匀地分布在不同的物理资源上。


多少数据量才需要分库分表?

分库分表虽然能有效解决大规模数据带来的性能瓶颈,但并不是所有项目都需要一开始就实施分库分表。通常,我们会在数据量和访问量达到一定规模后,才会考虑分库分表。以下是一些常见的参考指标,帮助判断是否需要进行分库分表:

  1. 数据量的增长趋势:当一个数据库的单表数据量在数百万到千万级别时,查询性能可能会出现明显下降。在某些高并发场景中,甚至达到百万级别时便会触发性能瓶颈。如果预计数据量还将持续增长,提前实施分库分表可以避免未来的性能问题。
  2. 单表数据量超过 1000 万行:在一般的 OLTP(在线事务处理)系统中,如果单表数据量超过 1000 万行,查询和写入性能通常会受到影响。这时可以考虑按数据类型、时间、用户等维度对表进行分区或分表。
  3. 单表容量达到 10GB - 50GB:大容量的表会对 I/O 性能和内存消耗造成较大压力,影响数据库的整体响应速度。此时可以考虑通过分表的方式,将数据分散到不同的表中,以提高访问效率。
  4. QPS(每秒查询请求数)超过 1000 次:如果数据库的查询请求数(QPS)超过 1000 次,且查询延迟明显增加,可能需要考虑分库分表来提升系统的处理能力。高并发的业务场景中,分库分表可以将请求分散到不同的数据库上,降低单个库的负载。
  5. 业务扩展和高可用性需求:如果业务已经扩展到多地区多机房,或对高可用性有严格的要求,可以通过分库分表将不同地区的数据存储在对应的数据中心,以降低跨地区访问延迟并提高可用性。

需要注意的是,分库分表是一种复杂的数据库优化方案,实施成本较高。因此,对于数据量较小、访问量较低的项目,过早实施分库分表可能会增加系统复杂度而得不偿失。在实际项目中,建议根据业务需求、数据增长趋势和性能测试结果来评估是否需要分库分表。


分库分表的常见方案

分库分表的实现方式多种多样,不同的方案适用于不同的业务场景。以下是几种常见的分库分表方案:

1. 垂直分库(Vertical Sharding)

垂直分库是将数据库按照业务模块进行拆分。比如,在电商系统中,可以将用户数据、订单数据、商品数据分别存储在不同的数据库中。这种方式常用于业务模块较多、各模块之间数据关联较少的场景。

  • 优点

    • 业务逻辑清晰,易于维护。
    • 单库数据量减少,查询性能提升。
    • 不同业务模块的数据隔离,利于扩展性。
  • 缺点

    • 跨库查询不便,特别是在需要关联查询的场景中。
    • 业务模块划分较为固定,不适合频繁变化的业务需求。
  • 适用场景:适用于业务模块明确、数据之间关联较少的系统,如电商平台、内容管理系统等。

2. 垂直分表(Vertical Partitioning)

垂直分表是将一张表中不同的列拆分成多个表,以减少表的宽度和数据冗余。例如,用户表中包含登录信息和用户详情,可以将登录信息单独存储在一张表中,减少查询的数据量。

  • 优点

    • 减少数据冗余,节省存储空间。
    • 查询时只需访问需要的列,减少 I/O 操作,提升查询效率。
  • 缺点

    • 需要多次查询才能获得完整的数据,查询逻辑复杂。
    • 对于一些需要频繁关联查询的场景,性能提升有限。
  • 适用场景:适合字段较多且查询频率不一的场景,如用户表中包含不同用途的字段。

3. 水平分库(Horizontal Sharding)

水平分库是将同一业务模块的数据按一定规则(如用户 ID、订单 ID)划分到多个数据库中。每个库中都存放同样结构的表,但数据分布不同。

  • 优点

    • 单库的数据量减少,读写性能提升。
    • 数据的水平扩展性较强,可以动态增加数据库实例。
  • 缺点

    • 数据路由和查询逻辑复杂,需要管理不同数据库之间的访问。
    • 跨库事务和跨库查询较为复杂,对数据一致性要求较高的场景难以支持。
  • 适用场景:适合高并发、海量数据的场景,如社交应用中的用户数据、订单数据等。

4. 水平分表(Horizontal Partitioning)

水平分表是将同一表的数据按一定规则拆分成多张表存储在同一数据库中。比如,可以按照时间、ID 范围对订单表分表,避免单表数据量过大影响性能。

  • 优点

    • 减少单表数据量,提升查询效率。
    • 数据分片灵活,可以根据业务需求进行定制。
  • 缺点

    • 对业务逻辑影响较大,需要修改数据库访问层的代码。
    • 需要额外的分表管理,增加数据运维的复杂度。
  • 适用场景:适用于数据量大且数据类型相同的业务场景,如订单、日志等时间序列数据。

5. 混合分库分表

混合分库分表结合了垂直分库与水平分库,或者垂直分表与水平分表,根据业务复杂性对数据库进行灵活拆分。例如,既可以按照业务模块垂直分库,也可以在同一模块中对单表数据进行水平分表。

  • 优点

    • 灵活性高,可以应对复杂的业务场景。
    • 数据扩展能力强,支持海量数据处理。
  • 缺点

    • 系统设计和实现难度较大,管理和维护成本较高。
    • 跨库和跨表查询的复杂性增加,需要实现较复杂的数据路由和查询策略。
  • 适用场景:适合复杂的大型系统,如涉及多种业务模块的金融、电商平台等。

在实际项目中,分库分表方案的选择取决于业务的实际需求。垂直分库和垂直分表适合简化系统设计的场景,水平分库和水平分表则适用于高并发的大数据场景,而混合分库分表则能支持复杂的业务需求。在接下来的内容中,我们将详细介绍如何使用 MyCAT 来实现分库分表,以提升系统的性能和扩展性。


如何实现分库分表

分库分表的实现方式多样,一般可以通过手动代码封装或者借助插件实现。

1. 手动代码封装

在一些项目中,我们可以通过在代码层进行逻辑封装来实现分库分表。这种方式的核心思路是通过自定义的数据路由策略,将请求分散到不同的库或表中。通常会根据特定字段(如用户 ID、订单 ID)进行分片计算,来确定数据的存储位置。

  • 实现过程

    1. 定义分片规则:如通过 ID 进行 Hash 计算或根据时间区间进行分片。
    2. 封装数据访问层:在数据访问层对查询、插入等操作进行路由控制,根据分片规则选择目标库或表。
    3. 跨库事务处理:手动封装跨库事务逻辑,确保数据一致性。
  • 优缺点

    • 优点:代码封装灵活,可以根据业务需求自定义分片策略。
    • 缺点:实现复杂,特别是对于跨库事务和数据一致性要求较高的场景,维护成本较高。

我之前的文章中已经分享过关于手动封装分库分表的代码示例和相关伪代码,详细描述了如何设计路由策略和封装数据访问层。

2. 使用分库分表中间件插件

除了手动封装,借助插件实现分库分表也是一种常见且高效的方式。中间件可以隐藏底层数据库的拆分细节,让开发人员只需关注业务逻辑即可。以下是几种常见的分库分表中间件:

常见的分库分表中间件

  1. MyCAT

    • 简介:MyCAT 是一款开源的分布式数据库中间件,支持多种分片策略,并且提供读写分离、跨库事务、缓存等功能。作为一种数据库代理,它能够支持 MySQL、Oracle 等多种数据库。
    • 适用场景:适合需要水平分片、读写分离、高并发访问的大型项目,尤其是在 MySQL 场景中应用较多。
    • 特点:MyCAT 配置灵活,支持多种分片方式和跨库事务,但配置和运维相对复杂。
  2. ShardingSphere

    • 简介:ShardingSphere 是 Apache 旗下的开源分布式数据库中间件,包含 Sharding-JDBC、Sharding-Proxy、Sharding-Sidecar 三种形态,支持多数据库之间的分库分表、读写分离和分布式事务。
    • 适用场景:适合多种数据库组合需求(如 MySQL 和 PostgreSQL),以及微服务架构中的分布式数据处理。
    • 特点:ShardingSphere 提供 API 层的分片实现(Sharding-JDBC)和代理层的分片实现(Sharding-Proxy),灵活性强,支持微服务架构中的分布式事务,但对资源要求较高。
  3. Cobar

    • 简介:Cobar 是阿里巴巴开源的 MySQL 数据库中间件,用于支持数据分库分表、读写分离。它的架构类似于数据库代理服务器,可以将 SQL 请求转发到相应的分片数据库。
    • 适用场景:适用于读写分离需求较高、并且主要基于 MySQL 的系统。
    • 特点:Cobar 实现了类似数据库代理的功能,支持自动分片和读写分离,但由于项目较早,社区不活跃,功能相对 MyCAT 和 ShardingSphere 较少。
  4. TDDL(Taobao Distributed Data Layer)

    • 简介:TDDL 是阿里巴巴内部使用的分布式数据库访问层。它支持多数据源管理、分库分表、读写分离等功能,但该项目主要应用于阿里巴巴内部,并未广泛开源。
    • 适用场景:适合阿里巴巴体系内的项目,支持高并发、大数据量的交易处理。
    • 特点:具备较强的事务处理和并发处理能力,但由于不是完全开源,外部项目使用较少。
  5. Vitess

    • 简介:Vitess 是一个用于分布式 MySQL 的数据库中间件,原生支持水平分片和容器化,适用于大规模分布式系统。它由 YouTube 开发,现已在 CNCF(Cloud Native Computing Foundation)托管。
    • 适用场景:适用于需要 MySQL 分布式解决方案的大规模系统,特别是容器化和云原生环境。
    • 特点:Vitess 专为水平分片设计,原生支持 Kubernetes,具有很强的扩展能力,但对开发和运维要求较高。

今天我们将详细介绍如何使用 MyCAT 来实现分库分表。MyCAT 是一种开源的分布式数据库中间件,能够帮助我们实现灵活的水平分片、读写分离和跨库事务功能。相比于手动封装,MyCAT 提供了丰富的分片策略和数据路由规则,可以帮助开发者轻松应对大规模数据存储和访问需求。


MyCAT

MyCAT 是一款开源的分布式数据库中间件,基于 Java 开发,主要用于实现数据的分库分表和水平扩展。作为数据库代理,MyCAT 位于应用层与数据库之间,能够接收应用程序的 SQL 请求并将其分发到不同的数据库节点上,通过灵活的分片和数据路由策略,帮助实现高效的数据分布和访问。

在数据量和访问量不断增长的场景中,传统的单库架构逐渐无法满足业务需求。而 MyCAT 通过提供分库分表、读写分离、跨库事务等功能,使得系统能够承载更大的数据量和更高的并发量,从而有效解决大数据场景下的性能瓶颈问题。

MyCAT 的核心优势

  1. 分库分表

    • MyCAT 支持多种分片策略,如 Hash 分片、范围分片、自定义分片等,可以根据业务需求选择合适的分片规则。
    • 分库分表功能可以将数据分散到多个数据库或多张表中,降低单库压力,提升系统的读写性能。
    • 配置简单,灵活性高,开发者可以轻松地将逻辑库映射到不同的物理库或表,实现数据的水平扩展。
  2. 读写分离

    • MyCAT 支持主从读写分离,将读请求分配到从库,写请求分配到主库,从而分散数据库的压力,提高读写性能。
    • 读写分离的实现对应用程序透明,开发者只需在 MyCAT 中进行简单配置,即可实现读写操作的自动分离。
    • 适合读多写少的应用场景,可以在不增加主库压力的情况下显著提升查询性能。
  3. 跨库事务支持

    • MyCAT 支持分布式事务,能够在多个数据库之间实现事务控制,保证数据的一致性。
    • 通过两阶段提交(2PC)协议,MyCAT 可以处理跨库事务,确保在分库分表的环境下依然能够实现事务的 ACID 特性。
    • 适用于需要跨多个分片节点的事务操作,尤其是在资金处理、订单处理等需要强一致性的场景中。
  4. 高可扩展性

    • MyCAT 可以根据业务需求水平扩展,通过动态增加数据库实例来支持更大规模的数据。
    • 支持多数据源和多实例配置,便于数据在多个物理节点上的分布,适应业务的持续增长。
    • 在系统压力增大时,可以轻松增加节点和配置,保持系统的高可用性。
  5. 支持多种数据库类型

    • MyCAT 支持 MySQL、Oracle、PostgreSQL 等多种数据库类型,可以适应不同数据库环境的分库分表需求。
    • 具备跨库查询的能力,支持 SQL 的聚合查询、排序、分页等功能,使其兼容性更强。

MyCAT 的工作原理

MyCAT 作为一个分布式数据库中间件,位于应用程序和后端数据库之间,充当数据库代理的角色。其核心原理是接收来自应用程序的 SQL 请求,通过配置好的分片规则和路由策略,将请求路由到相应的物理数据库或表上,并将查询结果整合返回给应用程序。

具体来说,MyCAT 的工作过程可以分为以下几步:

  1. SQL 解析:MyCAT 接收到应用程序发送的 SQL 请求后,会先对 SQL 语句进行解析,分析 SQL 的类型(如查询、插入、更新、删除等)、目标表以及查询条件。
  2. 路由选择:根据 SQL 语句的解析结果和配置的分片规则,MyCAT 确定 SQL 应该路由到哪个数据节点或分片上。例如,对于查询订单信息的请求,MyCAT 会根据订单 ID 的分片规则选择目标库或表。
  3. SQL 执行:MyCAT 将路由后的 SQL 请求发送到相应的物理数据库执行。对于跨库查询的请求,MyCAT 会在多个节点上并发执行 SQL 语句,并将各个节点的查询结果合并。
  4. 结果整合:在跨库查询的场景下,MyCAT 会将来自不同数据节点的查询结果整合,进行排序、分页、聚合等处理,然后将结果返回给应用程序。

通过上述步骤,MyCAT 实现了对数据的透明分片、路由和整合,使得应用程序可以像操作单个数据库一样使用分库分表的数据库架构。

MyCAT 支持的数据库类型及与物理库的映射

MyCAT 支持多种主流数据库,包括 MySQL、Oracle、PostgreSQL 等,能够适应不同数据库环境的需求。不同的数据库类型可以被配置成 MyCAT 的数据节点(DataNode),通过配置文件将逻辑库与物理库进行映射。

MyCAT 的映射关系主要体现在两个核心配置文件中:

  • schema.xml:在 schema.xml 中,开发者可以定义逻辑库(例如 logic_db)和逻辑表(例如 user_table),并通过分片规则将逻辑表映射到具体的数据节点或物理表上。
  • server.xml:在 server.xml 中配置物理数据库的连接信息,例如 IP 地址、端口、用户名和密码,并将这些数据库作为 MyCAT 的后端节点使用。

通过这种逻辑库和物理库的映射关系,MyCAT 能够在数据量不断增长的情况下平衡各个物理库的负载,实现高效的数据分布。

MyCAT 支持的分片策略

MyCAT 提供了多种灵活的分片策略,可以满足不同业务场景的需求。以下是 MyCAT 支持的几种常见分片策略,以及其适用场景:

  1. Hash 分片

    • 原理:通过对分片字段(如用户 ID、订单 ID)进行 Hash 计算,将数据均匀分布到多个分片节点上。
    • 适用场景:适合数据分布较为均匀的场景,如用户表、订单表等,能够在多个分片节点间平衡数据量和访问压力。
    • 优点:分布均匀,容易扩展,便于实现水平扩展。
    • 缺点:不适合范围查询,若需要跨分片进行聚合查询,性能可能受限。
  2. 范围分片

    • 原理:根据分片字段的取值范围来划分数据。例如,将订单按年份或月份进行分片,2022 年的订单放在分片 A,2023 年的订单放在分片 B。
    • 适用场景:适合按时间、地理区域等范围查询的场景,如日志表、订单表。
    • 优点:适合范围查询,数据按需存储在相应的分片节点中,查询效率高。
    • 缺点:数据分布不均匀,某些分片节点可能承受更高的访问压力。
  3. 自定义分片

    • 原理:用户可以根据特定业务需求自定义分片规则,例如根据用户等级、地区等业务字段进行分片。
    • 适用场景:适用于有特殊分片需求的业务场景,如电商平台根据品类进行商品数据分片。
    • 优点:灵活性高,可以根据业务需求精确分片。
    • 缺点:实现复杂,需要自行编写分片规则,适用性有限。

如何选择分片策略

在实际项目中,根据数据的类型、分布特征和访问需求选择合适的分片策略是非常关键的:

  • Hash 分片适合用户数据、交易数据等需要均匀分布的场景。
  • 范围分片适合按时间、地理位置等连续性字段分布的数据,例如日志和历史数据。
  • 自定义分片适合有特殊业务需求的场景,需要根据业务的特点灵活配置。

通过合理选择和配置分片策略,MyCAT 能够帮助开发者有效地管理和分配数据,确保系统在高并发、高数据量的情况下依然保持良好的性能。


MyCAT 环境搭建与安装

在实际项目中,搭建 MyCAT 环境是实现分库分表的第一步。下面将详细介绍 MyCAT 的安装步骤,分为直接安装和使用 Docker 安装两种方式。

1. 直接安装 MyCAT
步骤 1:下载 MyCAT
  • 前往 MyCAT 官方网站 或 GitHub 仓库 下载最新版本的 MyCAT。
  • 下载的是一个压缩包,例如 Mycat-server-1.6.7.4-release-20210108230010-linux.tar.gz
步骤 2:解压安装包
  • 将下载的安装包上传到服务器的指定目录(例如 /usr/local),然后解压:

    tar -zxvf Mycat-server-*.tar.gz
    
  • 解压后,会生成一个名为 mycat 的目录。

步骤 3:配置环境变量
  • 为了方便使用,可以将 MyCAT 的 bin 目录添加到系统环境变量中。

  • 编辑 /etc/profile 文件,添加以下内容:

    export MYCAT_HOME=/usr/local/mycat
    export PATH=$PATH:$MYCAT_HOME/bin
    
  • 保存后,执行以下命令使环境变量生效:

    source /etc/profile
    
步骤 4:配置 MyCAT
  • 进入 MyCAT 的配置目录:

    cd /usr/local/mycat/conf
    
  • 主要配置文件:

    • server.xml:配置 MyCAT 的用户名、密码、端口等信息。
    • schema.xml:定义逻辑库、逻辑表、数据节点和分片规则。
  • 根据实际需求,编辑 server.xmlschema.xml 文件。具体配置将在后续章节详细说明。

步骤 5:启动 MyCAT
  • 在 MyCAT 的 bin 目录下,执行启动命令:

    mycat start
    
  • 如果提示权限不足,可能需要赋予执行权限:

    chmod +x /opt/mycat/bin/*
    
  • 启动成功后,可以查看 MyCAT 的运行状态:

    mycat status
    
步骤 6:验证安装
  • 使用 MySQL 客户端连接 MyCAT,默认端口为 8066

    mysql -h 127.0.0.1 -P 8066 -u mycat -p
    
  • 输入密码后,如果成功连接,说明 MyCAT 安装和启动成功。

2. 使用 Docker 安装 MyCAT

由于docker镜像仓库中mycat的镜像比较少而且比较陈旧,所以我们可以自行构建 MyCAT 的 Docker 镜像。如果需要自定义 MyCAT 镜像,可以编写一个 Dockerfile。

  • 创建一个目录(例如 mycat-docker),在其中创建 Dockerfile 文件:

    FROM openjdk:8-jre-slim
    
    # 使用 LABEL 替代 MAINTAINER 
    LABEL maintainer="YourName <your.email@example.com>"
    
    # 设置工作目录 
    WORKDIR /usr/local
    
    # 下载并解压 MyCAT,直接解压到 /usr/local/mycat 
    RUN apt-get update && \ 
        apt-get install -y wget procps && \ 
        wget https://github.com/MyCATApache/Mycat-Server/releases/download/Mycat-server-1.6.7.4-release/Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz && \           mkdir -p /usr/local/mycat && \ 
        tar -zxvf Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz -C /usr/local/mycat --strip-components=1 && \ 
        rm Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz && \ 
        apt-get remove -y wget && apt-get autoremove -y && apt-get clean
    
    # 配置环境变量
    ENV MYCAT_HOME=/usr/local/mycat
    ENV PATH=$PATH:$MYCAT_HOME/bin
    
    # 暴露端口
    EXPOSE 8066 9066
    
    # 启动 MyCAT
    CMD ["mycat", "console"]
    
  • mycat-docker 目录下构建镜像:

    docker build -t mycat-custom .
    

    构建完成后我们通过命令docker images 可以看到已经成功构建自定义mycat镜像
    在这里插入图片描述

步骤 1:运行 MyCAT 容器
  1. 使用自定义镜像启动容器(初次运行,无需挂载本地配置文件):

    docker run -d --name mycat -p 8066:8066 -p 9066:9066 mycat-custom
    
  2. 复制容器中的配置文件到本地目录

    • 创建本地配置目录(例如 /path/to/mycat/config/):

      mkdir -p /path/to/mycat/config/
      
    • 复制容器中的 server.xmlrule.xmlschema.xml 文件到本地目录:

      docker cp mycat:/usr/local/mycat/conf/server.xml /path/to/mycat/config/server.xml
      docker cp mycat:/usr/local/mycat/conf/schema.xml /path/to/mycat/config/schema.xml
      docker cp mycat:/usr/local/mycat/conf/rule.xml /path/to/mycat/config/rule.xml
      
  3. 删除当前容器

    docker rm -f mycat
    
  4. 重新运行容器并挂载本地配置文件

    docker run -d --name mycat -p 8066:8066 -p 9066:9066 \
        -v /path/to/mycat/config/server.xml:/usr/local/mycat/conf/server.xml \
        -v /path/to/mycat/config/schema.xml:/usr/local/mycat/conf/schema.xml \
         -v /path/to/mycat/config/rule.xml:/usr/local/mycat/conf/rule.xml \
        mycat-custom
    
  5. 参数说明

    • -d:后台运行容器。
    • --name mycat:将容器命名为 mycat
    • -p 8066:8066-p 9066:9066:将容器的端口映射到主机,MyCAT 服务和管理端口。
    • -v:将主机的配置文件(server.xmlschema.xml)挂载到容器中,便于本地直接修改配置。
步骤 2:验证安装
  • 查看容器运行状态:

    docker ps
    

    在这里插入图片描述

  • 查看 MyCAT 日志,确保其正常启动:

    docker logs mycat
    
  • 使用 MySQL 客户端连接 MyCAT:

    mysql -h 127.0.0.1 -P 8066 -u root -p
    
  • 输入密码后,如果成功连接,说明 MyCAT 容器运行正常。

    我用工具连接 如图所示 我们成功连接

    在这里插入图片描述

成功启动 MyCAT 容器后,可以对配置文件 server.xmlschema.xml 进行优化和整理,尤其是注释部分。MyCAT 的默认配置文件通常包含大量的注释,可能让配置项显得杂乱,不便于查看和维护。因此,可以删除多余的注释,并仅保留关键配置说明,使文件更加简洁清晰。

优化 server.xml

server.xml 用于配置 MyCAT 的账户信息、端口等基本参数。在优化时,可以:

  1. 删除多余的注释,仅保留关键注释。
  2. 分组整理配置项,使文件结构清晰。
优化示例
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">

<mycat:server xmlns:mycat="http://io.mycat/">

	<!-- 系统配置 -->
	<system>
		<property name="nonePasswordLogin">0</property> <!-- 0: 需要密码登录;1: 不需要密码登录 -->
		<property name="ignoreUnknownCommand">0</property> <!-- 0: 报错,1: 忽略未知命令 -->
		<property name="useHandshakeV10">1</property> <!-- 使用 MySQL Handshake v10,兼容部分客户端 -->
		<property name="removeGraveAccent">1</property> <!-- 移除 SQL 中的 `符号 -->
		<property name="useSqlStat">0</property> <!-- 是否开启 SQL 统计 -->
		<property name="useGlobleTableCheck">0</property> <!-- 全局表一致性检测 -->
		<property name="sqlExecuteTimeout">300</property> <!-- SQL 执行超时时间(秒)-->
		<property name="sequnceHandlerType">1</property> <!-- 序列处理器类型 -->
		<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|)|\s)*)+</property> <!-- 序列匹配模式 -->
		<property name="subqueryRelationshipCheck">false</property> <!-- 子查询中关联字段检查 -->
		<property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property> <!-- 序列处理器类 -->
		<property name="handleDistributedTransactions">0</property> <!-- 处理分布式事务 -->
		<property name="useOffHeapForMerge">0</property> <!-- 是否开启 Off-Heap 内存处理 -->
		<property name="memoryPageSize">64k</property> <!-- 内存页大小 -->
		<property name="spillsFileBufferSize">1k</property> <!-- 溢出文件缓冲大小 -->
		<property name="systemReserveMemorySize">384m</property> <!-- 系统保留内存大小 -->
		<property name="useZKSwitch">false</property> <!-- 是否使用 Zookeeper 进行主从切换 -->
		<property name="strictTxIsolation">false</property> <!-- 严格事务隔离 -->
		<property name="parallExecute">0</property> <!-- 是否开启并行执行 -->
	</system>

	<!-- 用户配置 -->
	<user name="root" defaultAccount="true">
		<property name="password">123456</property>
		<property name="schemas">TESTDB</property>
		<property name="defaultSchema">TESTDB</property>
	</user>

	<user name="user">
		<property name="password">user</property>
		<property name="schemas">TESTDB</property>
		<property name="readOnly">true</property> <!-- 只读用户 -->
		<property name="defaultSchema">TESTDB</property>
	</user>

</mycat:server>
优化后的文件说明
  • 系统配置:将 system 部分的配置项按用途分组,删除了冗余注释,仅保留了每个配置项的核心作用说明,便于快速理解每项配置。
  • 用户配置:将 user 部分配置简化,仅保留用户 rootuser 的基本信息及访问权限。

这样精简后的配置文件可以更加清晰地展示关键配置项,便于我们后面维护和修改。

优化 schema.xml

schema.xml 是 MyCAT 配置的核心文件,包含逻辑库、逻辑表、数据节点和物理数据库的映射关系。优化时可以:

  1. 简化结构:将不必要的默认注释删除,保留最常用的配置示例。
  2. 逻辑分层:将逻辑库、逻辑表、数据节点、物理库的配置分为几大部分,以便快速查找。
  3. 清晰的关键注释:对核心配置项添加简洁注释,说明每个部分的用途。
优化示例
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">

<mycat:schema xmlns:mycat="http://io.mycat/">

    <!-- 定义逻辑库 -->
    <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
        
        <!-- 逻辑表配置,使用自动分片策略 -->
        <table name="travelrecord,address" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" splitTableNames="true"/>
    </schema>

    <!-- 数据节点配置,指向不同的物理数据库 -->
    <dataNode name="dn1" dataHost="localhost1" database="db1"/>
    <dataNode name="dn2" dataHost="localhost1" database="db2"/>
    <dataNode name="dn3" dataHost="localhost1" database="db3"/>

    <!-- 物理数据库配置 -->
    <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
        <heartbeat>select user()</heartbeat>
        
        <!-- 主写库配置 -->
        <writeHost host="hostM1" url="localhost:3306" user="root" password="123456"/>
    </dataHost>

</mycat:schema>
优化思路总结
  • 保留简洁注释:每个部分保留一个简洁的注释,描述其功能。
  • 分块组织结构:逻辑表、数据节点、物理数据库分块组织,便于阅读。
  • 删除冗余注释:去掉默认注释内容,仅保留必需的注释,使文件更加整洁。

这样经过优化的配置文件结构清晰,便于快速定位和维护,配置项一目了然,可以大大提高文件的可读性和操作性。

优化 rule.xml

rule.xml 是 MyCAT 中用于定义分片规则和算法的配置文件,包含了分片规则、分片字段、分片算法以及分片函数等信息。在优化时,可以:

  1. 简化结构:删除不必要的默认注释,保留常用的分片规则和算法配置示例。
  2. 逻辑分层:将分片规则和分片函数配置分为几大部分,便于快速查找和理解每一部分的用途。
  3. 清晰的关键注释:对每个配置项添加简洁注释,说明分片规则和算法的用途和参数含义。
优化示例
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">

<mycat:rule xmlns:mycat="http://io.mycat/">

    <!-- 分片规则定义 -->
    
    <!-- rule1: 使用 func1 算法,通过 id 字段进行分片 -->
    <tableRule name="rule1">
        <rule>
            <columns>id</columns>
            <algorithm>func1</algorithm>
        </rule>
    </tableRule>

    <!-- sharding-by-date: 基于 createTime 日期字段进行分片,使用 partbyday 算法 -->
    <tableRule name="sharding-by-date">
        <rule>
            <columns>createTime</columns>
            <algorithm>partbyday</algorithm>
        </rule>
    </tableRule>

    <!-- auto-sharding-long: 使用 rang-long 算法,基于 id 字段自动分片 -->
    <tableRule name="auto-sharding-long">
        <rule>
            <columns>id</columns>
            <algorithm>rang-long</algorithm>
        </rule>
    </tableRule>

    <!-- mod-long: 基于 id 字段,使用 mod-long 算法进行取模分片 -->
    <tableRule name="mod-long">
        <rule>
            <columns>id</columns>
            <algorithm>mod-long</algorithm>
        </rule>
    </tableRule>

    <!-- sharding-by-murmur: 基于 id 字段,使用 murmur 哈希算法分片 -->
    <tableRule name="sharding-by-murmur">
        <rule>
            <columns>id</columns>
            <algorithm>murmur</algorithm>
        </rule>
    </tableRule>

    <!-- 分片函数定义 -->
    
    <!-- murmur 算法,基于 MurmurHash 哈希算法 -->
    <function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash">
        <property name="seed">0</property> <!-- Hash 种子值,默认为 0 -->
        <property name="count">2</property> <!-- 数据库节点数量 -->
        <property name="virtualBucketTimes">160</property> <!-- 虚拟节点数量 -->
    </function>

    <!-- mod-long 算法,使用取模分片 -->
    <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
        <property name="count">3</property> <!-- 分片数量 -->
    </function>

    <!-- rang-long 算法,基于长整型范围自动分片 -->
    <function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
        <property name="mapFile">autopartition-long.txt</property> <!-- 外部映射文件 -->
    </function>

    <!-- partbyday 算法,基于日期分片 -->
    <function name="partbyday" class="io.mycat.route.function.PartitionByDate">
        <property name="dateFormat">yyyy-MM-dd</property> <!-- 日期格式 -->
        <property name="sBeginDate">2014-01-01</property> <!-- 开始日期 -->
        <property name="sEndDate">2014-01-31</property> <!-- 结束日期 -->
        <property name="sPartionDay">10</property> <!-- 每 10 天分一个片 -->
    </function>

</mycat:rule>
优化思路总结
  • 保留简洁注释:每个分片规则和函数定义均附有简洁注释,便于快速理解其功能和参数配置。
  • 分块组织结构:分片规则和分片函数各自分块组织,方便查找和维护。
  • 删除冗余注释:去掉不必要的注释,仅保留核心配置内容,使文件简洁清晰。

MyCAT 实现分库分表

在本示例中,我们将详细讲解如何使用 MyCAT 进行分库分表,采用 order_id 字段为分片依据。我们会先介绍 MyCAT 的分片规则配置,然后配置 MyCAT,最后通过 Hyperf 框架进行分库分表的测试。

1. 分库分表规则介绍

我们选用 MyCAT 的 mod-long 规则,以 order_id 为分片字段,通过取模的方式决定数据在不同物理数据库节点上的存储位置。这个规则的基本思路是:

  • 分片字段:选取订单表中的 order_id
  • 分片算法:使用 mod-long 算法,根据 order_id % 节点数量 的结果分配数据,确保数据均匀分布在各节点。

该方法适合订单 ID 具有连续增长特性的场景,在满足读写性能需求的同时,实现了数据的水平扩展。

2. 配置 MyCAT 规则和数据节点

配置 schema.xml

schema.xml 是 MyCAT 的核心配置文件,包含逻辑库、逻辑表和数据节点。我们将设置一个逻辑库 order_db,并指定数据节点 dn1dn2,它们指向两个物理数据库 db1db2

<mycat:schema xmlns:mycat="http://io.mycat/">

    <!-- 定义逻辑库 -->
    <schema name="order_db" checkSQLschema="false" sqlMaxLimit="100">
        <table name="order_table" primaryKey="order_id" dataNode="dn1,dn2" rule="mod-long"/>
    </schema>

    <!-- 数据节点配置 -->
    <dataNode name="dn1" dataHost="localhost1" database="db1"/>
    <dataNode name="dn2" dataHost="localhost2" database="db2"/>

<!-- 物理数据库配置 -->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
    <heartbeat>select user()</heartbeat>

    <!-- 主写库配置 -->
    <writeHost host="hostM1" url="172.18.0.5:3306" user="root" password="123456"/>
</dataHost>

<dataHost name="localhost2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
    <heartbeat>select user()</heartbeat>

    <!-- 主写库配置 -->
    <writeHost host="hostM1" url="172.18.0.5:3306" user="root" password="123456"/>
</dataHost>
</mycat:schema>

上面的例子是按照规则进行对表分库,其实这个配置很灵活的的 下面我举几个例子我们来看下

仅分表的配置

假设只在一个数据库 db1 中进行分表,不涉及多个数据库:

<schema name="order_db" checkSQLschema="false" sqlMaxLimit="100">
    <!-- 配置一个逻辑表,并指定多个物理表,使用分表规则 -->
    <table name="order_table" primaryKey="order_id" dataNode="dn1" rule="mod-long" actualTables="order_table_00,order_table_01"/>
</schema>

<dataNode name="dn1" dataHost="localhost1" database="db1"/>

<dataHost name="localhost1" maxCon="1000" minCon="10" dbType="mysql" dbDriver="native">
    <writeHost host="hostM1" url="localhost:3306" user="root" password="password"/>
</dataHost>

在此配置中:

  • actualTables:指定在物理库 db1 中的真实表 order_table_00order_table_01
  • rule=“mod-long” :表示根据 order_id 对数据进行分表存储。

MyCAT 将根据分片规则将数据插入到 order_table_00order_table_01,实现仅在单库中分表的效果。

分库 + 分表的配置

如果既要分库又要分表,可以同时指定多个 dataNodeactualTables。假设有两个数据库 db1db2,并且每个数据库中有两个分表:

<schema name="order_db" checkSQLschema="false" sqlMaxLimit="100">
    <!-- 配置分库和分表的逻辑表 -->
    <table name="order_table" primaryKey="order_id" dataNode="dn1,dn2" rule="mod-long" actualTables="order_table_00,order_table_01"/>
</schema>

<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost2" database="db2" />

<dataHost name="localhost1" maxCon="1000" minCon="10" dbType="mysql" dbDriver="native">
    <writeHost host="hostM1" url="localhost:3306" user="root" password="password"/>
</dataHost>

<dataHost name="localhost2" maxCon="1000" minCon="10" dbType="mysql" dbDriver="native">
    <writeHost host="hostM2" url="localhost:3307" user="root" password="password"/>
</dataHost>

在此配置中:

  • dataNode:指向两个数据节点 dn1dn2,分别对应数据库 db1db2
  • actualTables:指定每个数据库中使用的真实分表。MyCAT 会在 db1 中创建 order_table_00order_table_01,在 db2 中也同样创建 order_table_00order_table_01
  • rule=“mod-long” :MyCAT 将根据 order_id 分片规则选择合适的 dataNodeactualTables,实现分库分表。

总结

  • 仅分表:使用一个 dataNode 指向单一物理数据库,同时在 actualTables 中指定多个分表。

  • 分库+分表:配置多个 dataNode,每个数据节点对应一个物理库,同时在每个库中分表。


配置 rule.xml

rule.xml 文件中定义 mod-long 分片规则,通过 order_id 取模的方式实现分片。该规则将 order_id 的值与数据库节点数量进行取模,确保数据均匀分布到两个节点上。

<tableRule name="mod-long">
    <rule>
        <columns>order_id</columns>
        <algorithm>mod-long</algorithm>
    </rule>
</tableRule>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <property name="count">2</property> <!-- 数据节点数量 -->
</function>

修改 server.xml

server.xml 中,将默认逻辑库名称配置为 order_db,以与 schema.xml 的配置一致:

<mycat:server xmlns:mycat="http://io.mycat/">

    <system>
        <property name="nonePasswordLogin">0</property>
        <property name="sqlExecuteTimeout">300</property>
    </system>

    <!-- 用户配置 -->
    <user name="root" defaultAccount="true">
        <property name="password">123456</property>
        <property name="schemas">order_db</property>
    </user>

</mycat:server>

3. 重启 MyCAT 服务并验证配置

确保以上配置文件保存正确,重启 MyCAT 容器以应用配置:

docker restart mycat

4. 在 MySQL 中创建物理表结构

首先,在所有目标物理数据库中创建 order_table 表结构,以确保每个分片的数据库都包含相同的表结构。

在数据库 db1db2 中执行以下 SQL:

CREATE TABLE order_table (
    order_id INT PRIMARY KEY,
    user_id INT,
    amount DECIMAL(10, 2),
    order_date DATETIME
);

5.连接到 MyCAT 容器并插入测试数据

可以直接通过主机端口 8066 连接到 MyCAT 代理库:

mysql -h 127.0.0.1 -P 8066 -u root -p

在这里插入图片描述

连接后,选择逻辑库 order_db,并插入测试数据:

USE order_db;

INSERT INTO order_table (order_id, user_id, amount, order_date) VALUES (1, 101, 50.00, '2024-01-01 10:00:00'); 
INSERT INTO order_table (order_id, user_id, amount, order_date) VALUES (2, 102, 75.00, '2024-01-01 11:00:00'); 
INSERT INTO order_table (order_id, user_id, amount, order_date) VALUES (3, 103, 100.00, '2024-01-01 12:00:00');

在这里插入图片描述

6.验证分片结果

我们通过命令连接到mysql容器

mysql -h 127.0.0.1 -u root -p

连接后分别选择db1库和db2库查询 order_table 表中的数据,验证数据是否按 order_id 的取模结果分片:

在这里插入图片描述

在这里插入图片描述

通过查询结果,可以看到数据按照 order_id 的取模分布在 db1db2 中,例如 order_id 为 1 和 3 的记录可能在 db1,而 order_id 为 2 的记录可能在 db2

7. 验证查询聚合操作

通过 MyCAT 进行查询聚合操作,验证 MyCAT 是否能够正确处理跨分片的查询。例如:

SELECT * FROM order_table WHERE user_id = 101;
SELECT * FROM order_table ORDER BY order_date DESC;

在这里插入图片描述

MyCAT 将自动根据分片规则查询各个物理节点的数据,并将结果聚合返回给客户端。


MyCAT 分片策略的实现和选择

MyCAT 提供了多种分片策略,以满足不同业务场景的数据分片需求。主要的分片策略包括:

  1. Hash 分片:基于哈希值进行分片,将数据均匀分配到多个分片上,适合社交网络中用户 ID 的分片。
  2. 范围分片:根据字段的值范围划分数据,适合按时间或其他连续字段进行分片的场景,如订单系统中的订单 ID 或日期。
  3. 自定义分片:可根据特定需求实现自定义逻辑分片,适合复杂的分片需求或需要基于业务逻辑动态调整分片规则的场景。

下面我会详细介绍每种分片策略的实现方式、选择建议,以及相应的配置示例。


1. Hash 分片

适用场景:适用于数据访问模式较为均匀的场景,如社交网络中的用户 ID 分片,能有效避免单一节点的热点问题。

实现原理:对分片字段(如 user_id)的哈希值进行取模运算,根据结果决定数据的存储位置。

配置示例

rule.xml 中定义 Hash 分片规则,并在 schema.xml 中引用此规则。

rule.xml

<tableRule name="hash-sharding">
    <rule>
        <columns>user_id</columns>
        <algorithm>mod-long</algorithm>
    </rule>
</tableRule>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <property name="count">4</property> <!-- 数据节点数量 -->
</function>

schema.xml

<schema name="social_network" checkSQLschema="false" sqlMaxLimit="100">
    <table name="user_table" primaryKey="user_id" dataNode="dn1,dn2,dn3,dn4" rule="hash-sharding"/>
</schema>

解释:这里 mod-long 函数通过 user_id % 4 的值来确定数据的分片节点,可以有效均匀分布用户数据。


2. 范围分片

适用场景:适用于数据具有连续字段或时间字段的场景,如订单系统中的订单 ID 或订单日期,通常适用于按时间分片的场景。

实现原理:根据字段值的范围将数据划分到不同的分片中。可以定义固定的范围,例如日期范围或 ID 范围。

配置示例

rule.xml 中定义范围分片规则,并在 schema.xml 中引用。

rule.xml

<tableRule name="range-sharding">
    <rule>
        <columns>order_date</columns>
        <algorithm>rang-long</algorithm>
    </rule>
</tableRule>

<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
    <property name="mapFile">autopartition-long.txt</property> <!-- 映射文件路径,指定范围 -->
</function>

autopartition-long.txt 映射文件示例:

20240101=dn1
20240201=dn2
20240301=dn3

schema.xml

<schema name="order_db" checkSQLschema="false" sqlMaxLimit="100">
    <table name="order_table" primaryKey="order_id" dataNode="dn1,dn2,dn3" rule="range-sharding"/>
</schema>

解释rang-long 函数使用外部映射文件 autopartition-long.txt,将不同日期范围的订单数据存储到不同的节点。例如,20240101 之前的数据存储到 dn1,而 20240201 之前的数据存储到 dn2


3. 自定义分片

适用场景:适用于需要灵活分片的业务场景,比如按地域或根据业务逻辑动态调整分片规则的场景。

实现原理:自定义分片规则可以灵活实现任意复杂的分片逻辑。

配置示例

rule.xml 中定义自定义分片规则,并在 schema.xml 中引用。

rule.xml

<tableRule name="custom-sharding">
    <rule>
        <columns>custom_field</columns>
        <algorithm>custom-algorithm</algorithm>
    </rule>
</tableRule>

<function name="custom-algorithm" class="io.mycat.route.function.CustomPartition">
    <property name="param1">value1</property> <!-- 可根据需要定义参数 -->
</function>

schema.xml

<schema name="custom_db" checkSQLschema="false" sqlMaxLimit="100">
    <table name="custom_table" primaryKey="custom_id" dataNode="dn1,dn2" rule="custom-sharding"/>
</schema>

解释:自定义分片函数 custom-algorithm 可以根据 custom_field 的值决定数据存储位置。此类分片可以通过自定义逻辑支持复杂的业务需求。

分片策略选择建议
  • Hash 分片:适用于均匀分布的数据访问模式,避免热点。例如,社交网络中用户 ID 的分片。
  • 范围分片:适合有时间字段或连续数据字段的场景,适合订单系统或日志系统。
  • 自定义分片:适合特殊的业务逻辑需求,如按地域、按业务类型进行分片。

通过正确选择分片策略,我们在业务实现中可以提高数据的分布均衡性和访问效率,满足不同业务需求。


MyCAT 读写分离

MyCAT 支持读写分离功能,帮助减轻主库(写库)压力,将读请求分发到从库(读库),从而提升系统的读性能。读写分离适合读多写少的场景,能有效提高数据库的并发处理能力。

读写分离原理

MyCAT 实现读写分离的关键在于将写请求路由到主库(writeHost),将读请求路由到从库(readHost)。通过 MyCAT 的配置,我们可以设置一个主库用于写操作,并指定多个从库用于读操作,从而实现主从同步与负载均衡。

配置步骤

下面步骤不再做实际演示 有需要的可以根据步骤测试一下 我自己配置过了。

以下是通过 MyCAT 配置读写分离的示例,包括如何配置主库和从库的 writeHostreadHost

1. 配置 schema.xml 文件

schema.xml 中,我们定义逻辑库,并指定数据节点:

<schema name="app_db" checkSQLschema="false" sqlMaxLimit="100">
    <table name="user_table" primaryKey="user_id" dataNode="dn1" rule="mod-long"/>
</schema>

<dataNode name="dn1" dataHost="localhost" database="app_db"/>
2. 配置 server.xml 中的读写分离

server.xml 中,指定用户的默认逻辑库:

<mycat:server xmlns:mycat="http://io.mycat/">

    <system>
        <property name="nonePasswordLogin">0</property>
        <property name="sqlExecuteTimeout">300</property>
    </system>

    <!-- 用户配置 -->
    <user name="root" defaultAccount="true">
        <property name="password">123456</property>
        <property name="schemas">app_db</property>
    </user>

</mycat:server>
3. 配置读写分离的主从节点

schema.xml 中设置主从库配置,将主库设为 writeHost,从库设为 readHost

<dataHost name="localhost" maxCon="1000" minCon="10" balance="1" dbType="mysql" dbDriver="native" writeType="0" switchType="1" slaveThreshold="100">
    <heartbeat>select user()</heartbeat>
    
    <!-- 主库配置 -->
    <writeHost host="hostM1" url="mysql-master:3306" user="root" password="password"/>
    
    <!-- 从库配置 -->
    <readHost host="hostS1" url="mysql-slave1:3306" user="root" password="password"/>
    <readHost host="hostS2" url="mysql-slave2:3306" user="root" password="password"/>
</dataHost>
  • writeHost:用于写操作的主库(mysql-master)。
  • readHost:用于读操作的从库(mysql-slave1mysql-slave2)。

解释

  • balance="1":配置读操作的负载均衡。
  • switchType="1":配置主从切换策略。
  • slaveThreshold="100":超过此阈值的查询将被分发到从库。
4. 启动 MyCAT 并测试读写分离

重启 MyCAT 容器,使配置生效:

docker restart mycat
5. 验证读写分离

连接 MyCAT 代理端口(假设 8066),并测试写操作和读操作:

mysql -h 127.0.0.1 -P 8066 -u root -p

执行写操作

USE app_db;
INSERT INTO user_table (user_id, name) VALUES (1, 'Alice');

写操作会自动路由到主库 mysql-master,可直接在主库上验证数据插入情况。

执行读操作

SELECT * FROM user_table WHERE user_id = 1;

读操作会自动路由到从库 mysql-slave1mysql-slave2,可以连接到从库验证数据是否同步。

6. 示例场景

在这种配置下,MyCAT 可以将读操作均衡分发到多个从库,有效提升了读性能;同时,所有写操作集中在主库,保证数据的一致性。适合需要高频读操作的应用场景,如内容查询类应用或数据分析场景。

通过 MyCAT 的读写分离功能,能够轻松配置主从库架构,实现读写分离,有效提高数据库的读性能并减轻主库的负载。


MyCAT 跨库事务支持

MyCAT 提供了跨库事务支持,通过协调多个物理数据库的事务操作,尽量保持数据的一致性。由于跨库事务的复杂性,MyCAT 的实现基于“事务隔离”与“分布式事务”两种模式,以在性能与一致性之间取得平衡。

MyCAT 的跨库事务实现原理
  1. 事务隔离:MyCAT 支持在逻辑库级别进行事务隔离处理。当同一个事务操作多个逻辑表时,MyCAT 会尽量在提交前保持操作隔离。但由于实际的数据分布在不同物理库中,存在一致性风险。
  2. 分布式事务:MyCAT 支持分布式事务(两阶段提交)模式,即在执行跨库写操作时,MyCAT 先发送 PREPARE 阶段的命令,在确保所有分片数据库准备就绪后,再发送 COMMIT 阶段指令,保证跨库操作的原子性。此过程会对性能产生影响,适合一致性要求高的场景。
跨库事务示例

假设一个交易系统中,用户余额和订单记录分布在不同的物理库中。可以通过以下 SQL 语句在 MyCAT 逻辑库中执行跨库事务:

START TRANSACTION;

-- 更新用户余额(位于数据库1)
UPDATE user_balance SET balance = balance - 100 WHERE user_id = 1;

-- 插入订单记录(位于数据库2)
INSERT INTO order_record (user_id, amount, status) VALUES (1, 100, 'paid');

COMMIT;

MyCAT 将以上操作分发到不同的物理库,在 COMMIT 前确保两步操作都成功执行。若任何一步失败,MyCAT 会执行回滚操作以保证数据一致性。

性能影响和一致性权衡

跨库事务对性能的影响较大,因为它涉及多个数据库的协调,特别是在分布式环境中,网络延迟、锁冲突和数据库负载都会影响事务执行效率。在业务需求中,需要考虑以下权衡:

  • 一致性优先:适用于金融等高一致性要求场景,采用分布式事务尽可能确保数据一致性。
  • 性能优先:在一些允许一定程度数据不一致的场景下(如分析、日志等),可以选择使用事务隔离模式以提高并发和响应速度。

MyCAT 性能优化

MyCAT 的性能优化涉及多个方面,从连接池、内存配置到缓存功能的使用。优化可以显著提升 MyCAT 的吞吐量和响应速度。

常见优化策略
  1. 连接池配置:MyCAT 提供了连接池功能,通过合理配置连接池的 maxConminCon 参数,可以减少数据库连接的创建和销毁时间,提升请求处理效率。
  2. 内存参数调整:MyCAT 支持内存管理,通过调整 memoryPageSizesystemReserveMemorySize 等参数,可以为 MyCAT 的合并、排序等操作分配更多内存,提高性能。
  3. 缓存配置:MyCAT 可以配置查询缓存,将频繁访问的数据缓存在内存中,减少对数据库的直接访问。在配置缓存时,需要根据业务特征设置合适的缓存失效时间,以避免数据不一致的问题。
MyCAT 性能监控方法
  1. 日志分析:MyCAT 的日志文件中记录了 SQL 执行情况、慢查询、错误等信息。通过分析日志,可以发现执行耗时的 SQL 语句,定位性能瓶颈。
  2. 状态查看:MyCAT 支持使用 SHOW @@VERSIONSHOW @@CONNECTIONS 等命令查看系统状态,通过分析连接数、并发数等数据判断系统负载情况,调整配置。
配置示例:连接池和缓存

以下为优化 MyCAT 性能的示例配置,包括连接池和缓存的设置:

server.xml(连接池配置):

<dataHost name="localhost" maxCon="500" minCon="50" dbType="mysql" dbDriver="native">
    <heartbeat>select 1</heartbeat>
    <writeHost host="master" url="localhost:3306" user="root" password="password"/>
</dataHost>

schema.xml(缓存配置示例):

<schema name="app_db" cachePolicy="LRU" cacheRefreshPeriod="600" maxCacheSize="10000">
    <table name="cached_table" primaryKey="id" dataNode="dn1" rule="mod-long"/>
</schema>
  • maxCon/minCon:设置连接池的最大和最小连接数,以应对高并发请求。
  • cachePolicy:使用 LRU 缓存策略,cacheRefreshPeriod 设置缓存刷新周期(秒),maxCacheSize 限制缓存大小。

通过合理配置 MyCAT 的连接池和缓存等参数,并结合日志和状态监控,能有效提升系统的并发性能和响应速度。

最后

MyCAT 作为一款开源的分布式数据库中间件,提供了分库分表、读写分离和跨库事务等多种功能,适用于数据量大、并发量高的场景。通过 MyCAT,我们可以轻松地将数据分布到多个物理节点,提升系统的读写性能和扩展性。然而,使用 MyCAT 也面临一些挑战,比如分布式事务带来的复杂性、配置和维护的成本等。

使用 MyCAT 的优势与挑战

优势

  • 水平扩展:分库分表可以将数据分散到多个节点,支持业务数据的线性增长。
  • 读写分离:将读操作分发到从库,有效减轻主库的负载,适合读多写少的场景。
  • 跨库事务:支持分布式事务,适合对数据一致性要求较高的业务。

挑战

  • 复杂性:分库分表和读写分离的配置较为复杂,需要仔细设计分片策略。
  • 性能影响:分布式事务会影响性能,可能导致一定的延迟,适合有一致性需求的场景。
  • 维护成本:需要定期优化和维护 MyCAT 配置,监控系统状态,及时调整。
MyCAT 的最佳实践
  1. 合理的分片设计:分片策略的选择应根据业务需求,比如订单系统使用范围分片,用户系统使用哈希分片,确保数据分布均匀,避免热点问题。
  2. 定期维护和优化:定期检查 MyCAT 配置文件,根据实际的负载情况调整连接池、缓存和内存配置,以应对业务的变化。
  3. 监控和日志分析:使用 MyCAT 的状态监控命令和日志分析,关注慢查询和错误信息,定位性能瓶颈,及时调整配置。
  4. 缓存与组合中间件:在需要高性能读取的场景中,可以引入 Redis 等缓存中间件,与 MyCAT 配合使用。通过将热点数据缓存到 Redis 中,减少数据库压力,提升系统响应速度。
可能的改进建议
  • 缓存中间件引入:在需要极高读性能的场景,可以结合 Redis、Memcached 等缓存中间件,避免过多直接访问数据库,提升读性能。
  • 结合分布式文件存储:在需要处理大量文件的场景,可结合分布式文件存储系统(如 HDFS)与 MyCAT 分布式数据库系统,实现数据存储和处理的协同。
  • 服务化改进:在微服务架构中,配合 MyCAT 将数据库服务化,按业务拆分,进一步细化分片,提高系统可扩展性。

通过合理设计和优化,MyCAT 可以极大地提升分布式数据库的性能和可扩展性,但也需定期维护和监控,确保系统稳定高效运行。结合缓存、分布式存储等技术,可以进一步提高 MyCAT 系统的性能和灵活性。


http://www.kler.cn/a/392422.html

相关文章:

  • Python 连接 Redis 进行增删改查(CRUD)操作
  • 【STM32F1】——无线收发模块RF200与串口通信
  • 《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址
  • GxtWaitCursor:Qt下基于RAII的鼠标等待光标类
  • 如何用C#和Aspose.PDF实现PDF转Word工具
  • WebRTC API分析
  • windows中docker安装redis和redisinsight记录
  • 什么时候用 Tailwind 什么时候用 CSS
  • 第 8 章 - Go语言 数组与切片
  • 大语言模型安全威胁
  • [Docker#3] LXC | 详解安装docker | docker的架构与生态
  • Three.js 纹理与网格的优化
  • Linux将二进制软件包编译成rpm软件包教程详解
  • unity3d————四元数的计算
  • 【每日推荐】使用 Ollama 平台上的 Llama 3.2-vision 模型进行视频目标检测
  • 【PGCCC】Postgresql Toast 原理
  • Maven 构建项目
  • qt creator开发一个Linux 下运行的无界面的程序
  • 力扣617:合并二叉树
  • Cesium基础-(Entity)-(label )
  • 如何快速开发一套MES系统?
  • SpringBoot(八)使用AES库对字符串进行加密解密
  • 每日计划-1109
  • Git - 日志
  • Python+Appium编写脚本
  • TCP(上):成熟可靠的传输层协议