Redis集群方案及实现
在作出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版本号,參看Redis cluster tutorial
在做调研的时候,以前特别关注过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;
- /**
- * 插入<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的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;
- }
版权声明:本文博客原创文章,博客,未经同意,不得转载。
Redis集群方案及实现的更多相关文章
- Redis集群方案介绍
由于Redis出众的性能,其在众多的移动互联网企业中得到广泛的应用.Redis在3.0版本前只支持单实例模式,虽然现在的服务器内存可以到100GB.200GB的规模,但是单实例模式限制了Redis没法 ...
- Redis集群方案
Redis集群方案 前段时间搞了搞Redis集群,想用做推荐系统的线上存储,说来挺有趣,这边基础架构不太完善,因此需要我们做推荐系统的自己来搭这个存储环境,就自己折腾了折腾.公司所给机器的单机性能其实 ...
- Redis集群方案怎么做?大牛给你介绍五种方案!
Redis集群方案 Redis数据量日益增大,而且使用的公司越来越多,不仅用于做缓存,同时趋向于存储这块,这样必促使集群的发展,各个公司也在收集适合自己的集群方案,目前行业用的比较多的是下面几种集群架 ...
- 大厂们的 redis 集群方案
redis 集群方案主要有两类,一是使用类 codis 的架构,按组划分,实例之间互相独立: 另一套是基于官方的 redis cluster 的方案:下面分别聊聊这两种方案: 类 codis 架构 这 ...
- Redis集群方案怎么做?
转载自:https://www.jianshu.com/p/1ecbd1a88924 Redis集群方案 Redis数据量日益增大,而且使用的公司越来越多,不仅用于做缓存,同时趋向于存储这块,这样必促 ...
- Redis集群方案总结
Redis集群方案总结 Redis集群方案总结Codis其余方案Redis cluster 目前,Redis中目前集群有以下几种方案: 主从复制 哨兵模式 redis cluster 代理 codis ...
- Redis 集群方案介绍
由于Redis出众的性能,其在众多的移动互联网企业中得到广泛的应用.Redis在3.0版本前只支持单实例模式,虽然现在的服务器内存可以到100GB.200GB的规模,但是单实例模式限制了Redis没法 ...
- Redis集群方案收集
说明: 如果不考虑客户端分片去实现集群,那么市面上基本可以说就三种方案最成熟,它们分别如下所示: 系统 贡献者 是否官方Redis实现 编程语言 Twemproxy Twitter 是 C Redis ...
- 基于Twemproxy的Redis集群方案(转载)
原文地址:基于Twemproxy的Redis集群方案 概述 由于单台redis服务器的内存管理能力有限,使用过大内存redis服务器的性能急剧下降,且服务器发生故障将直接影响大面积业务.为了获取更好的 ...
- Redis集群方案(来自网络)
参考: https://www.zhihu.com/question/21419897 http://www.cnblogs.com/haoxinyue/p/redis.html 为什么集群? 通常, ...
随机推荐
- Xamarin for android:为button设置click事件的几种方法
原文:Xamarin for android:为button设置click事件的几种方法 在Xamarin中一个最基础的事情,就是为一个button指定click事件处理方法,可是即使是这么一件事也有 ...
- 足球和oracle系列(3):oracle过程排名,世界杯第二回合战罢到来!
足球与oracle系列(3):oracle进程排名.世界杯次回合即将战罢! 声明: 这不是技术文档,既然学来几招oracle简单招式.就忍不了在人前卖弄几下.纯为茶余饭后与数朋库友的插科 ...
- 华为-on练习--小写字符数的统计显示
主题: 手动输入一个字符串,只有小写字母,统计每个字符和输出频率中出现的串,输出.提示可以使用map 样例:输入:aaabbbccc 输出:a 3 b 3 c 3 分析: 看到后面的提示,简直就是不用 ...
- poj 1959 Darts 同意反复组合
水题.直接贴代码. //poj 1959 //sep9 #include <iostream> using namespace std; int n; int f[128]; int so ...
- 输入框 js正则推断输入
1.文本框仅仅能输入数字代码(小数点也不能输入) <input onkeyup="this.value=this.value.replace(/\D/g,'')" onaf ...
- Android AlarmManager报警的实现
什么是AlarmManager? AlarmManager它是Android经常使用的系统-Level提醒服务,我们指定为广播中的特定时间Intent. 我们设定一个时间,然后在该时间到来时.Alar ...
- SQL中IN,NOT IN,EXISTS,NOT EXISTS的用法和差别
SQL中IN,NOT IN,EXISTS,NOT EXISTS的用法和差别: IN:确定给定的值是否与子查询或列表中的值相匹配. IN 关键字使您得以选择与列表中的任意一个值匹配的行. 当要获得居住在 ...
- Android搜索芽发展clientVersion1.0结束(过程和结果显示)
本文原:http://blog.csdn.net/minimicall 转载标明. 博士生.找我,我希望有一个合作伙伴.为了帮助他解决了移动终端产品.他给了我他的想法的叙述性说明,搜索布.要搜索布图像 ...
- vuejs 相关资料
官网 http://vuejs.org/ 中文网站 http://cn.vuejs.org/ Vue.js——60分钟快速入门 http://www.cnblogs.com/keepfool/p/56 ...
- jquery ui 笔记
准备: 1.下载jquery ui库:http://jqueryui.com/download/ 2.选择theme 3.建立一个良好的发展环境(mysql.netbeans) 4.创建数据库:jqu ...