分库分表的 9种分布式主键ID 生成方案,挺全乎的

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 学习就要举一反九

《sharding-jdbc 分库分表的 4种分片策略》 中我们介绍了 sharding-jdbc 4种分片策略的使用场景,可以满足基础的分片功能开发,这篇我们来看看分库分表后,应该如何为分片表生成全局唯一的主键 ID

引入任何一种技术都是存在风险的,分库分表当然也不例外,除非库、表数据量持续增加,大到一定程度,以至于现有高可用架构已无法支撑,否则不建议大家做分库分表,因为做了数据分片后,你会发现自己踏上了一段踩坑之路,而分布式主键 ID 就是遇到的第一个坑。

不同数据节点间生成全局唯一主键是个棘手的问题,一张逻辑表 t_order 拆分成多个真实表 t_order_n,然后被分散到不同分片库 db_0db_1... ,各真实表的自增键由于无法互相感知从而会产生重复主键,此时数据库本身的自增主键,就无法满足分库分表对主键全局唯一的要求。

 db_0--
    |-- t_order_0
    |-- t_order_1
    |-- t_order_2
 db_1--
    |-- t_order_0
    |-- t_order_1
    |-- t_order_2

尽管我们可以通过严格约束,各个分片表自增主键的 初始值步长 的方式来解决 ID 重复的问题,但这样会让运维成本陡增,而且可扩展性极差,一旦要扩容分片表数量,原表数据变动比较大,所以这种方式不太可取。

 步长 step = 分表张数

 db_0--
    |-- t_order_0  ID: 0、6、12、18...
    |-- t_order_1  ID: 1、7、13、19...
    |-- t_order_2  ID: 2、8、14、20...
 db_1--
    |-- t_order_0  ID: 3、9、15、21...
    |-- t_order_1  ID: 4、10、16、22...
    |-- t_order_2  ID: 5、11、17、23...

目前已经有了许多第三放解决方案可以完美解决这个问题,比如基于 UUIDSNOWFLAKE算法 、segment号段,使用特定算法生成不重复键,或者直接引用主键生成服务,像美团(Leaf)和 滴滴(TinyId)等。

sharding-jdbc 内置了两种分布式主键生成方案,UUIDSNOWFLAKE,不仅如此它还抽离出分布式主键生成器的接口,以便于开发者实现自定义的主键生成器,后续我们会在自定义的生成器中接入 滴滴(TinyId)的主键生成服务。

前边介绍过在 sharding-jdbc 中要想为某个字段自动生成主键 ID,只需要在 application.properties 文件中做如下配置:

# 主键字段
spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
# 主键ID 生成方案
spring.shardingsphere.sharding.tables.t_order.key-generator.type=UUID
# 工作机器 id
spring.shardingsphere.sharding.tables.t_order.key-generator.props.worker.id=123

key-generator.column 表示主键字段,key-generator.type 为主键 ID 生成方案(内置或自定义的),key-generator.props.worker.id 为机器ID,在主键生成方案设为 SNOWFLAKE 时机器ID 会参与位运算。

在使用 sharding-jdbc 分布式主键时需要注意两点:

  • 一旦 insert 插入操作的实体对象中主键字段已经赋值,那么即使配置了主键生成方案也会失效,最后SQL 执行的数据会以赋的值为准。
  • 不要给主键字段设置自增属性,否则主键ID 会以默认的 SNOWFLAKE 方式生成。比如:用 mybatis plus@TableId 注解给字段 order_id 设置了自增主键,那么此时配置哪种方案,总是按雪花算法生成。

下面我们从源码上分析下 sharding-jdbc 内置主键生成方案 UUIDSNOWFLAKE 是怎么实现的。

UUID

打开 UUID 类型的主键生成实现类 UUIDShardingKeyGenerator 的源码发现,它的生成规则只有 UUID.randomUUID() 这么一行代码,额~ 心中默默来了一句卧槽

UUID 虽然可以做到全局唯一性,但还是不推荐使用它作为主键,因为我们的实际业务中不管是 user_id 还是 order_id 主键多为整型,而 UUID 生成的是个 32 位的字符串。

它的存储以及查询对 MySQL 的性能消耗较大,而且 MySQL 官方也明确建议,主键要尽量越短越好,作为数据库主键 UUID 的无序性还会导致数据位置频繁变动,严重影响性能。

public final class UUIDShardingKeyGenerator implements ShardingKeyGenerator {
    private Properties properties = new Properties();

    public UUIDShardingKeyGenerator() {
    }

    public String getType() {
        return "UUID";
    }

    public synchronized Comparable<?> generateKey() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public Properties getProperties() {
        return this.properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}

SNOWFLAKE

SNOWFLAKE(雪花算法)是默认使用的主键生成方案,生成一个 64bit的长整型(Long)数据。

sharding-jdbc 中雪花算法生成的主键主要由 4部分组成,1bit符号位、41bit时间戳位、10bit工作进程位以及 12bit 序列号位。

雪花算法ID组成

符号位(1bit位)

Java 中 Long 型的最高位是符号位,正数是0,负数是1,一般生成ID都为正数,所以默认为0

时间戳位(41bit)

41位的时间戳可以容纳的毫秒数是 2 的 41次幂,而一年的总毫秒数为 1000L * 60 * 60 * 24 * 365,计算使用时间大概是69年,额~,我有生之间算是够用了。

Math.pow(2, 41) / (365 * 24 * 60 * 60 * 1000L) = = 69年 

工作进程位(10bit)

表示一个唯一的工作进程id,默认值为 0,可通过 key-generator.props.worker.id 属性设置。

spring.shardingsphere.sharding.tables.t_order.key-generator.props.worker.id=0000

序列号位(12bit)

同一毫秒内生成不同的ID。

时钟回拨

了解了雪花算法的主键 ID 组成后不难发现,这是一种严重依赖于服务器时间的算法,而依赖服务器时间的就会遇到一个棘手的问题:时钟回拨

为什么会出现时钟回拨呢?

互联网中有一种网络时间协议 ntp 全称 (Network Time Protocol) ,专门用来同步、校准网络中各个计算机的时间。

这就是为什么,我们的手机现在不用手动校对时间,可每个人的手机时间还都是一样的。

我们的硬件时钟可能会因为各种原因变得不准( 快了慢了 ),此时就需要 ntp 服务来做时间校准,做校准的时候就会发生服务器时钟的 跳跃 或者 回拨 的问题。

雪花算法如何解决时钟回拨

服务器时钟回拨会导致产生重复的 ID,SNOWFLAKE 方案中对原有雪花算法做了改进,增加了一个最大容忍的时钟回拨毫秒数。

如果时钟回拨的时间超过最大容忍的毫秒数阈值,则程序直接报错;如果在可容忍的范围内,默认分布式主键生成器,会等待时钟同步到最后一次主键生成的时间后再继续工作。

最大容忍的时钟回拨毫秒数,默认值为 0,可通过属性 max.tolerate.time.difference.milliseconds 设置。

# 最大容忍的时钟回拨毫秒数
spring.shardingsphere.sharding.tables.t_order.key-generator.max.tolerate.time.difference.milliseconds=5

下面是看下它的源码实现类 SnowflakeShardingKeyGenerator,核心流程大概如下:

最后一次生成主键的时间 lastMilliseconds 与 当前时间currentMilliseconds 做比较,如果 lastMilliseconds > currentMilliseconds则意味着时钟回调了。

那么接着判断两个时间的差值(timeDifferenceMilliseconds)是否在设置的最大容忍时间阈值 max.tolerate.time.difference.milliseconds内,在阈值内则线程休眠差值时间 Thread.sleep(timeDifferenceMilliseconds),否则大于差值直接报异常。

 
/**
 * @author xiaofu
 */
public final class SnowflakeShardingKeyGenerator implements ShardingKeyGenerator{
    @Getter
    @Setter
    private Properties properties = new Properties();
    
    public String getType() {
        return "SNOWFLAKE";
    }
    
    public synchronized Comparable<?> generateKey() {
        /**
         * 当前系统时间毫秒数 
         */ 
        long currentMilliseconds = timeService.getCurrentMillis();
        /**
         * 判断是否需要等待容忍时间差,如果需要,则等待时间差过去,然后再获取当前系统时间 
         */ 
        if (waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) {
            currentMilliseconds = timeService.getCurrentMillis();
        }
        /**
         * 如果最后一次毫秒与 当前系统时间毫秒相同,即还在同一毫秒内 
         */
        if (lastMilliseconds == currentMilliseconds) {
            /**
             * &位与运算符:两个数都转为二进制,如果相对应位都是1,则结果为1,否则为0
             * 当序列为4095时,4095+1后的新序列与掩码进行位与运算结果是0
             * 当序列为其他值时,位与运算结果都不会是0
             * 即本毫秒的序列已经用到最大值4096,此时要取下一个毫秒时间值
             */
            if (0L == (sequence = (sequence + 1) & SEQUENCE_MASK)) {
                currentMilliseconds = waitUntilNextTime(currentMilliseconds);
            }
        } else {
            /**
             * 上一毫秒已经过去,把序列值重置为1 
             */
            vibrateSequenceOffset();
            sequence = sequenceOffset;
        }
        lastMilliseconds = currentMilliseconds;
        
        /**
         * XX......XX XX000000 00000000 00000000    时间差 XX
         *          XXXXXX XXXX0000 00000000    机器ID XX
         *                     XXXX XXXXXXXX    序列号 XX
         *  三部分进行|位或运算:如果相对应位都是0,则结果为0,否则为1
         */
        return ((currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) | sequence;
    }
    
    /**
     * 判断是否需要等待容忍时间差
     */
    @SneakyThrows
    private boolean waitTolerateTimeDifferenceIfNeed(final long currentMilliseconds) {
        /**
         * 如果获取ID时的最后一次时间毫秒数小于等于当前系统时间毫秒数,属于正常情况,则不需要等待 
         */
        if (lastMilliseconds <= currentMilliseconds) {
            return false;
        }
        /**
         * ===>时钟回拨的情况(生成序列的时间大于当前系统的时间),需要等待时间差 
         */
        /**
         * 获取ID时的最后一次毫秒数减去当前系统时间毫秒数的时间差 
         */
        long timeDifferenceMilliseconds = lastMilliseconds - currentMilliseconds;
        /**
         * 时间差小于最大容忍时间差,即当前还在时钟回拨的时间差之内 
         */
        Preconditions.checkState(timeDifferenceMilliseconds < getMaxTolerateTimeDifferenceMilliseconds(), 
                "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastMilliseconds, currentMilliseconds);
        /**
         * 线程休眠时间差 
         */
        Thread.sleep(timeDifferenceMilliseconds);
        return true;
    }
    
    // 配置的机器ID
    private long getWorkerId() {
        long result = Long.valueOf(properties.getProperty("worker.id", String.valueOf(WORKER_ID)));
        Preconditions.checkArgument(result >= 0L && result < WORKER_ID_MAX_VALUE);
        return result;
    }
    
    private int getMaxTolerateTimeDifferenceMilliseconds() {
        return Integer.valueOf(properties.getProperty("max.tolerate.time.difference.milliseconds", String.valueOf(MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS)));
    }
    
    private long waitUntilNextTime(final long lastTime) {
        long result = timeService.getCurrentMillis();
        while (result <= lastTime) {
            result = timeService.getCurrentMillis();
        }
        return result;
    }
}

但从 SNOWFLAKE 方案生成的主键ID 来看,order_id 它是一个18位的长整型数字,是不是发现它太长了,想要 MySQL 那种从 0 递增的自增主键该怎么实现呢?别急,后边已经会给出了解决办法!

SNOWFLAKE 主键ID

自定义

sharding-jdbc 利用 SPI 全称( Service Provider Interface) 机制拓展主键生成规则,这是一种服务发现机制,通过扫描项目路径 META-INF/services 下的文件,并自动加载文件里所定义的类。

实现自定义主键生成器其实比较简单,只有两步。

第一步,实现 ShardingKeyGenerator 接口,并重写其内部方法,其中 getType() 方法为自定义的主键生产方案类型、generateKey() 方法则是具体生成主键的规则。

下面代码中用 AtomicInteger 来模拟实现一个有序自增的 ID 生成。

/**
 * @Author: xiaofu
 * @Description: 自定义主键生成器
 */
@Component
public class MyShardingKeyGenerator implements ShardingKeyGenerator {


    private final AtomicInteger count = new AtomicInteger();

    /**
     * 自定义的生成方案类型
     */
    @Override
    public String getType() {
        return "XXX";
    }

    /**
     * 核心方法-生成主键ID
     */
    @Override
    public Comparable<?> generateKey() {
        return count.incrementAndGet();
    }

    @Override
    public Properties getProperties() {
        return null;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

第二步,由于是利用 SPI 机制实现功能拓展,我们要在 META-INF/services 文件中配置自定义的主键生成器类路劲。

com.xiaofu.sharding.key.MyShardingKeyGenerator

自定义主键 SPI 配置

上面这些弄完我们测试一下,配置定义好的主键生成类型 XXX,并插入几条数据看看效果。

spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type=XXX

通过控制台的SQL 解析日志发现,order_id 字段已按照有序自增的方式插入记录,说明配置的没问题。

举一反九

既然可以自定义生成方案,那么实现分布式主键的思路就很多了,又想到之前我写的这篇 《9种 分布式ID生成方案》,发现可以完美兼容,这里挑选其中的 滴滴(Tinyid)来实践一下,由于它是个单独的分布式ID生成服务,所以要先搭建环境了。

Tinyid 的服务提供HttpTinyid-client 两种接入方式,下边使用 Tinyid-client 方式快速使用,更多的细节到这篇文章里看吧,实在是介绍过太多次了。

Tinyid 服务搭建

先拉源代码 https://github.com/didi/tinyid.git

由于是基于号段模式实现的分布式ID,所以依赖于数据库,要创建相应的表 tiny_id_infotiny_id_token 并插入默认数据。


CREATE TABLE `tiny_id_info` (
    `id` BIGINT (20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
    `biz_type` VARCHAR (63) NOT NULL DEFAULT '' COMMENT '业务类型,唯一',
    `begin_id` BIGINT (20) NOT NULL DEFAULT '0' COMMENT '开始id,仅记录初始值,无其他含义。初始化时begin_id和max_id应相同',
    `max_id` BIGINT (20) NOT NULL DEFAULT '0' COMMENT '当前最大id',
    `step` INT (11) DEFAULT '0' COMMENT '步长',
    `delta` INT (11) NOT NULL DEFAULT '1' COMMENT '每次id增量',
    `remainder` INT (11) NOT NULL DEFAULT '0' COMMENT '余数',
    `create_time` TIMESTAMP NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '创建时间',
    `update_time` TIMESTAMP NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '更新时间',
    `version` BIGINT (20) NOT NULL DEFAULT '0' COMMENT '版本号',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uniq_biz_type` (`biz_type`)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8 COMMENT 'id信息表';

CREATE TABLE `tiny_id_token` (
    `id` INT (11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增id',
    `token` VARCHAR (255) NOT NULL DEFAULT '' COMMENT 'token',
    `biz_type` VARCHAR (63) NOT NULL DEFAULT '' COMMENT '此token可访问的业务类型标识',
    `remark` VARCHAR (255) NOT NULL DEFAULT '' COMMENT '备注',
    `create_time` TIMESTAMP NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '创建时间',
    `update_time` TIMESTAMP NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '更新时间',
    PRIMARY KEY (`id`)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8 COMMENT 'token信息表';

INSERT INTO `tiny_id_token` (`id`, `token`, `biz_type`, `remark`, `create_time`, `update_time`) VALUES ('1', '0f673adf80504e2eaa552f5d791b644c', 'order', '1', '2017-12-14 16:36:46', '2017-12-14 16:36:48');

INSERT INTO `tiny_id_info` (`id`, `biz_type`, `begin_id`, `max_id`, `step`, `delta`, `remainder`, `create_time`, `update_time`, `version`) VALUES ('1', 'order', '1', '1', '100000', '1', '0', '2018-07-21 23:52:58', '2018-07-22 23:19:27', '1');

并在 Tinyid 服务中配置上边表所在数据源信息

datasource.tinyid.primary.url=jdbc:mysql://47.93.6.e:3306/ds-0?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
datasource.tinyid.primary.username=root
datasource.tinyid.primary.password=root

最后项目 maven install ,右键 TinyIdServerApplication 启动服务, Tinyid 分布式ID生成服务就搭建完毕了。

自定义 Tinyid 主键类型

Tinyid 服务搭建完下边在项目中引入它,新建个 tinyid_client.properties 文件其中添加 tinyid.servertinyid.token 属性,token 为之前 SQL 预先插入的用户数据。

# tinyid 分布式ID
# 服务地址
tinyid.server=127.0.0.1:9999
# 业务token
tinyid.token=0f673adf80504e2eaa552f5d791b644c

代码中获取 ID更简单,只需一行代码,业务类型 order 是之前 SQ L 预先插入的数据。

Long id = TinyId.nextId("order");

我们开始自定义 Tinyid 主键生成类型的实现类 TinyIdShardingKeyGenerator

/**
 * @Author: xiaofu
 * @Description: 自定义主键生成器
 */
@Component
public class TinyIdShardingKeyGenerator implements ShardingKeyGenerator {
    
    /**
     * 自定义的生成方案类型
     */
    @Override
    public String getType() {
        return "tinyid";
    }

    /**
     * 核心方法-生成主键ID
     */
    @Override
    public Comparable<?> generateKey() {
        
        Long id = TinyId.nextId("order");
        
        return id;
    }

    @Override
    public Properties getProperties() {
        return null;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

并在配置文件中启用 Tinyid 主键生成类型,到此配置完毕,赶紧测试一下。

# 主键字段
spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
# 主键ID 生成方案
spring.shardingsphere.sharding.tables.t_order.key-generator.type=tinyid

测试 Tinyid 主键

向数据库插入订单记录测试发现,主键ID字段 order_id 已经为趋势递增的了, Tinyid 服务成功接入,完美!

在这里插入图片描述

总结

后续的八种生成方式大家参考 《9种 分布式ID生成方案》 按需接入吧,整体比较简单这里就不依次实现了。

案例 GitHub 地址:https://github.com/chengxy-nds/Springboot-Notebook/tree/master/springboot-sharding-jdbc

整理了几百本各类技术电子书,送给小伙伴们。关注公号回复 666 自行领取。和一些小伙伴们建了一个技术交流群,一起探讨技术、分享技术资料,旨在共同学习进步,如果感兴趣就加入我们吧!

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
12天前
|
消息中间件 SQL 中间件
大厂都在用的分布式事务方案,Seata+RocketMQ带你打破10万QPS瓶颈
分布式事务涉及跨多个数据库或服务的操作,确保数据一致性。本地事务通过数据库直接支持ACID特性,而分布式事务则需解决跨服务协调难、高并发压力及性能与一致性权衡等问题。常见的解决方案包括两阶段提交(2PC)、Seata提供的AT和TCC模式、以及基于消息队列的最终一致性方案。这些方法各有优劣,适用于不同业务场景,选择合适的方案需综合考虑业务需求、系统规模和技术团队能力。
98 7
|
1月前
|
算法 关系型数据库 MySQL
分布式唯一ID生成:深入理解Snowflake算法在Go中的实现
在分布式系统中,确保每个节点生成的 ID 唯一且高效至关重要。Snowflake 算法由 Twitter 开发,通过 64 位 long 型数字生成全局唯一 ID,包括 1 位标识位、41 位时间戳、10 位机器 ID 和 12 位序列号。该算法具备全局唯一性、递增性、高可用性和高性能,适用于高并发场景,如电商促销时的大量订单生成。本文介绍了使用 Go 语言的 `bwmarrin/snowflake` 和 `sony/sonyflake` 库实现 Snowflake 算法的方法。
42 1
分布式唯一ID生成:深入理解Snowflake算法在Go中的实现
|
20天前
|
NoSQL Java 数据处理
基于Redis海量数据场景分布式ID架构实践
【11月更文挑战第30天】在现代分布式系统中,生成全局唯一的ID是一个常见且重要的需求。在微服务架构中,各个服务可能需要生成唯一标识符,如用户ID、订单ID等。传统的自增ID已经无法满足在集群环境下保持唯一性的要求,而分布式ID解决方案能够确保即使在多个实例间也能生成全局唯一的标识符。本文将深入探讨如何利用Redis实现分布式ID生成,并通过Java语言展示多个示例,同时分析每个实践方案的优缺点。
38 8
|
17天前
|
缓存 NoSQL Java
Spring Boot中的分布式缓存方案
Spring Boot提供了简便的方式来集成和使用分布式缓存。通过Redis和Memcached等缓存方案,可以显著提升应用的性能和扩展性。合理配置和优化缓存策略,可以有效避免常见的缓存问题,保证系统的稳定性和高效运行。
35 3
|
16天前
|
消息中间件 架构师 数据库
本地消息表事务:10Wqps 高并发分布式事务的 终极方案,大厂架构师的 必备方案
45岁资深架构师尼恩分享了一篇关于分布式事务的文章,详细解析了如何在10Wqps高并发场景下实现分布式事务。文章从传统单体架构到微服务架构下分布式事务的需求背景出发,介绍了Seata这一开源分布式事务解决方案及其AT和TCC两种模式。随后,文章深入探讨了经典ebay本地消息表方案,以及如何使用RocketMQ消息队列替代数据库表来提高性能和可靠性。尼恩还分享了如何结合延迟消息进行事务数据的定时对账,确保最终一致性。最后,尼恩强调了高端面试中需要准备“高大上”的答案,并提供了多个技术领域的深度学习资料,帮助读者提升技术水平,顺利通过面试。
本地消息表事务:10Wqps 高并发分布式事务的 终极方案,大厂架构师的 必备方案
|
22天前
|
NoSQL 安全 PHP
hyperf-wise-locksmith,一个高效的PHP分布式锁方案
`hyperf-wise-locksmith` 是 Hyperf 框架下的互斥锁库,支持文件锁、分布式锁、红锁及协程锁,有效防止分布式环境下的竞争条件。本文介绍了其安装、特性和应用场景,如在线支付系统的余额扣减,确保操作的原子性。
24 4
|
1月前
|
NoSQL 算法 关系型数据库
分布式 ID 详解 ( 5大分布式 ID 生成方案 )
本文详解分布式全局唯一ID及其5种实现方案,关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
分布式 ID 详解 ( 5大分布式 ID 生成方案 )
|
2月前
|
存储 缓存 NoSQL
分布式架构下 Session 共享的方案
【10月更文挑战第15天】在实际应用中,需要根据具体的业务需求、系统架构和性能要求等因素,选择合适的 Session 共享方案。同时,还需要不断地进行优化和调整,以确保系统的稳定性和可靠性。
|
2月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
16天前
|
存储 NoSQL Java
使用lock4j-redis-template-spring-boot-starter实现redis分布式锁
通过使用 `lock4j-redis-template-spring-boot-starter`,我们可以轻松实现 Redis 分布式锁,从而解决分布式系统中多个实例并发访问共享资源的问题。合理配置和使用分布式锁,可以有效提高系统的稳定性和数据的一致性。希望本文对你在实际项目中使用 Redis 分布式锁有所帮助。
47 5