Mysql分表加雪花算法实战

DBC 415 0
温馨提示

老规矩,上来直接代码,会用就行!思想你也不会来我这里学[aru_42]
我们使用的是Mybatic-plus来快速实现!

依赖

点击查看完整内容

ProductOrderMapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.xdclass.model.ProductOrderDO;



public interface ProductOrderMapper extends BaseMapper<ProductOrderDO> {
}

ProductOrderDO

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.Date;


@Data
@TableName("product_order")
@EqualsAndHashCode(callSuper = false)
public class ProductOrderDO {

    //@TableId(value = "id",type = IdType.ASSIGN_ID)
    private Long id;

    private String outTradeNo;

    private String state;

    private Date createTime;

    private Double payAmount;

    private String nickname;

    private Long userId;

}
温馨提示

这里说明一下,我们利用Mybatic-plus其实也可以生成雪花算法,但是这里我们还是不用它,我们用下面的

配置文件

spring.application.name=xdclass-sharding-jdbc
server.port=8080


# 打印执行的数据库以及语句
spring.shardingsphere.props.sql.show=true

# 数据源 db0
spring.shardingsphere.datasource.names=ds0,ds1

# 第一个数据库
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://8.142.19.202:3306/xdclass_shop_order_0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=你的密码


# 第二个数据库
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://8.142.19.202:3306/xdclass_shop_order_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=你的密码


#配置workId
spring.shardingsphere.sharding.tables.product_order.key-generator.props.worker.id=1


#id生成策略
spring.shardingsphere.sharding.tables.product_order.key-generator.column=id
spring.shardingsphere.sharding.tables.product_order.key-generator.type=SNOWFLAKE

# 指定product_order表的数据分布情况,配置数据节点,行表达式标识符使用 ${...} 或 $->{...},
# 但前者与 Spring 本身的文件占位符冲突,所以在 Spring 环境中建议使用 $->{...}
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds0.product_order_$->{0..1}

# 指定product_order表的分片策略,分片策略包括【分片键和分片算法】
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.algorithm-expression=product_order_$->{user_id % 2}

这里我们看看我们的简单数据库,其实就是两个库,然后每一个库有两个表。(其他复杂情况同理可得,全是注释了)

Mysql分表加雪花算法实战插图

单元测试一下

import lombok.extern.slf4j.Slf4j;
import net.xdclass.DemoApplication;
import net.xdclass.mapper.ProductOrderMapper;
import net.xdclass.model.ProductOrderDO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Date;
import java.util.UUID;



@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
@Slf4j
public class DbTest {

    @Autowired
    private ProductOrderMapper productOrderMapper;


    @Test
    public void testSaveProductOrder(){

        for(int i=0; i<10;i++){
            ProductOrderDO productOrderDO = new ProductOrderDO();
            productOrderDO.setCreateTime(new Date());
            productOrderDO.setNickname("小滴课堂i="+1);
            productOrderDO.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
            productOrderDO.setPayAmount(100.00);
            productOrderDO.setState("PAY");

            productOrderDO.setUserId(Long.valueOf(i+""));
            productOrderMapper.insert(productOrderDO);

        }

    }


}
温馨提示

好了 ,到了这里我们已经实现了简单的分表操作,还有简单的实现雪花算法!下面再补充一些知识,转载自小滴课堂——22年新版-架构师系列-新版ShardingJDBC分库分表mysql数据库实战

  • 单库下一般使用Mysql自增ID, 但是分库分表后,会造成不同分片上的数据表主键会重复。
  • 需求
    • 性能强劲
    • 全局唯一
    • 防止恶意用户规矩id的规则来获取数据

 

  • 业界常用ID解决方案
    • 数据库自增ID
      • 利用自增id, 设置不同的自增步长,auto_increment_offset、auto-increment-increment
      • DB1: 单数
        • 从1开始、每次加2
      • DB2: 偶数
        • 从2开始,每次加2
      • 缺点
        • 依靠数据库系统的功能实现,但是未来扩容麻烦
        • 主从切换时的不一致可能会导致重复发号
        • 性能瓶颈存在单台sql上

 

  • UUID
    • 性能非常高,没有网络消耗
    • 缺点
      • 无序的字符串,不具备趋势自增特性
      • UUID太长,不易于存储,浪费存储空间,很多场景不适用

     

  • Redis发号器
    • 利用Redis的INCR和INCRBY来实现,原子操作,线程安全,性能比Mysql强劲
    • 缺点
      • 需要占用网络资源,增加系统复杂度

     

  • Snowflake雪花算法
    • twitter 开源的分布式 ID 生成算法,代码实现简单、不占用宽带、数据迁移不受影响
    • 生成的 id 中包含有时间戳,所以生成的 id 按照时间递增
    • 部署了多台服务器,需要保证系统时间一样,机器编号不一样
    • 缺点
      • 依赖系统时钟(多台服务器时间一定要一样)
  • 什么是雪花算法Snowflake
    • twitter用scala语言编写的高效生成唯一ID的算法
    • 优点
      • 生成的ID不重复
      • 算法性能高
      • 基于时间戳,基本保证有序递增

       

  • 计算机的基础知识回顾
    • bit与byte
      • bit(位):电脑中存储的最小单位,可以存储二进制中的0或1
      • byte(字节):一个byte由8个bit组成
    • 常规64位系统里面java数据类型存储字节大小
      • int:4 个字节
      • short:2 个字节
      • long:8 个字节
      • byte:1 个字节
      • float:4 个字节
      • double:8 个字节
      • char:2 个字节

     

    • 科普:数据类型在不同位数机器的平台下长度不同(怼面试官的严谨性)
      • 16位平台 int 2个字节16位
      • 32位平台 int 4个字节32位
      • 64位平台 int 4个字节32位

       

  • 雪花算法生成的数字,long类,所以就是8个byte,64bit
    • 表示的值 -9223372036854775808(-2的63次方) ~ 9223372036854775807(2的63次方-1)
    • 生成的唯一值用于数据库主键,不能是负数,所以值为0~9223372036854775807(2的63次方-1)

分布式ID生成器Snowflake里面的坑你是否知道

  • 布式ID生成器需求
    • 性能强劲
    • 全局唯一不能重复
    • 防止恶意用户规矩id的规则来获取数据

 

  • 全局唯一不能重复-坑
    • 坑一
      • 分布式部署就需要分配不同的workId, 如果workId相同,可能会导致生成的id相同

     

    • 坑二:
      • 分布式情况下,需要保证各个系统时间一致,如果服务器的时钟回拨,就会导致生成的 id 重复
        • 啥时候会有系统回拨????
          • 小滴课堂-老王闲着,人工去生产环境做了系统时间调整,应该不会这么傻吧
          • 业务需求,代码里面做了系统时间同步
  • 配置实操
  • spring.shardingsphere.sharding.tables.product_order.key-generator.props.worker.id=1

方式一

订单id使用MybatisPlus的配置,ProductOrder类配置

@TableId(value = "id", type = IdType.ASSIGN_ID)
默认实现类为DefaultIdentifierGenerator雪花算法

方式二

使用Sharding-Jdbc配置文件,注释DO类里面的id分配策略

#id生成策略
spring.shardingsphere.sharding.tables.product_order.key-generator.column=id
spring.shardingsphere.sharding.tables.product_order.key-generator.type=SNOWFLAKE
  • 进阶:动态指定sharding jdbc 的雪花算法中的属性work.id属性
    • 使用sharding-jdbc中的使用IP后几位来做workId, 但在某些情况下会出现生成重复ID的情况
      • 解决办法时:在启动时给每个服务分配不同的workId, 引入redis/zk都行,缺点就是多了依赖

发表评论 取消回复
表情 图片 链接 代码

分享