【公告】博客系统优化升级
Unity3D学习,离VR开发还有一步
博乐招募开始啦
虚拟现实,一探究竟
Redis集群方案及实现
2014-08-30 17:20
43035人阅读
评论(15)
收藏
举报
本文章已收录于:
.embody{
padding:10px 10px 10px;
margin:0 -20px;
border-bottom:solid 1px #ededed;
}
.embody_b{
margin:0 ;
padding:10px 0;
}
.embody .embody_t,.embody .embody_c{
display: inline-block;
margin-right:10px;
}
.embody_t{
font-size: 12px;
color:#999;
}
.embody_c{
font-size: 12px;
}
.embody_c img,.embody_c em{
display: inline-block;
vertical-align: middle;
}
.embody_c img{
width:30px;
height:30px;
}
.embody_c em{
margin: 0 20px 0 10px;
color:#333;
font-style: normal;
}
分类:
Redis(12)
作者同类文章
X
版权声明:本文为博主原创文章,未经博主允许不得转载。
之前做了一个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端封装了Redis客户端,客户端基于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; }
-
顶
- 14
- 踩
- 0
- 上一篇HBase原子性保证
- 下一篇Vector Clock理解
我的同类文章
Redis(12)
.blog-ass-articl dd {
color: #369;
width: 99%; /*修改行*/
float: left;
overflow: hidden;
font: normal normal 12px/23px "SimSun";
height: 23px;
margin: 0;
padding: 0 0 0 10px;
margin-right: 30px;
background: url(http://static.blog.csdn.net/skin/default/images/blog-dot-red3.gif) no-repeat 0 10px;
}
参考知识库
- 更多资料请参考:
-
猜你在找
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
.tag_list
{
background: none repeat scroll 0 0 #FFFFFF;
border: 1px solid #D7CBC1;
color: #000000;
font-size: 12px;
line-height: 20px;
list-style: none outside none;
margin: 10px 2% 0 1%;
padding: 1px;
}
.tag_list h5
{
background: none repeat scroll 0 0 #E0DBD3;
color: #47381C;
font-size: 12px;
height: 24px;
line-height: 24px;
padding: 0 5px;
margin: 0;
}
.tag_list h5 a
{
color: #47381C;
}
.classify
{
margin: 10px 0;
padding: 4px 12px 8px;
}
.classify a
{
margin-right: 20px;
white-space: nowrap;
}
#popup_mask
{
position: absolute;
width: 100%;
height: 100%;
background: #000;
z-index: 9999;
left: 0px;
top: 0px;
opacity: 0.3;
filter: alpha(opacity=30);
display: none;
}