跳转到内容

分库分表迭代过程

  1. sql 优化;
  2. 加本地缓存-ehcache;
  3. 加分布式缓存-redis;
  4. 加读写分离
  5. 加多数据源
  6. 加分库分表
  • gen-data : 主要用来生成大量数据。
  • sharding-datasource
    • TODO: 主要实验读写分离;没有完成主从复制的数据库实例的搭建。
  • sharding-redis
    • 主要用来验证使用 redis 缓存提高接口响应时间;
    • TODO: 需要完善 redis 的配置,主要是序列化的问题,如存到 redis 中的 k-v 出现乱码;
  • sharding-sphere-ds
    • 主要完成多库多表的生成过程。使用的是单元测试。
  • una-saas : 应该迁移到此项目中。

构造百万数据

  1. 几种不同的插入方式: https://mp.weixin.qq.com/s/Gqb7OXHsLqwSjfNfJiCesw?scene=1&click_id=6
    1. 存储过程
    2. 原生的 preparestatement
    3. batchinstert

sql 优化

  1. 观察下列三种情况下的查询时间以及执行计划的区别
    1. 不创建索引
    2. 创建索引
    3. 创建索引之后,但索引失效

ehcache 本地缓存优化

  1. 配置过程
  2. 相关注解
  3. 原理

redis 分布式缓存优化

  1. 如何选 key?过期时间?如何保证一致性问题?
  2. redis 的使用
    1. 配置 key 和 value 的序列化
    2. 配置连接池

多数据源

sql
create table if not exists order_info
(
    id                     bigint auto_increment,
    order_no               varchar(32)                             not null comment '订单号',
    order_amount           decimal(8, 2)                           not null comment '订单金额',
    merchant_id            bigint                                  not null comment '商户ID',
    user_id                bigint                                  not null comment '用户ID',
    order_freight          decimal(8, 2) default 0.00              not null comment '运费',
    order_status           tinyint       default 0                 not null comment '订单状态,10待付款,20待接单,30已接单,40配送中,50已完成,55部分退款,60全部退款,70取消订单',
    trans_time             timestamp     default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '交易时间',
    pay_status             tinyint       default 2                 not null comment '支付状态,1待支付,2支付成功,3支付失败',
    recharge_time          timestamp     default CURRENT_TIMESTAMP not null comment '支付完成时间',
    pay_amount             decimal(8, 2) default 0.00              not null comment '实际支付金额',
    pay_discount_amount    decimal(8, 2) default 0.00              not null comment '支付优惠金额',
    address_id             bigint                                  not null comment '收货地址ID',
    delivery_type          tinyint       default 2                 not null comment '配送方式,1自提。2配送',
    delivery_status        tinyint       default 0                 null comment '配送状态,0 配送中,2已送达,3待收货,4已送达',
    delivery_expect_time   timestamp                               null comment '配送预计送达时间',
    delivery_complete_time timestamp                               null comment '配送送达时间',
    delivery_amount        decimal(8, 2) default 0.00              not null comment '配送运费',
    coupon_id              bigint                                  null comment '优惠券id',
    cancel_time            timestamp                               null comment '订单取消时间',
    confirm_time           timestamp                               null comment '订单确认时间',
    remark                 varchar(512)                            null comment '订单备注留言',
    create_user            bigint                                  null comment '创建用户',
    update_user            bigint                                  null comment '更新用户',
    create_time            timestamp     default CURRENT_TIMESTAMP not null comment '创建时间',
    update_time            timestamp                               null on update CURRENT_TIMESTAMP comment '更新时间',
    delete_flag            tinyint       default 0                 not null comment '逻辑删除标记',
    primary key (id, order_no),
    constraint uinx_order_no
        unique (order_no)
)
    comment '订单表' charset = utf8mb4
                     row_format = DYNAMIC;

create table if not exists order_item_detail
(
    id                  bigint auto_increment
        primary key,
    order_no            varchar(32)                             not null comment '订单号',
    product_id          bigint                                  not null comment '商品ID',
    category_id         bigint                                  not null comment '商品分类ID',
    goods_num           int           default 1                 not null comment '商品购买数量',
    goods_price         decimal(8, 2) default 0.00              not null comment '商品单价',
    goods_amount        decimal(8, 2) default 0.00              not null comment '商品总价',
    product_name        varchar(64)                             null comment '商品名',
    discount_amount     decimal(8, 2) default 0.00              not null comment '商品优惠金额',
    discount_id         bigint                                  null comment '参与活动ID',
    product_picture_url varchar(128)                            null comment '商品图片',
    create_user         bigint                                  null comment '创建用户',
    update_user         bigint                                  null comment '更新用户',
    create_time         timestamp     default CURRENT_TIMESTAMP not null comment '创建时间',
    update_time         timestamp     default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
    delete_flag         tinyint       default 0                 not null comment '逻辑删除标记'
)
    comment '订单明细表' charset = utf8mb4
                         row_format = DYNAMIC;

create index idx_order_no
    on order_item_detail (order_no);

sharding-multi-ds

思路: 就是把订单信息按照用户 id,分散到不同的数据源中,比如把 userid%datasourcesize=0 的订单数据放到 datasource01 中;把=1 的放到 datasource02 中。。。

整体原理:

  1. 构造一个 线程本地内存 来保存配置文件中的三个数据源;
  2. 在业务方法中添加根据 userId 来确定具体某一个数据源的方法;
  3. 重写 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey 方法,改为从 第一步 构造的 线程本地内存 中获取;

AbstractRoutingDataSource 使用的是 模版方法 的设计模式。预留几个可以用来扩展的方法,框架操作时,可以直接操作 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#getConnection() ;

业务改造:修改: me.zeanzai.shardingmultids.repository.OrderInfoRepository

  1. 不要添加事务注解,添加事务注解之后,需要重新修改 me.zeanzai.shardingmultids.context.config.DataSourceRouter 类;添加事务注解的话,涉及到的改造数据源获取的过程就不一样了;
  2. 添加 DataSourceContextHolder.setDataSourceType(orderInfo.getUserId()); 来设置具体的数据源;

测试: http://127.0.0.1:8083/user/order/generateOrder

json
{
  "orderInfo": {
    "id": 2,
    "createTime": "",
    "createUser": 2,
    "updateTime": "",
    "updateUser": 0,
    "deleteFlag": 0,
    "orderNo": "",
    "orderAmount": 23.0,
    "orderFreight": 2.0,
    "orderStatus": 2,
    "transTime": "",
    "payStatus": 2,
    "merchantId": 22222,
    "userId": 3789696,
    "rechargeTime": "",
    "payAmount": 0.0,
    "payDiscountAmount": 0.0,
    "addressId": 232,
    "remark": "ttttt",
    "deliveryType": 0,
    "deliveryStatus": 0,
    "deliveryExpectTime": "",
    "deliveryCompleteTime": "",
    "deliveryAmount": 0.0,
    "buyerStatus": 0,
    "couponId": 0
  },
  "orderItemDetailList": [
    {
      "createTime": "",
      "createUser": 456,
      "updateTime": "",
      "updateUser": 0,
      "deleteFlag": 0,
      "orderNo": "",
      "productId": 465,
      "categoryId": 444,
      "goodsNum": 2.0,
      "goodsPrice": 2.0,
      "goodsAmount": 2.0,
      "discountAmount": 2.0,
      "discountId": 2
    },
    {
      "createTime": "",
      "createUser": 456,
      "updateTime": "",
      "updateUser": 0,
      "deleteFlag": 0,
      "orderNo": "",
      "productId": 465,
      "categoryId": 444,
      "goodsNum": 2.0,
      "goodsPrice": 2.0,
      "goodsAmount": 2.0,
      "discountAmount": 2.0,
      "discountId": 2
    },
    {
      "createTime": "",
      "createUser": 456,
      "updateTime": "",
      "updateUser": 0,
      "deleteFlag": 0,
      "orderNo": "",
      "productId": 465,
      "categoryId": 444,
      "goodsNum": 2.0,
      "goodsPrice": 2.0,
      "goodsAmount": 2.0,
      "discountAmount": 2.0,
      "discountId": 2
    },
    {
      "createTime": "",
      "createUser": 456,
      "updateTime": "",
      "updateUser": 0,
      "deleteFlag": 0,
      "orderNo": "",
      "productId": 465,
      "categoryId": 444,
      "goodsNum": 2.0,
      "goodsPrice": 2.0,
      "goodsAmount": 2.0,
      "discountAmount": 2.0,
      "discountId": 2
    }
  ]
}

修改上面 json 中的 userid,然后运行,会发现不同的 userid 会选择不同的 datasource,查看数据库,订单信息及明细信息正常。

本方案出现的问题:

  1. 涉及到框架中数据源的改造;并且如果涉及到事务,还需要改造框架中的事务处理的过程;并且,如果订单信息和订单明细信息不在同一个数据源里面,可能还涉及到分布式事务,框架改造的工作量更大;

多租户

模块名称:una-saas

读写分离

分库分表

make it come true