淘宝返利app多数据源设计:基于MyCat的分库分表与读写分离

淘宝返利app多数据源设计:基于MyCat的分库分表与读写分离

大家好,我是省赚客APP研发者阿宝!

在省赚客这类高并发返利应用中,用户订单、佣金记录、推广关系等核心数据量增长迅猛。单库单表在日均百万级订单下已出现性能瓶颈。为保障系统稳定性和扩展性,我们采用MyCat作为中间件,实现分库分表 + 读写分离的多数据源架构,支撑当前千万级用户规模下的高效数据访问。

整体架构设计

系统部署4个MySQL实例(2主2从),通过MyCat逻辑库rebate_db对外提供统一入口:

  • 写操作 :路由至主库(dn1_master, dn2_master);
  • 读操作 :负载均衡至从库(dn1_slave, dn2_slave);
  • 分片规则:按用户ID哈希分片,确保同一用户数据落在同一库。

MyCat配置文件关键部分如下:

xml 复制代码
<!-- schema.xml -->
<schema name="rebate_db" checkSQLschema="false" sqlMaxLimit="100">
    <table name="user_order" dataNode="dn1,dn2" rule="user_id_mod"/>
    <table name="commission_record" dataNode="dn1,dn2" rule="user_id_mod"/>
</schema>

<dataNode name="dn1" dataHost="host1" database="rebate_01"/>
<dataNode name="dn2" dataHost="host2" database="rebate_02"/>

<dataHost name="host1" maxCon="1000" minCon="10" balance="1">
    <writeHost host="master" url="192.168.1.10:3306" user="root" password="xxx">
        <readHost host="slave" url="192.168.1.11:3306" user="root" password="xxx"/>
    </writeHost>
</dataHost>

分片规则定义在rule.xml

xml 复制代码
<tableRule name="user_id_mod">
    <rule>
        <columns>user_id</columns>
        <algorithm>mod-long</algorithm>
    </rule>
</tableRule>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <property name="count">2</property>
</function>

Java应用连接配置

Spring Boot项目通过JDBC URL直连MyCat(端口8066),无需感知底层分片:

yaml 复制代码
# application.yml
spring:
  datasource:
    url: jdbc:mysql://mycat.juwatech.cn:8066/rebate_db?useUnicode=true&characterEncoding=utf8
    username: app_user
    password: secure_password
    driver-class-name: com.mysql.cj.jdbc.Driver

业务代码保持透明,例如订单创建:

java 复制代码
package juwatech.cn.order.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;

@Mapper
public interface UserOrderMapper {

    @Insert("INSERT INTO user_order (user_id, order_no, amount, status) VALUES (#{userId}, #{orderNo}, #{amount}, #{status})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(UserOrder order);
}

MyCat根据user_id自动路由到dn1dn2,开发者无需编写分库逻辑。

强制走主库场景处理

对于"下单后立即查询"等强一致性场景,需强制读主库。MyCat支持注解式路由:

java 复制代码
package juwatech.cn.order.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderQueryService {

    @Transactional(readOnly = false)
    public UserOrder createAndQuery(String userId, String orderNo) {
        // 插入订单(写主库)
        orderMapper.insert(new UserOrder(userId, orderNo, 100L, "PENDING"));

        // 强制读主库:MyCat注解 /*balance*/ 或 /*master*/
        return orderMapper.selectByOrderNoWithHint(orderNo);
    }
}

对应Mapper方法:

java 复制代码
@Select("/*master*/ SELECT * FROM user_order WHERE order_no = #{orderNo}")
UserOrder selectByOrderNoWithHint(String orderNo);

该注释将绕过读写分离,直接查询主库,避免主从延迟导致查不到数据。

全局自增ID生成

由于分库后MySQL自增主键不再全局唯一,我们采用Snowflake算法生成分布式ID:

java 复制代码
package juwatech.cn.common.id;

@Component
public class SnowflakeIdGenerator {

    private final Snowflake snowflake = IdUtil.createSnowflake(1, 1);

    public long nextId() {
        return snowflake.nextId();
    }
}

在实体插入前赋值:

java 复制代码
UserOrder order = new UserOrder();
order.setId(idGenerator.nextId());
order.setUserId(userId);
orderMapper.insert(order);

跨分片查询优化

对于运营后台的全局统计需求(如"昨日总佣金"),避免全表扫描。我们采用以下策略:

  1. 冗余汇总表 :每日凌晨通过Flink聚合写入daily_commission_summary(不分片);
  2. 异步导出:大数据量查询走离线数仓,不压在线库。

示例汇总任务:

java 复制代码
@Scheduled(cron = "0 0 2 * * ?")
public void aggregateDailyCommission() {
    List<CommissionSummary> summaries = commissionMapper.sumByDate(LocalDate.now().minusDays(1));
    summaryMapper.batchInsert(summaries); // 写入非分片表
}

监控与故障切换

MyCat提供JDBC连接池监控和心跳检测。当主库宕机时,自动切换至备用主库(需配合MHA或Orchestrator)。我们还通过Prometheus采集MyCat指标:

yaml 复制代码
# mycat_exporter配置
metrics_path: /actuator/prometheus
static_configs:
  - targets: ['mycat-metrics:9104']

关键告警项包括:连接池耗尽、SQL执行超时、主从延迟>5s。

上线后,系统写入TPS提升3倍,复杂查询响应时间从2s降至200ms以内,有效支撑大促期间流量洪峰。

本文著作权归聚娃科技省赚客app开发者团队,转载请注明出处!

相关推荐
sheji341613 小时前
【开题答辩全过程】以 基于JSP的汽车租赁管理系统为例,包含答辩的问题和答案
java·开发语言·汽车
wen__xvn13 小时前
C++ 中 std::set 的用法
java·c++·c#
多米Domi01113 小时前
0x3f 第21天 三更java进阶1-35 hot100普通数组
java·python·算法·leetcode·动态规划
步步为营DotNet13 小时前
深深度探索.NET 中HttpClient的复用策略:提升性能与稳定性的关键度解析.NET 中IServiceCollection:构建可扩展服务体系的关键
java·网络·.net
牛马11114 小时前
WidgetsFlutterBinding.ensureInitialized()在 Flutter Web 端启动流程的影响
java·前端·flutter
宠友信息14 小时前
面向多端部署的社区平台技术方案:uniapp 与java微服务架构的工程化实践
java·微服务·微信·架构·uni-app·springboot
YanDDDeat14 小时前
Prometheus + Grafana 搭建应用监控体系
java·后端·eureka·grafana·prometheus
诗酒当趁年华14 小时前
Token刷新策略
java