redis 集群批量操作實(shí)現(xiàn)
Redis集群是沒法執(zhí)行批量操作命令的,如mget,pipeline等。這是因?yàn)閞edis將集群劃分為16383個(gè)哈希槽,不同的key會(huì)劃分到不同的槽中。但是,Jedis客戶端提供了計(jì)算key的slot方法,已經(jīng)slot和節(jié)點(diǎn)之間的映射關(guān)系,通過(guò)這兩個(gè)數(shù)據(jù),就可以計(jì)算出每個(gè)key所在的節(jié)點(diǎn),然后使用pipeline獲取數(shù)據(jù)。具體代碼如下:
初始化 JedisCluster類
@Configuration
public class JedisClusterConfig {
@Value("${spring.redis.cluster.nodes}")
private String clusterNodes;
@Value("${spring.redis.cache.commandTimeout}")
private Integer commandTimeout;
@Bean
public JedisCluster getJedisCluster() {
String[] serverArray = clusterNodes.split(",");
Set<HostAndPort> nodes = new HashSet<>();
for (String ipPort : serverArray) {
String[] ipPortPair = ipPort.split(":");
nodes.add(new HostAndPort(ipPortPair[0].trim(), Integer.valueOf(ipPortPair[1].trim())));
}
return new JedisCluster(nodes, commandTimeout);
}
}
工具類 JedisClusterUtil
@Component
public class JedisClusterUtil {
@Autowired
private JedisCluster jedisCluster;
@Resource(name = "redisTemplate4Json")
protected RedisTemplate<String, Object> redisTemplate;
/**
* ZSet批量查詢
* @param keys
* @return
*/
public List<Object> batchZRange(List<String> keys) {
List<Object> resList = new ArrayList<>();
if (keys == null || keys.size() == 0) {
return resList;
}
if (keys.size() == 1) {
BoundZSetOperations<String, Object> operations = redisTemplate.boundZSetOps(keys.get(0));
Set<Object> set = operations.reverseRange(0, 0);
resList.add(set.iterator().next());
return resList;
}
Map<JedisPool, List<String>> jedisPoolMap = getJedisPool(keys);
List<String> keyList;
JedisPool currentJedisPool = null;
Pipeline currentPipeline;
List<Object> res = new ArrayList<>();
Map<String, Object> resultMap = new HashMap<>();
//執(zhí)行
for (Map.Entry<JedisPool, List<String>> entry : jedisPoolMap.entrySet()) {
Jedis jedis = null;
try {
currentJedisPool = entry.getKey();
keyList = entry.getValue();
//獲取pipeline
jedis = currentJedisPool.getResource();
currentPipeline = jedis.pipelined();
for (String key : keyList) {
currentPipeline.zrevrange(key, 0, 0);
}
//從pipeline中獲取結(jié)果
res = currentPipeline.syncAndReturnAll();
currentPipeline.close();
for (int i = 0; i < keyList.size(); i++) {
if (null == res.get(i)) {
resultMap.put(keyList.get(i), null);
} else {
Set<Object> set = (Set<Object>) res.get(i);
if (null == set || set.isEmpty()) {
resultMap.put(keyList.get(i), null);
} else {
byte[] byteStr = set.iterator().next().toString().getBytes();
Object obj = redisTemplate.getDefaultSerializer().deserialize(byteStr);
resultMap.put(keyList.get(i), obj);
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
returnResource(jedis, currentJedisPool);
}
}
resList = sortList(keys, resultMap);
return resList;
}
/**
* Value批量查詢
* @param keys
* @return
*/
public List<Object> batchGet(List<String> keys){
List<Object> resList = new ArrayList<>();
if (keys == null || keys.size() == 0) {
return resList;
}
if (keys.size() == 1) {
BoundValueOperations<String, Object> operations = redisTemplate.boundValueOps(keys.get(0));
resList.add(operations.get());
return resList;
}
Map<JedisPool, List<String>> jedisPoolMap = getJedisPool(keys);
List<String> keyList;
JedisPool currentJedisPool = null;
Pipeline currentPipeline;
List<Object> res = new ArrayList<>();
Map<String, Object> resultMap = new HashMap<>();
for (Map.Entry<JedisPool, List<String>> entry : jedisPoolMap.entrySet()) {
Jedis jedis = null;
try {
currentJedisPool = entry.getKey();
keyList = entry.getValue();
//獲取pipeline
jedis = currentJedisPool.getResource();
currentPipeline = jedis.pipelined();
for (String key : keyList) {
currentPipeline.get(key);
}
//從pipeline中獲取結(jié)果
res = currentPipeline.syncAndReturnAll();
currentPipeline.close();
for (int i = 0; i < keyList.size(); i++) {
if (null == res.get(i)) {
resultMap.put(keyList.get(i), null);
} else {
byte[] byteStr = keyList.get(i).toString().getBytes();
Object obj = redisTemplate.getDefaultSerializer().deserialize(byteStr);
resultMap.put(keyList.get(i), obj);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
returnResource(jedis, currentJedisPool);
}
}
resList = sortList(keys, resultMap);
return resList;
}
private Map<JedisPool, List<String>> getJedisPool(List<String> keys){
//JedisCluster繼承了BinaryJedisCluster
//BinaryJedisCluster的JedisClusterConnectionHandler屬性
//里面有JedisClusterInfoCache,根據(jù)這一條繼承鏈,可以獲取到JedisClusterInfoCache
//從而獲取slot和JedisPool直接的映射
MetaObject metaObject = SystemMetaObject.forObject(jedisCluster);
JedisClusterInfoCache cache = (JedisClusterInfoCache) metaObject.getValue("connectionHandler.cache");
//保存地址+端口和命令的映射
Map<JedisPool, List<String>> jedisPoolMap = new HashMap<>();
JedisPool currentJedisPool = null;
List<String> keyList;
for (String key : keys) {
//計(jì)算哈希槽
int crc = JedisClusterCRC16.getSlot(key);
//通過(guò)哈希槽獲取節(jié)點(diǎn)的連接
currentJedisPool = cache.getSlotPool(crc);
//由于JedisPool作為value保存在JedisClusterInfoCache中的一個(gè)map對(duì)象中,每個(gè)節(jié)點(diǎn)的
//JedisPool在map的初始化階段就是確定的和唯一的,所以獲取到的每個(gè)節(jié)點(diǎn)的JedisPool都是一樣
//的,可以作為map的key
if (jedisPoolMap.containsKey(currentJedisPool)) {
jedisPoolMap.get(currentJedisPool).add(key);
} else {
keyList = new ArrayList<>();
keyList.add(key);
jedisPoolMap.put(currentJedisPool, keyList);
}
}
return jedisPoolMap;
}
private List<Object> sortList(List<String> keys, Map<String, Object> params) {
List<Object> resultList = new ArrayList<>();
Iterator<String> it = keys.iterator();
while (it.hasNext()) {
String key = it.next();
resultList.add(params.get(key));
}
return resultList;
}
/**
* 釋放jedis資源
*
* @param jedis
*/
public void returnResource(Jedis jedis, JedisPool jedisPool) {
if (jedis != null && jedisPool != null) {
jedisPool.returnResource(jedis);
}
}
注意:一定要完成后釋放 jedis 資源 不然會(huì)造成卡死現(xiàn)象
到此這篇關(guān)于redis 集群批量操作實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)redis 集群批量操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Redis+Lua腳本實(shí)現(xiàn)分布式限流組件封裝的方法
這篇文章主要介紹了基于Redis+Lua腳本實(shí)現(xiàn)分布式限流組件封裝,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
Spring+Redis+RabbitMQ開發(fā)限流和秒殺項(xiàng)目功能
本項(xiàng)目將通過(guò)整合Springboot和Redis以及Lua腳本來(lái)實(shí)現(xiàn)限流和秒殺的效果,將通過(guò)RabbitMQ消息隊(duì)列來(lái)實(shí)現(xiàn)異步保存秒殺結(jié)果的效果,對(duì)Spring?Redis?RabbitMQ限流秒殺功能實(shí)現(xiàn)感興趣的朋友一起看看吧2022-02-02
如何操作Redis和zookeeper實(shí)現(xiàn)分布式鎖
這篇文章主要介紹了如何操作Redis和zookeeper實(shí)現(xiàn)分布式鎖的相關(guān)資料,需要的朋友可以參考下2017-07-07
Redis數(shù)據(jù)庫(kù)分布式設(shè)計(jì)方案介紹
大家好,本篇文章主要講的是Redis數(shù)據(jù)庫(kù)分布式設(shè)計(jì)方案介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01
基于Redis位圖實(shí)現(xiàn)系統(tǒng)用戶登錄統(tǒng)計(jì)
這篇文章主要介紹了基于Redis位圖實(shí)現(xiàn)系統(tǒng)用戶登錄統(tǒng)計(jì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11

