Redis使用教程之jedis客户端sendCommand方法的byte[]入参,防止混淆string的byte与数值byte的区别

1.背景

在使用jedis客户端操作redis数据的过程中,发现了一个通用的方法sendCommand,封装了redis的所有命令,代码路径:redis.clients.jedis.BinaryJedis#sendCommand(redis.clients.jedis.commands.ProtocolCommand, byte[]...),具体代码如下

    public Object sendCommand(ProtocolCommand cmd, byte[]... args) {
        this.checkIsInMultiOrPipeline();
        this.client.sendCommand(cmd, args);
        return this.client.getOne();
    }

2.封装使用sendCommand方法

封装代码

public void sendCommandWrapper(List> commandList) {
            Jedis jedis = new Jedis("127.0.0.1",3306);
            for (List command : commandList) {
                byte[][] splitResult = command.stream().toArray(byte[][]::new);
                for (byte[] cmd : command) {
                   jedis.sendCommand(() -> splitResult[0],
                        Arrays.copyOfRange(splitResult, 1, splitResult.length));
            }
    }
}

方法入参

参数类似如下

[[["set"],["a"],["v"]],[["set"],["a"],["b"]]],[["set"],["a"],["1"]]]]

然后把每个字符换成byte

[[[115, 101, 116], [97], [118]], [[115, 101, 116], [97], [98]], [[115, 101, 116], [97], [49]]]

3.存在的问题

sendCommand方法可以传递string的命令转成的byte[]参数,但是其中有两个特例

需要注意的特例

zset的score是有符号的浮点型

Pexpireat key 时间戳:时间戳是long型

问题归纳

以“Pexpireat key 时间戳”命令为例,long型时间戳通过redis协议到redis底层存储是byte[]的方式,使用sendCommand的时候传递的命令入参也是byte[],但是这两种byte[]不是同一种byte[]。

这两种 byte[] 不同的主要原因是它们所表示的含义不同。

在使用 Redis 命令时,我们需要将命令的参数转换为 byte[] 格式,以便可以发送给 Redis 服务器。这里的 byte[] 实际上是字符串的字节数组表示。Redis 协议是基于文本的,即它要求在与服务器通信时发送文本字符串,因此发送给 Redis 服务器的 byte[] 实际上是表示字符串的字节数组。

String.valueOf(redisTTLLong).getBytes(StandardCharsets.UTF_8)

而在 Redis 底层存储中,时间戳所表示的含义是一个数字,而不是一个字符串。在底层存储中,Redis 将时间戳转换为了二进制形式,即一个 byte[] 数组。这个 byte[] 数组表示的是一个数字,它在内存中以二进制补码的形式存储。

//比如从底层取出来的byte,想转回时间戳需要的转换逻辑: byte转long 
private static long convertRedisTTLToTimestamp(byte[] ttlBytes) {
        // Convert the byte array to an 8-byte binary string
        byte[] binaryBytes = new byte[8];
        for (int i = 0; i < 8; i++) {
            binaryBytes[i] = i < ttlBytes.length ? ttlBytes[i] : 0;
            if (binaryBytes[i] < 0) {
                binaryBytes[i] += 256;
            }
        }
        // Rearrange the binary string according to the big endian byte order
        long timestamp = 0L;
        for (int i = 0; i < 8; i++) {
            timestamp = (timestamp << 8) + (binaryBytes[i] & 0xff);
        }
        // returns the converted timestamp in milliseconds
        return timestamp;
    }

因此,这两种 byte[] 不同的原因在于它们所表示的含义不同。一个表示字符串,一个表示数字的二进制补码。虽然它们都是 byte[] 类型,但它们的内部存储和解析方式是不同的

4.总结

使用jedis的sendCommand命令时,要记住要传入的参数,原本属于数值类型时,需要转byte[]数组是直接转成字符串的字节数组。也就是把long时间戳通过String.valueOf(redisTTLLong).getBytes(StandardCharsets.UTF_8)命令转换出来的byte[]。

请勿与long转byte等相关的补码、大小端等概念混淆。

你可能感兴趣的:(redis,redis,数据库,jedis,sendCommand,byte[])