Redis集群方案及实现 - yfk的专栏 - 博客频道 - CSDN.NET

【公告】博客系统优化升级    
   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

我的同类文章

Redis(12)

http://blog.csdn.net

.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;
}

时间: 2024-10-04 00:04:52

Redis集群方案及实现 - yfk的专栏 - 博客频道 - CSDN.NET的相关文章

架构设计:系统存储(18)——Redis集群方案:高性能

1.概述 通过上一篇文章(<架构设计:系统存储(17)--Redis集群方案:高可用>)的内容,Redis主从复制的基本功能和进行Redis高可用集群监控的Sentinel基本功能基本呈现给了读者.虽然本人并不清楚上一篇根据笔者实际工作经验所撰写的文章有什么重大问题,导致那么多朋友集体点踩而且截止目前又没有任何人愿意为笔者指出这些问题,但是这不会影响笔者继续学习.总结技术知识的热情.从这篇文章开始我们一起来讨论Redis中两种高性能集群方案,并且在讨论过程中将上一篇文章介绍的高可用集群方案结合

关于redis集群方案

最近在研究redis集群方案,看到知乎上有个朋友写的观点很好,就先收过来了.原文见:http://www.zhihu.com/question/21419897 为什么集群? 通常,为了提高网站响应速度,总是把热点数据保存在内存中而不是直接从后端数据库中读取.Redis是一个很好的Cache工具.大型网站应用,热点数据量往往巨大,几十G上百G是很正常的事儿,在这种情况下,如何正确架构Redis呢? 首先,无论我们是使用自己的物理主机,还是使用云服务主机,内存资源往往是有限制的,scale up不

Redis集群方案(来自网络)

参考: https://www.zhihu.com/question/21419897 http://www.cnblogs.com/haoxinyue/p/redis.html 为什么集群? 通常,为了提高网站响应速度,总是把热点数据保存在内存中而不是直接从后端数据库中读取.Redis是一个很好的Cache工具.大型网站应用,热点数据量往往巨大,几十G上百G是很正常的事儿,在这种情况下,如何正确架构Redis呢? 首先,无论我们是使用自己的物理主机,还是使用云服务主机,内存资源往往是有限制的,

Redis 集群方案介绍

由于Redis出众的性能,其在众多的移动互联网企业中得到广泛的应用.Redis在3.0版本前只支持单实例模式,虽然现在的服务器内存可以到100GB.200GB的规模,但是单实例模式限制了Redis没法满足业务的需求(例如新浪微博就曾经用Redis存储了超过1TB的数据).Redis的开发者Antirez早在博客上就提出在Redis 3.0版本中加入集群的功能,但3.0版本等到2015年才发布正式版.各大企业在3.0版本还没发布前为了解决Redis的存储瓶颈,纷纷推出了各自的Redis集群方案.这

Redis集群方案介绍

由于Redis出众的性能,其在众多的移动互联网企业中得到广泛的应用.Redis在3.0版本前只支持单实例模式,虽然现在的服务器内存可以到100GB.200GB的规模,但是单实例模式限制了Redis没法满足业务的需求(例如新浪微博就曾经用Redis存储了超过1TB的数据).Redis的开发者Antirez早在博客上就提出在Redis 3.0版本中加入集群的功能,但3.0版本等到2015年才发布正式版.各大企业在3.0版本还没发布前为了解决Redis的存储瓶颈,纷纷推出了各自的Redis集群方案.这

(转)Redis 集群方案

根据一些测试整理出来的一份方案: 1. Redis 性能 对于redis 的一些简单测试,仅供参考: 测试环境:Redhat6.2 , Xeon E5520(4核)*2/8G,1000M网卡 Redis 版本:2.6.9 客户端机器使用redis-benchmark 简单GET.SET操作: 1. 1单实例测试 1. Value大小:10Byte~1390Byte 处理速度: 7.5 w/s,速度受单线程处理能力限制 2. Value 大小:1400 左右 处理速度突降到5w/s 样子,网卡未能

大厂们的 redis 集群方案

redis 集群方案主要有两类,一是使用类 codis 的架构,按组划分,实例之间互相独立: 另一套是基于官方的 redis cluster 的方案:下面分别聊聊这两种方案: 类 codis 架构 这套架构的特点: 分片算法:基于 slot hash桶: 分片实例之间相互独立,每组 一个master 实例和多个slave: 路由信息存放到第三方存储组件,如 zookeeper 或etcd 旁路组件探活 使用这套方案的公司: 阿里云: ApsaraCache, RedisLabs.京东.百度等 c

Redis 集群方案

根据一些测试整理出来的一份方案: 1. Redis 性能 对于redis 的一些简单测试,仅供参考: 测试环境:Redhat6.2 , Xeon E5520(4核)*2/8G,1000M网卡 Redis 版本:2.6.9 客户端机器使用redis-benchmark 简单GET.SET操作: 1. 1单实例测试 1. Value大小:10Byte~1390Byte 处理速度: 7.5 w/s,速度受单线程处理能力限制 2. Value 大小:1400 左右 处理速度突降到5w/s 样子,网卡未能

redis集群方案总结

一,公司现在正在使用的集群方案(redis+sentinel) 通过多个Sentinel一起监控redis集群,检测到master不可用时,通过投票来决定master是否需要切换. Sentinel 之间互相检测(通过在共同检测的master中写入信息来进行),Sentinel 只需要配置master节点,自动通过master来获取已经连接的slave列表.当其中的某一个Sentinel 检测到master宕机之后,标示master为SDOWN,向其他的Sentinel 发送SENTINEL i