博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Redis集群方案及实现
阅读量:6975 次
发布时间:2019-06-27

本文共 5095 字,大约阅读时间需要 16 分钟。

在作出Redis群集解决方案,他跑了小半个。行表现得非常稳定

在几乎相同的经历与大家分享,我写在前面的文章,能够做为背景阅读

应用

我们的Redis集群主要承担了下面服务:
1. 实时推荐
2. 用户画像
3. 诚信分值服务

集群状况

集群峰值QPS 1W左右,RW响应时间999线在1ms左右
整个集群:
1. Redis节点: 8台物理机;每台128G内存;每台机器上8个instance
2. Sentienl:3台虚拟机

集群方案

Redis Node由一组Redis Instance组成,一组Redis Instatnce能够有一个Master Instance。多个Slave Instance
Redis官方的cluster还在beta版本号,參看
在做调研的时候,以前特别关注过KeepAlived+VIP 和 Twemproxy
只是最后还是决定基于Redis Sentinel实现一套,整个项目大概在1人/1个半月

总体设计

1. 数据Hash分布在不同的Redis Instatnce上
2. M/S的切换採用Sentinel
3. 写:仅仅会写master Instance,从sentinel获取当前的master Instane
4. 读:从Redis Node中基于权重选取一个Redis Instance读取,失败/超时则轮询其它Instance
5. 通过RPC服务訪问。RPC server端封装了Redisclient,client基于jedis开发
6. 批量写/删除:不保证事务

RedisKey

public class RedisKey implements Serializable{	private static final long serialVersionUID = 1L;		//每一个业务不同的family	private 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之后RedisKey	public 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;		/**	 * 插入
数据到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
redisKeys, final ArrayList
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
redisKeys) throws Exception; /** * 插入
数据到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; /** * 插入
数据到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的Master
4.  写数据到Redis
//获取写哪个Redis Node		int slot = getSlot(keyHash);		RedisDataNode redisNode =  rdList.get(slot);		//写Master		JedisSentinelPool 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 Instatnce
4.  轮询读
//获取读哪个Redis Node		int slot = getSlot(keyHash);		RedisDataNode redisNode =  rdList.get(slot);		//依据权重选取一个工作Instatnce		int 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 instance		if(maxWeight == 0){			return (int) (Math.random() * RANDOM_SIZE % redisInstanceList.size());		}				//获取随机数		int rand = (int) (Math.random() * RANDOM_SIZE % maxWeight);		int sum = 0;			//选取Redis Instance		for (int i = 0; i < redisInstanceList.size(); i++) {			sum += redisInstanceList.get(i).getWeight();			if (rand < sum) {				return i;			}		}				return 0;	}

版权声明:本文博客原创文章,博客,未经同意,不得转载。

你可能感兴趣的文章
2.5万名网友认同流量跑得快 电信计量监管陷尴尬局面
查看>>
你未必知道 十大服务器虚拟化优化窍门
查看>>
POTN——新时代网络融合的必经之路
查看>>
浅谈各地降低能耗技术 关注可持续发展
查看>>
如何写出漂亮的React组件
查看>>
Windows管理员不可错过的那些卓越DevOps工具(下)
查看>>
物联网兴起 我国工业发展需关注四大关键技术
查看>>
面向未来就绪,企业级存储如何重新定义?
查看>>
智慧医疗在张家界的应用 首家智慧医疗医院投用
查看>>
Web 开发中 20 个很有用的 CSS 库
查看>>
Android系统中的进程管理:进程的创建
查看>>
看好大工业数据市场 GE将向软件业务14亿美元
查看>>
《JavaScript和jQuery实战手册(原书第2版)》——1.1节编程简介
查看>>
牛津大学量化金融创始人:如何获取并应用互联网大数据?
查看>>
快讯:特斯拉宣布26亿美元收购太阳能公司SolarCity
查看>>
你要知道的4个机房除尘小技巧
查看>>
百度金融布局金交所,账户和场景成掣肘
查看>>
仙童半导体拒绝华润等收购 担忧难获监管批准
查看>>
阿里视频云最强转码技术揭秘:窄带高清原理解析+用户接入指南
查看>>
茶道长:为什么加粉对于你的微商团队来说这么难?
查看>>