Redis是我们在项目开发中常用的缓存中间件,Redis Cluster是Redis的分布式实现,可以帮我们提高架构是稳定性,可用性。在一次项目开发过程中,使用Redis进行两个Key进行比较,发生下面异常:
org.springframework.dao.InvalidDataAccessApiUsageException: CROSSSLOT Keys in request don't hash to the same slot. channel: nested exception is org.redisson.client.RedisException: CROSSSLOT Keys in request don't hash to the same slot. channel: [id: 0xd8e0d004, L:/10.186.72.183:49438 - R:10.150.25.24/10.150.25.24:6001] command: (SDIFF), params: [[111, 101, 115, 45, 112, 111, 111, 108, 45, 100, ...], [111, 101, 115, 45, 112, 111, 111, 108, 45, 100, ...]] at org.redisson.spring.data.connection.RedissonExceptionConverter.convert(RedissonExceptionConverter.java:48) at org.redisson.spring.data.connection.RedissonExceptionConverter.convert(RedissonExceptionConverter.java:35) at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44) at org.redisson.spring.data.connection.RedissonConnection.transform(RedissonConnection.java:234) at org.redisson.spring.data.connection.RedissonConnection.syncFuture(RedissonConnection.java:229)将Redis的Key中固定的值用{}包括起来。 举例:key1=order:1,key2=order:2 我们将order用{}括起来:key1={order}:1,key2={order}:2 这样问题就可以解决了。
报错日志的意思是:请求的Key键没有散列到同一个槽位中。 Redis 集群的键空间被分割为 16384 个槽(slot), 集群的最大节点数量也是 16384 个。 每个Key都会经过CRC16计算散列到固定的槽位中,对于多个键,仅当它们都共享相同的连接插槽时才执行(源码中的描述)。
Redis集群规范中说到: Jedis源码中的体现:
public static int getSlot(byte[] key) { if (key == null) { throw new JedisClusterOperationException("Slot calculation of null is impossible"); } int s = -1; int e = -1; boolean sFound = false; for (int i = 0; i < key.length; i++) { if (key[i] == '{' && !sFound) { s = i; sFound = true; } if (key[i] == '}' && sFound) { e = i; break; } } if (s > -1 && e > -1 && e != s + 1) { return getCRC16(key, s + 1, e) & (16384 - 1); } return getCRC16(key) & (16384 - 1); }Redisson源码中的体现: 有点小问题:如果只拼前缀{ ,如“{order:1”,则会报错 java.lang.IllegalArgumentException: 1 > -1
public int calcSlot(byte[] key) { if (key == null) { return 0; } int start = indexOf(key, (byte) '{'); if (start != -1) { int end = indexOf(key, (byte) '}'); key = Arrays.copyOfRange(key, start+1, end); } int result = CRC16.crc16(key) % MAX_SLOT; return result; }