在作出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集群方案及实现的更多相关文章

  1. Redis集群方案介绍

    由于Redis出众的性能,其在众多的移动互联网企业中得到广泛的应用.Redis在3.0版本前只支持单实例模式,虽然现在的服务器内存可以到100GB.200GB的规模,但是单实例模式限制了Redis没法 ...

  2. Redis集群方案

    Redis集群方案 前段时间搞了搞Redis集群,想用做推荐系统的线上存储,说来挺有趣,这边基础架构不太完善,因此需要我们做推荐系统的自己来搭这个存储环境,就自己折腾了折腾.公司所给机器的单机性能其实 ...

  3. Redis集群方案怎么做?大牛给你介绍五种方案!

    Redis集群方案 Redis数据量日益增大,而且使用的公司越来越多,不仅用于做缓存,同时趋向于存储这块,这样必促使集群的发展,各个公司也在收集适合自己的集群方案,目前行业用的比较多的是下面几种集群架 ...

  4. 大厂们的 redis 集群方案

    redis 集群方案主要有两类,一是使用类 codis 的架构,按组划分,实例之间互相独立: 另一套是基于官方的 redis cluster 的方案:下面分别聊聊这两种方案: 类 codis 架构 这 ...

  5. Redis集群方案怎么做?

    转载自:https://www.jianshu.com/p/1ecbd1a88924 Redis集群方案 Redis数据量日益增大,而且使用的公司越来越多,不仅用于做缓存,同时趋向于存储这块,这样必促 ...

  6. Redis集群方案总结

    Redis集群方案总结 Redis集群方案总结Codis其余方案Redis cluster 目前,Redis中目前集群有以下几种方案: 主从复制 哨兵模式 redis cluster 代理 codis ...

  7. Redis 集群方案介绍

    由于Redis出众的性能,其在众多的移动互联网企业中得到广泛的应用.Redis在3.0版本前只支持单实例模式,虽然现在的服务器内存可以到100GB.200GB的规模,但是单实例模式限制了Redis没法 ...

  8. Redis集群方案收集

    说明: 如果不考虑客户端分片去实现集群,那么市面上基本可以说就三种方案最成熟,它们分别如下所示: 系统 贡献者 是否官方Redis实现 编程语言 Twemproxy Twitter 是 C Redis ...

  9. 基于Twemproxy的Redis集群方案(转载)

    原文地址:基于Twemproxy的Redis集群方案 概述 由于单台redis服务器的内存管理能力有限,使用过大内存redis服务器的性能急剧下降,且服务器发生故障将直接影响大面积业务.为了获取更好的 ...

  10. Redis集群方案(来自网络)

    参考: https://www.zhihu.com/question/21419897 http://www.cnblogs.com/haoxinyue/p/redis.html 为什么集群? 通常, ...

随机推荐

  1. Java设计模式之认识阶段

    设计模式是什么? 设计模式(Design pattern)是一套被重复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 其本质就是继承与接口的组合应用. 为什么要用设计模? 使用设计模式是为了 ...

  2. Flipping Game(枚举)

    Flipping Game time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  3. HDU5086Revenge of Segment Tree(数论)

    HDU5086Revenge of Segment Tree(数论) pid=5086" target="_blank" style="">题目 ...

  4. C++笔试面试总结

    手游广州某公司书面今天接受采访时.刚进去中午1中场休息.他们公司谁刚刚醒来,一个冷漠打牌,然后去上班.瞬间,这些公司有没有什么好印象,压抑. 接着快2点的时候.发了一份笔试题.大部分题目均在网上的&l ...

  5. spring改版官网下载jar包, 源代码和文档

    从网上找了一些方法,现在都整理了一下,有简单粗暴的,也有百转回肠的(详细,直接从官网一步一步的进入下载页),希望大家根据自己的喜好可以找到的真爱. 方法一:(简单粗暴直接) http://repo.s ...

  6. 将DataTable 数据插入 SQL SERVER 数据库

    原文:将DataTable 数据插入 SQL SERVER 数据库 以下提供3中方式将DataTable中的数据插入到SQL SERVER 数据库: 一:使用sqlcommand.executenon ...

  7. 九度OJ 1177 查找 (模拟)

    题目1177:查找 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:5659 解决:1667 题目描写叙述: 读入一组字符串(待操作的),再读入一个int n记录记下来有几条命令,总共同拥有 ...

  8. C/C++:C++友元类

    C++友元类: 正常情况下,一个类是訪问不到还有一个类的私有成员的. 就像以下这个: #include <iostream> class A{ private: int a; public ...

  9. 【SQL】Oracle的PL/SQL语法及其拓展数据类型总结

    PL/SQL语法 PL/SQL程序由三部分组成,声明部分.执行部分.异常处理部分. 模板: DECLARE /*变量声明*/ BEGIN /*程序主体*/ EXCEPTION /*异常处理部分*/ E ...

  10. Excel一项研究指出色彩

    Excel一项研究指出色彩 从Office2007跳到了2013版本号,尽管有着种种不适,但应该承认Excel2013版本号在图表和色彩上更加人性化和"专业"了一些. 但假设须要更 ...