之前做了一个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;//每个业务不同的familyprivate 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之后RedisKeypublic 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 Nodeint slot = getSlot(keyHash);RedisDataNode redisNode =  rdList.get(slot);//写MasterJedisSentinelPool 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 Nodeint slot = getSlot(keyHash);RedisDataNode redisNode =  rdList.get(slot);//根据权重选取一个工作Instatnceint 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 instanceif(maxWeight == 0){return (int) (Math.random() * RANDOM_SIZE % redisInstanceList.size());}//获取随机数int rand = (int) (Math.random() * RANDOM_SIZE % maxWeight);int sum = 0;//选取Redis Instancefor (int i = 0; i < redisInstanceList.size(); i++) {sum += redisInstanceList.get(i).getWeight();if (rand < sum) {return i;}}return 0;}

转载于:https://my.oschina.net/zipu888/blog/549572

Redis集群方案及实现相关推荐

  1. Redis集群方案,Codis安装测试

    Redis集群方案,Codis安装测试 1,关于豌豆荚开源的Codis Codis是豌豆荚使用Go和C语言开发.以代理的方式实现的一个Redis分布式集群解决方案,且完全兼容Twemproxy.Twe ...

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

    Redis集群方案及实现 - yfk的专栏        - 博客频道 - CSDN.NET yfk的专栏 学习&记录&分享 目录视图 摘要视图 订阅 [公告]博客系统优化升级   U ...

  3. 基于Twemproxy的Redis集群方案

    基于Twemproxy的Redis集群方案 原文地址:http://www.cnblogs.com/haoxinyue/p/redis.html  为了保持方便,愿原博主谅解 概述 由于单台redis ...

  4. Redis - 集群方案

    在开发测试环境中,我们一般搭建Redis的单实例来应对开发测试需求,但是在生产环境,如果对可用性.可靠性要求较高,则需要引入Redis的集群方案.虽然现在各大云平台有提供缓存服务可以直接使用,但了解一 ...

  5. redis集群方案-Twemproxy

    redis集群方案-Twemproxy 1 Twemproxy是什么? Twemproxy是一种代理分片机制,来源于Twitter开源.Twemproxy按照路由规则,转发给后台的各个Redis服务器 ...

  6. Redis集群方案对比:Codis、Twemproxy、Redis Cluster

    为了保证Redis的高可用,主要需要以下几个方面: 数据持久化 主从复制 自动故障恢复 集群化 我们简单理一下这几个方案的特点,以及它们之间的联系. 数据持久化本质上是为了做数据备份,有了数据持久化, ...

  7. Redis 集群方案

    根据一些测试整理出来的一份方案: 1. Redis 性能 对于redis 的一些简单测试,仅供参考: 测试环境:Redhat6.2 , Xeon E5520(4核)*2/8G,1000M网卡 Redi ...

  8. Redis集群方案应该怎么做?都有哪些方案?

    1.codis. 目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在 节点数量改变情况下,旧节点数据可恢复到新hash节点. 2.redis cluster3.0自带的集群,特点在 ...

  9. Redis集群方案:redis-cluste原理与搭建

    一.Redis集群方式介绍 3.0版本之前的redis是不支持集群的,只支持单实例模式,虽然支持主从模式.哨兵模式部署来解决单点故障,但是想要集群得用中间件.不适合大型数据. 常见集群方案: (1)官 ...

最新文章

  1. docker学习实践之路[第五站]mysql镜像应用
  2. 第19课:Spark高级排序彻底解密
  3. WEB初学者简介,web入门
  4. MySQL数据库InnoDB坏页处理修复
  5. linux sort,uniq,cut,wc命令详解
  6. 机器学习——python实现SVM模型w,b的查看
  7. 自定义ViewGroup实现仿微信图片选择器
  8. Nginx之http配置
  9. WebSocket子协议STOMP详解
  10. 重建二叉树(C++)
  11. 大规模分布式系统架构与设计实战
  12. 局域网服务器共享文件权限,局域网共享文件怎么设置权限
  13. 16个外接SMA天线R2000超高频RFID写卡器HXU2899M上位机命令数据块
  14. 《统计学》第八版贾俊平第八章假设检验知识点总结及课后习题答案
  15. 全自动高清录播服务器,高清高清录播服务器 高清全自动录播系统 方便携带 搭建快捷...
  16. pycharm-03-工程结构
  17. python画平行坐标图_[宜配屋]听图阁
  18. Ubuntu:安装rust
  19. MongoDB的多表关联查询
  20. java 字母个数字_Java 中字母对应的数字是多少

热门文章

  1. 机器学习什么显卡_为什么机器学习模型在生产中会退化?
  2. pt, px, DPI: 关于长度单位的误解
  3. ad中装配图如何导出,PCB设计软件输出装配图的3个方法
  4. 计算机如何识别这是一个键盘,键盘两个键同时按下时计算机如何识别被按下的是哪个键...
  5. 中国大学Mocca哈工大C语言第8周编程题在线测试
  6. 数据库大作业代码展示1
  7. mysql 存放byte_mysql 存储byte数组
  8. Python自动化办公(一) :滴滴行程单信息提取存入excel表格 2021-04-13
  9. 用RGBD投影激光雷达数据:depthimage_to_laserscan
  10. 【UiBot实战教程】梦想云ERP系统数据获取和填写(一)