Redis解决不同业务单号自增的问题 保证连续性

引言

在很多业务场景中,我们经常会遇到需要生成不同业务的唯一单号的情况。而在分布式系统中,要保证这些单号的连续性和唯一性并不是一件简单的事情。本文将介绍如何利用 Redis 来解决不同业务单号自增的问题,保证连续性。

问题描述

在一个分布式系统中,我们有多个业务需要生成唯一单号。传统的方法是使用数据库的自增主键来生成单号,但是这样做存在以下问题:

  1. 单点故障:如果数据库服务器出现故障,会导致单号的生成停止。
  2. 性能瓶颈:数据库的自增主键需要加锁来保证唯一性,会对性能造成一定的影响。
  3. 单调递增:数据库的自增主键生成的单号是单调递增的,不适合作为业务单号使用,容易暴露业务规模。

为了解决以上问题,可以利用 Redis 的原子操作和分布式锁来生成不同业务的连续自增单号。

解决方案

Redis String 类型

首先,我们可以使用 Redis 的 String 类型来存储当前业务的单号。我们可以将每个业务对应的单号保存在一个 String 类型的键中,键的名称可以使用业务的标识符。

SET order:current 1000

上面的代码表示将 order 业务的当前单号设置为 1000。

Redis INCR 命令

Redis 提供了 INCR 命令,可以原子地对一个键进行自增操作。我们可以利用这个命令来生成连续的单号。

INCR order:current

上面的代码表示将 order 业务的当前单号加一。

分布式锁

为了在分布式系统中保证单号的唯一性,我们需要引入分布式锁机制。Redis 提供了 SETNX 命令,可以设置一个键的值,只有当键不存在时才会设置成功。我们可以利用 SETNX 命令来实现一个简单的分布式锁。

SETNX order:lock 1

上面的代码表示尝试获取 order 业务的锁。

代码示例

下面是一个使用 Java Redis 客户端 Jedis 来生成不同业务的连续自增单号的示例代码:

import redis.clients.jedis.Jedis;

public class OrderNumberGenerator {
    private static final String REDIS_HOST = "localhost";
    private static final int REDIS_PORT = 6379;

    public String generateOrderNumber(String business) {
        Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);

        String lockKey = business + ":lock";
        String orderKey = business + ":current";

        // 获取分布式锁
        while (jedis.setnx(lockKey, "1") == 0) {
            // 等待获取锁
            Thread.sleep(10);
        }

        // 自增单号
        long orderNumber = jedis.incr(orderKey);

        // 释放分布式锁
        jedis.del(lockKey);

        jedis.close();

        return business + orderNumber;
    }
}

上面的代码中,我们使用了一个循环来尝试获取分布式锁,如果获取失败则等待一段时间后再次尝试。然后使用 INCR 命令自增单号,并在最后释放分布式锁。

性能优化

为了提高生成单号的性能,我们可以将单号的生成操作放在 Redis 的 Lua 脚本中执行。这样可以减少网络通信的开销,并减少了分布式锁的竞争。

下面是一个使用 Lua 脚本来生成不同业务的连续自增单号的示例代码:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class OrderNumberGenerator {
    private static final String REDIS_HOST = "localhost";
    private static final int REDIS_PORT = 637