Redis缓存架构设计

对于下面两个架构图,有如下想法:

1)redis主从复制模式,为了解决master读写压力,对master进行写操作,对slave进行读操作。

2)而在分片集群中,如果对部分分片进行写,部分分片进行读,那么会导致写入后无法get指定key的情况。

3)二级缓存有必要吗?二级缓存最主要的问题解决存储介质由磁盘存储转变为内存存储,而redis本身就作为内存数据库,最主要的只能够解决网络问题。

其次多个应用对同一条key-value做修改,是否能保证各个二级缓存数据的一致性呢。

主从读写分离设计

//定义统一连接接口
public interface IRedisConnection {
public String connection_name = "";
/**
* @return the connection_name
*/
public String getConnection_name() ;
/**
* @param connectionName the connection_name to set
*/
public void setConnection_name(String connectionName) ;
/**
* @param key value
* @param value
*/
public void write(String key,String value)throws Exception;
/**
* @param key
* @return
*/
public Object read(String key)throws Exception; // public boolean isValiate() ;
} //读连接
public class ReadConnection extends Jedis implements IRedisConnection{
public String connection_name = "";
/**
* @param host
*/
public ReadConnection(String host,long port) {
super(host,(int)port);
// TODO Auto-generated constructor stub
}
/**
* @return the connection_name
*/
public String getConnection_name() {
return connection_name;
}
/**
* @param connectionName the connection_name to set
*/
public void setConnection_name(String connectionName) {
connection_name = connectionName;
} public Object read(String key){
return this.get(key);
}
public void write(String key,String value) throws Exception{
throw new Exception("未定义的方法");
}
} //写连接
public class WriteConnection extends Jedis implements IRedisConnection{
public String connection_name = "";
/**
* @param host
*/
public WriteConnection(String host,long port) {
super(host,(int)port);
// TODO Auto-generated constructor stub
}
/**
* @return the connection_name
*/
public String getConnection_name() {
return connection_name;
}
/**
* @param connectionName the connection_name to set
*/
public void setConnection_name(String connectionName) {
connection_name = connectionName;
} public void write(String key,String value){
this.set(key, value);
}
public Object read(String key)throws Exception{
throw new Exception("未锟斤拷锟斤拷姆锟斤拷锟?");
} } //获取一个指定读/写的连接 当然可以,故障检测 ,故障隔离 ,使用 JedisPool保存在内存中
public static IRedisConnection getConnection(String code,
int connectionType) throws Exception {
IRedisBaseSV sv = (IRedisBaseSV) ServiceFactory
.getService(IRedisBaseSV.class);
CFG_REDIS_SERVERBean server = sv.getServerFromCode(code);
if (server == null) {
log.error("根据编码:" + code + "不能找到对应的redis服务器资源");
throw new Exception("根据编码:" + code + "不能找到对应的redis服务器资源");
}
IRedisConnection conn; if (connectionType == RedisConstants.WRITE_ONLY_CONNECTION) {
conn = new WriteConnection(server.getServerIp(), server
.getServerPort());

} else {
conn = new ReadConnection(server.getServerIp(), server
.getServerPort());

} conn.setConnection_name(code);
return conn;
}

初始化切片池

/**
* 初始化切片池
*/
private static void initialShardedPool() throws Exception {
long start = System.nanoTime(); IRedisBaseSV sv = (IRedisBaseSV) ServiceFactory.getService(IRedisBaseSV.class);
// 初始化JedisPoolConfig
CFG_REDIS_PARAMETERBean[] para = sv.getRedisConfig("DEFAULT");
for (int i = 0; i < para.length; i++) {
if (para[i].getParameterName().equalsIgnoreCase(RedisConstants.REDIS_SERVER_SHARED_MAXACTIVE))
config.setMaxActive(Integer.parseInt(para[i].getParameterValue()));
if (para[i].getParameterName().equalsIgnoreCase(RedisConstants.REDIS_SERVER_SHARED_MAXIDLE))
config.setMaxIdle(Integer.parseInt(para[i].getParameterValue()));
if (para[i].getParameterName().equalsIgnoreCase(RedisConstants.REDIS_SERVER_SHARED_MAXWAIT))
config.setMaxWait(Long.parseLong(para[i].getParameterValue()));
if (para[i].getParameterName().equalsIgnoreCase(
RedisConstants.REDIS_SERVER_SHARED_TESTONBORROW))
config.setTestOnBorrow(Boolean.getBoolean(para[i].getParameterValue())); if (para[i].getParameterName().equalsIgnoreCase(RedisConstants.REDIS_SERVER_SHARED_NEED_SYN))
NEED_WRITE_SYNCHRONIZE = Boolean.parseBoolean(para[i].getParameterValue()); // Boolean.getBoolean不对
} CFG_REDIS_SERVERBean[] servers = sv.getRedisServer();
HashMap map = new HashMap();
for (int i = 0; i < servers.length; i++) {
map.put(servers[i].getBelongGroup().toUpperCase(), servers[i].getBelongGroup().toUpperCase());
}
serverGroupCodeList = new ArrayList(map.values());
ShardedJedisPool[] readConnectionPools;
ShardedJedisPool[] writeConnectionPools;
ShardedJedisPool[] persistConnectionPools;
readConnectionPools = new ShardedJedisPool[serverGroupCodeList.size()];
writeConnectionPools = new ShardedJedisPool[serverGroupCodeList.size()];
persistConnectionPools = new ShardedJedisPool[serverGroupCodeList.size()];
for (int i = 0; i < serverGroupCodeList.size(); i++) {
List<JedisShardInfo> readShards = new ArrayList<JedisShardInfo>();
List<JedisShardInfo> writeShards = new ArrayList<JedisShardInfo>();
List<JedisShardInfo> persistShards = new ArrayList<JedisShardInfo>();
//遍历所有的redis server实例
for (int j = 0; j < servers.length; j++) {
if (servers[j].getBelongGroup()
.equalsIgnoreCase(String.valueOf(serverGroupCodeList.get(i)))
&& servers[j].getUseType().equalsIgnoreCase(
String.valueOf(RedisConstants.READ_ONLY_CONNECTION))) {
// 先测试该连接是否可用
readServersAll.add(servers[j]);
Boolean connectionTest =
TestConnection(servers[j].getServerIp(), (int) servers[j].getServerPort(),
decryption(servers[j].getRequirepass()), servers[j].getBelongGroup());
if (connectionTest) {
JedisShardInfo jds =
new JedisShardInfo(servers[j].getServerIp(), (int) servers[j].getServerPort());
if (null != servers[j].getRequirepass()
&& !"".equals(servers[j].getRequirepass().trim()))
jds.setPassword(decryption(servers[j].getRequirepass()));
readShards.add(jds); }
} else if (servers[j].getBelongGroup().equalsIgnoreCase(
String.valueOf(serverGroupCodeList.get(i)))
&& servers[j].getUseType().equalsIgnoreCase(
String.valueOf(RedisConstants.WRITE_ONLY_CONNECTION))) {
Boolean connectionTest =
TestConnection(servers[j].getServerIp(), (int) servers[j].getServerPort(),
decryption(servers[j].getRequirepass()), servers[j].getBelongGroup());
if (connectionTest) {
JedisShardInfo jds =
new JedisShardInfo(servers[j].getServerIp(), (int) servers[j].getServerPort());
if (null != servers[j].getRequirepass()
&& !"".equals(servers[j].getRequirepass().trim()))
jds.setPassword(decryption(servers[j].getRequirepass()));
writeShards.add(jds);
}
} else if (servers[j].getBelongGroup().equalsIgnoreCase(
String.valueOf(serverGroupCodeList.get(i)))
&& servers[j].getUseType().equalsIgnoreCase(
String.valueOf(RedisConstants.PERSIST_ONLYL_CONNECTION))) {
Boolean connectionTest =
TestConnection(servers[j].getServerIp(), (int) servers[j].getServerPort(),
decryption(servers[j].getRequirepass()), servers[j].getBelongGroup());
if (connectionTest) {
JedisShardInfo jds =
new JedisShardInfo(servers[j].getServerIp(), (int) servers[j].getServerPort());
if (null != servers[j].getRequirepass()
&& !"".equals(servers[j].getRequirepass().trim()))
jds.setPassword(decryption(servers[j].getRequirepass()));
persistShards.add(jds);
}
}
} // 构造池 每个组分别对应三个分片池,可读分片池,可写分片池,持久化分片池
readConnectionPools[i] =
new ShardedJedisPool(config, readShards, Hashing.MURMUR_HASH,
Sharded.DEFAULT_KEY_TAG_PATTERN);
writeConnectionPools[i] =
new ShardedJedisPool(config, writeShards, Hashing.MURMUR_HASH,
Sharded.DEFAULT_KEY_TAG_PATTERN);
persistConnectionPools[i] =
new ShardedJedisPool(config, persistShards, Hashing.MURMUR_HASH,
Sharded.DEFAULT_KEY_TAG_PATTERN);
//按照组名 分别存可读池,可写池,持久化池于对应的map中【此处如果拿到一个可读池,依然可以做写操作】
readPoolMap.put(serverGroupCodeList.get(i), readConnectionPools[i]);
writePoolMap.put(serverGroupCodeList.get(i), writeConnectionPools[i]);
persistPoolMap.put(serverGroupCodeList.get(i), persistConnectionPools[i]);
}
long end = System.nanoTime();
log.debug("初始化连接池用时:" + (end - start)); }

释放回连接池和销毁连接

  /**
* 根据组将连接释放,重新放回连接池,解决多个线程频繁取获连接导致连接池连接数不够用的问题
*
* @para gourp 组名
* @para connectionType 连接类型:1.读2.写
*/
public static void releaseConnection(ShardedJedis jedis, String group, int connectionType)
throws Exception {
try {
ShardedJedisPool rp = null;
if (connectionType == RedisConstants.READ_ONLY_CONNECTION) {
rp = (ShardedJedisPool) readPoolMap.get(group.toUpperCase());
} else if (connectionType == RedisConstants.WRITE_ONLY_CONNECTION) {
rp = (ShardedJedisPool) writePoolMap.get(group.toUpperCase());
} else if (connectionType == RedisConstants.PERSIST_ONLYL_CONNECTION) {
rp = (ShardedJedisPool) persistPoolMap.get(group.toUpperCase());
}
rp.returnResource(jedis);
} catch (Exception ex) {
log.error("释放连接debug");
}
} /**
* 销毁连接 连接超时以后不能只是释放连接,需要销毁,否则下次使用会取到上次的结果,出现类型转换出错的问题。
*
* @param jedis
* @param group
* @param connectionType
* @throws Exception
*/
public static void destoryConnection(ShardedJedis jedis, String group, int connectionType)
throws Exception {
try {
ShardedJedisPool rp = null;
if (connectionType == RedisConstants.READ_ONLY_CONNECTION) {
rp = (ShardedJedisPool) readPoolMap.get(group.toUpperCase());
} else if (connectionType == RedisConstants.WRITE_ONLY_CONNECTION) {
rp = (ShardedJedisPool) writePoolMap.get(group.toUpperCase());
} else if (connectionType == RedisConstants.PERSIST_ONLYL_CONNECTION) {
rp = (ShardedJedisPool) persistPoolMap.get(group.toUpperCase());
}
rp.returnBrokenResource(jedis);
} catch (Exception ex) {
log.error("销毁连接debug");
}
}

测试连接,可用于故障隔离

  /**
* 初始化时调用,用于初始化set初始值,并测试连通性
* @param host
* @param port
* @param passwd
* @param group
* @return
*/
private static Boolean TestConnection(String host, int port, String passwd, String group) {
Boolean rtn = Boolean.FALSE;
JedisShardInfo jsd = new JedisShardInfo(host, port);
if (null != passwd && !"".equals(passwd.trim()))
jsd.setPassword(passwd);
Jedis jd = null;
try {
jd = new Jedis(jsd);
jd.set(group + "AILK_REDIS_CONNECT_TEST", "TRUE");
rtn = Boolean.TRUE;
} catch (Exception ex) {
if (log.isDebugEnabled()) {
log.error("【调试】," + host + ":" + port + "拒绝连接!" + ex.getMessage());
}
} finally {
if (null != jd)
jd.disconnect();
}
return rtn;
} /**
* 故障隔离调用,通过是否能够get值来判断
* @param host
* @param port
* @param passwd
* @param group
* @return
*/
private static Boolean isConnectioned(String host, int port, String passwd, String group) {
Boolean rtn = Boolean.FALSE;
JedisShardInfo jsd = new JedisShardInfo(host, port);
if (null != passwd && !"".equals(passwd.trim()))
jsd.setPassword(passwd);
Jedis jd = null;
try {
jd = new Jedis(jsd);
String ailkRedisConnectTest = jd.get(group + "AILK_REDIS_CONNECT_TEST");
if (ailkRedisConnectTest != null && ailkRedisConnectTest.equalsIgnoreCase("true"))
rtn = Boolean.TRUE;
} catch (Exception ex) {
if (log.isDebugEnabled()) {
log.error("【调试】," + host + ":" + port + "拒绝连接!" + ex.getMessage());
}
} finally {
if (null != jd)
jd.disconnect();
}
return rtn;
}

redis读写分离及可用性设计的更多相关文章

  1. Redis读写分离技术解析

    背景 云数据库Redis版不管主从版还是集群规格,replica作为备库不对外提供服务,只有在发生HA的时候,replica提升为master后才承担读写流量.这种架构读写请求都在master上完成, ...

  2. Redis读写分离(三)

    1.redis高并发跟整个系统的高并发之间的关系 redis,要搞高并发的话,不可避免,要把底层的缓存搞得很好 mysql,高并发,做到了,那么也是通过一系列复杂的分库分表,订单系统,事务要求的,QP ...

  3. Redis读写分离的简单配置

    Master进行写操作,可能只需要一台Master.进行写操作,关闭数据持久化. Slave进行读操作,可能需要多台Slave.进行读操作,打开数据持久化. 假设初始配置有Master服务器为A,sl ...

  4. redis 主从复制+读写分离+哨兵

    1.redis读写分离应用场景 当数据量变得庞大的时候,读写分离还是很有必要的.同时避免一个redis服务宕机,导致应用宕机的情况,我们启用sentinel(哨兵)服务,实现主从切换的功能.redis ...

  5. 在 Istio 中实现 Redis 集群的数据分片、读写分离和流量镜像

    Redis 是一个高性能的 key-value 存储系统,被广泛用于微服务架构中.如果我们想要使用 Redis 集群模式提供的高级特性,则需要对客户端代码进行改动,这带来了应用升级和维护的一些困难.利 ...

  6. Redis的读写分离

    1.概述 随着企业业务的不断扩大,请求的并发量不断增长,Redis可能终会出现无法负载的情况,此时我们就需要想办法去提升Redis的负载能力. 读写分离(主从复制)是一个比较简单的扩展方案,使用多台机 ...

  7. 读写分离子系统 - C# SQL分发子系统 - Entity Framework支持

    A2D Framework增加了EF支持,加上原先支持ADO.NET: 支持EF方式 支持ADO.NET方式 这次来讲如何让Entity Framework变成nb的读写分离 1. 先设计EF模型, ...

  8. DB层面上的设计 分库分表 读写分离 集群化 负载均衡

    第1章  引言 随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题.对于一个大型的 互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载.对于系统的稳定性和扩展性造成了极大的 ...

  9. Redis哨兵模式(sentinel)学习总结及部署记录(主从复制、读写分离、主从切换)

    Redis的集群方案大致有三种:1)redis cluster集群方案:2)master/slave主从方案:3)哨兵模式来进行主从替换以及故障恢复. 一.sentinel哨兵模式介绍Sentinel ...

随机推荐

  1. STL源码剖析:仿函数

    仿函数就是函数对象 函数对象: 重载了operator()的类对象 使用起来和普通函数一致,所以称为函数对象或是仿函数 STL中对于仿函数的参数进行了特殊处理,定义了两个特殊类,类里面只有类型定义 一 ...

  2. 【原创】xenomai内核解析--同步互斥机制(一)--优先级倒置

    版权声明:本文为本文为博主原创文章,转载请注明出处.如有错误,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 目录 一.xenomai 资源管理简要 二.优先级倒 ...

  3. ST表解决RMQ问题

    RMQ问题: RMQ(Range Minimum/Maximum Query),区间最值查询.对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间 ...

  4. 花了一个月的时间在一个oj网站只刷了这些题,从此入门了绝大多数算法

    如果你想入门算法,那么我这篇文章也许可以帮到你. oj网站有这么多,当然还有其他的.我当初是在hdu上面刷的,不要问我为什么,问就是当时我也是一个新手,懵懵懂懂就刷起来了.点这里可以进入这个网站htt ...

  5. HTML学习汇总

    HTML学习大汇总 (1)HTML概述 Html(超文本标记语言): 用文字来描述的标签语言,用文字来描述网页的一种语言. HTML是 HyperText Mark-up Language 的首字母简 ...

  6. matplotlib图表介绍

    Matplotlib 是一个python 的绘图库,主要用于生成2D图表. 常用到的是matplotlib中的pyplot,导入方式import matplotlib.pyplot as plt 一. ...

  7. Git文件合并

    两个分支:主分支master,分支pre 1.将pre分支文件合并到master分支: 切换到master分支下操作: 合并文件夹[如果是文件则为a.text b.text]: git checkou ...

  8. Python游戏编程入门 中文pdf扫描版|网盘下载内附地址提取码|

    Python是一种解释型.面向对象.动态数据类型的程序设计语言,在游戏开发领域,Python也得到越来越广泛的应用,并由此受到重视. 本书教授用Python开发精彩游戏所需的[]为重要的该你那.本书不 ...

  9. 高阶NumPy知识图谱-《利用Python进行数据分析》

    所有内容整理自<利用Python进行数据分析>,使用MindMaster Pro 7.3制作,emmx格式,源文件已经上传Github,需要的同学转左上角自行下载或者右击保存图片. 其他章 ...

  10. Python time asctime()方法

    描述 Python time asctime() 函数接受时间元组并返回一个可读的形式为"Tue Dec 11 18:07:14 2008"(2008年12月11日 周二18时07 ...