【踩坑记录】Sharding-JDBC(3.0.0)之分布式主键冲突

目录

背景

问题排查

解决

生成workId

配置workId


背景

因为业务数量较大,单表影响查询性能,采用了单库分表解决。引入了sharding-jdbc插件进行分表下的数据库操作。生产环境都是分布式部署服务的,有两台机器。最近有并发量高的业务场景,偶尔会出现生成主键重复的问题 Duplicate entry '******************' for key 'PRIMARY'。

问题排查

主键是sharding-jdbc生成的,使用默认的雪花算法,是可以保证分布式主键唯一的。所以就很奇怪为什么主键会重复,又仔细的查看了官方文档,发现了问题。由于是分布式部署,没有指定不同进程的workId(默认是0),导致两台机器的workId相同,致使10bit工作进程位相同,所以两个写入请求同时打到两台机器上时,就可能会出现主键冲突。

下面简单解释一下:

符号位固定是0

时间戳位:生产服务器的时间是同步过的,所以同一时间写入数据,时间戳位是相同的

工作进程位:默认是0,分布式部署的话,不同进程要自己标记workId来实现区分,由于之前没有区分,导致工位进程位也相同

序列号位:序列号位是在同一进程中,保证id顺序的。有可能两个进程的请求的序列号是相同的,这时就会出现两台机器生成的id完全相同。

解决

定位了原因,之后就是给两台机器指定不同的workId就好。考虑到配置的通用性,使用hostname做区分。

生成workId

public class SnowflakeIdWorker {
    
    public static Long getWorkId() {
        String hostAddress = System.getenv("HOSTNAME");
        log.info("============= hostAddress: {} =============", hostAddress);
        int[] ints = org.apache.commons.lang3.StringUtils.toCodePoints(hostAddress);
        int sums = 0;
        int[] var3 = ints;
        int var4 = ints.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            int b = var3[var5];
            sums += b;
        }

        long workId = (long)(sums % 32);
        log.info("============== workId:{} =============", workId);
        return workId;
    }
}

配置workId

import com.yst.b2b.wallet.utils.SnowflakeIdWorker;
import io.shardingsphere.core.keygen.DefaultKeyGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

/**
 * sharding-jdbc 使用雪花算法生成主键时,workId配置
 *
 * @yx8102 2020/4/24
 */
@Configuration
@Slf4j
public class WorkIdConfig {

    static {

        long workId = 0;
        try {
            workId = SnowflakeIdWorker.getWorkId();
        } catch (Exception e) {
            log.error("生成workId发生异常.", e);
        }

        DefaultKeyGenerator.setWorkerId(workId);
    }
}

 

后续:其他生成workId的方法

【死磕Sharding-jdbc】---分布式ID

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章