Redis集群方案及实现

之前做了一个Redis的集群方案,跑了小半年,线上运行的很稳定差不多可以跟大家分享下经验,前面写了一篇文章数据在线服务的一些探索经验,可以做为背景阅读

应用我们的Redis集群主要承担了以下服务:1. 实时推荐2. 用户画像3. 诚信分值服务集群状况集群峰值QPS 1W左右,RW响应时间999线在1ms左右整个集群:1. Redis节点: 8台物理机;每台128G内存;每台机器上8个instance2. Sentienl:3台虚拟机集群方案

Redis Node由一组Redis Instance组成,一组Redis Instatnce可以有一个Master Instance,多个Slave InstanceRedis官方的cluster还在beta版本,参看Redis cluster tutorial在做调研的时候,曾经特别关注过KeepAlived+VIP 和 Twemproxy不过最后还是决定基于Redis Sentinel实现一套,整个项目大概在1人/1个半月整体设计1. 数据Hash分布在不同的Redis Instatnce上2. M/S的切换采用Sentinel3. 写:只会写master Instance,从sentinel获取当前的master Instane4. 读:从Redis Node中基于权重选取一个Redis Instance读取,失败/超时则轮询其他Instance5. 通过RPC服务访问,RPC server端封装了Redis客户端,客户端基于jedis开发6. 批量写/删除:不保证事务RedisKeypublic class RedisKey implements Serializable{private static final long serialVersionUID = 1L;//每个业务不同的familyprivate String family;private String key;……//物理保存在Redis上的key为经过MurmurHash之后的值private String makeRedisHashKey(){return String.valueOf(MurmurHash.hash64(makeRedisKeyString()));}//ReidsKey由family.key组成private String makeRedisKeyString(){return family +":"+ key;}//返回用户的经过Hash之后RedisKeypublic String getRedisKey(){return makeRedisHashKey();}…..}

Family的存在时为了避免多个业务key冲突,,给每个业务定义自己独立的Faimily出于性能考虑,参考Redis存储设计,实际保存在Redis上的key为经过hash之后的值

接口目前支持的接口包括:public interface RedisUseInterface{/** * 通过RedisKey获取value * * @param redisKey *redis中的key * @return *成功返回value,查询不到返回NULL */public String get(final RedisKey redisKey) throws Exception;/** * 插入<k,v>数据到Redis * * @param redisKey *the redis key * @param value *the redis value * @return *成功返回"OK",插入失败返回NULL */public String set(final RedisKey redisKey, final String value) throws Exception;/** * 批量写入数据到Redis * * @param redisKeys *the redis key list * @param values *the redis value list * @return *成功返回"OK",插入失败返回NULL */public String mset(final ArrayList<RedisKey> redisKeys, final ArrayList<String> values) throws Exception;/** * 从Redis中删除一条数据 * * @param redisKey *the redis key * @return *an integer greater than 0 if one or more keys were removed 0 if none of the specified key existed */public Long del(RedisKey redisKey) throws Exception;/** * 从Redis中批量删除数据 * * @param redisKey *the redis key * @return *返回成功删除的数据条数 */public Long del(ArrayList<RedisKey> redisKeys) throws Exception;/** * 插入<k,v>数据到Redis * * @param redisKey *the redis key * @param value *the redis value * @return *成功返回"OK",插入失败返回NULL */public String setByte(final RedisKey redisKey, final byte[] value) throws Exception;/** * 插入<k,v>数据到Redis * * @param redisKey *the redis key * @param value *the redis value * @return *成功返回"OK",插入失败返回NULL */public String setByte(final String redisKey, final byte[] value) throws Exception;/** * 通过RedisKey获取value * * @param redisKey *redis中的key * @return *成功返回value,查询不到返回NULL */public byte[] getByte(final RedisKey redisKey) throws Exception;/** * 在指定key上设置超时时间 * * @param redisKey *the redis key * @param seconds *the expire seconds * @return *1:success, 0:failed */public Long expire(RedisKey redisKey, int seconds) throws Exception;}写Redis流程1. 计算Redis Key Hash值2. 根据Hash值获取Redis Node编号3. 从sentinel获取Redis Node的Master4. 写数据到Redis//获取写哪个Redis Nodeint slot = getSlot(keyHash);RedisDataNode redisNode = rdList.get(slot);//写MasterJedisSentinelPool jp = redisNode.getSentinelPool();Jedis je = null;boolean success = true;try {je = jp.getResource();return je.set(key, value);} catch (Exception e) {log.error("Maybe master is down", e);e.printStackTrace();success = false;if (je != null)jp.returnBrokenResource(je);throw e;} finally {if (success && je != null) {jp.returnResource(je);}}读流程1. 计算Redis Key Hash值2. 根据Hash值获取Redis Node编号3. 根据权重选取一个Redis Instatnce4. 轮询读//获取读哪个Redis Nodeint slot = getSlot(keyHash);RedisDataNode redisNode = rdList.get(slot);//根据权重选取一个工作Instatnceint rn = redisNode.getWorkInstance();//轮询int cursor = rn;do {try {JedisPool jp = redisNode.getInstance(cursor).getJp();return getImpl(jp, key);} catch (Exception e) {log.error("Maybe a redis instance is down, slot : [" + slot + "]" + e);e.printStackTrace();cursor = (cursor + 1) % redisNode.getInstanceCount();if(cursor == rn){throw e;}}} while (cursor != rn);权重计算初始化的时候,会给每个Redis Instatnce赋一个权重值weight根据权重获取Redis Instance的代码:public int getWorkInstance() {//没有定义weight,则完全随机选取一个redis instanceif(maxWeight == 0){return (int) (Math.random() * RANDOM_SIZE % redisInstanceList.size());}//获取随机数int rand = (int) (Math.random() * RANDOM_SIZE % maxWeight);int sum = 0;//选取Redis Instancefor (int i = 0; i < redisInstanceList.size(); i++) {sum += redisInstanceList.get(i).getWeight();if (rand < sum) {return i;}}return 0;}

风不懂云的漂泊,天不懂雨的落魄,眼不懂泪的懦弱,

Redis集群方案及实现

相关文章:

你感兴趣的文章:

标签云: