一主多从结构
在这个架构中,将数据库的读请求和写请求进行分离。数量众多的读请求都分配到从数据库上,主数据库只负责写请求。从库保持主动和主库保持同步。这个架构比上一个有了很大的改进,一般的互联网应用。这个架构就能够很好的支持了。他的一个缺点是比较复杂,主从库之间保持高效实时,或者准实时的同步是一个不容易做到的事情。所以我们有了另一个思路,采用一个缓存服务器来存储热点数据,而关系数据用来存储持久化的数据。结构如下图所示
[图片上传失败…(image-6c743c-1595918224482)]
采用缓存服务器读的架构
[图片上传失败…(image-6de251-1595918224482)]
采用缓存服务器读的架构
在这个架构中,当读取数据的时候,先从缓存服务器中获取数据,如果获取调,则直接返回该数据。如果没有获取调,则从数据库中获取数据。获取到后,将该数据缓存到换出数据库中,供下次访问使用。当插入或者更新数据的时候,先将数据写入到关系数据库中,然后再更新缓存数据库中的数据。
当然了,为了应付更大规模的访问量,我们还可以将上面两个改进的架构组合起来使用,既有读写分离的关系数据库,又有可以高速访问的缓存服务。
以上缓存服务器架构的前提就是从缓存服务器中获取数据的效率大大高于从关系型数据库中获取的效率。否则缓存服务器就没有任何意义了。redis 的数据是保存在内存中的,能够保证从 redis 中获取数据的时间效率比从关系数据库中获取高出很多。
顺便给大家推荐一个Java技术交流群:473984645里面会分享一些资深架构师录制的视频资料:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多!
基于 redis 缓存服务的实现
【程序员|Java详解(如何实现一个-redis-缓存服务,牛客视频面试)】这一章节用一个实例来说明如何来在 Java 中实现一个 redis 的缓存服务。
建立 maven 工程并引入依赖
**定义接口类com.x9710.common.redis.CacheServ
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》ice**
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
在这个接口类中,主要定了下面的接口
· void putObject(String key, Object value);
· void putObject(String key, Object value, int expiration);
· Object pullObject(String key);
· Long ttl(String key);
· boolean delObject(String key);
· boolean expire(String key, int expireSecond);
· void clearObject();
顺便给大家推荐一个Java技术交流群:473984645里面会分享一些资深架构师录制的视频资料:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多!
这些接口分别用于存储不过期的对象、存储将来过期对象、获取缓存对象、获取缓存对象剩余存活时间、删除缓存对象、设置缓存对象过期时间、清除所有缓存对象的功能
package com.x9710.common.redis;
/**
- 缓存服务接口
public interface CacheService {
/*
- 将对象存放到缓存中
- @param key 存放的key
- @param value 存放的值
/
void putObject(String key, Object value);
/* - 将对象存放到缓存中
- @param key 存放的key
- @param value 存放的值
- @param expiration 过期时间,单位秒
/
void putObject(String key, Object value, int expiration);
/* - 从缓存中获取对象
- @param key 要获取对象的key
- @return 如果存在,返回对象,否则,返回null
/
Object pullObject(String key);
/* - 给缓存对象设置过期秒数
- @param key 要获取对象的key
- @param expireSecond 过期秒数
- @return 如果存在,返回对象,否则,返回null
/
boolean expire(String key, int expireSecond);
/* - 获取缓存对象过期秒数
- @param key 要获取对象的key
- @return 如果对象不存在,返回-2,如果对象没有过期时间,返回-1,否则返回实际过期时间
/
Long ttl(String key);
/* - 从缓存中删除对象
- @param key 要删除对象的key
- @return 如果出现错误,返回 false,否则返回true
/
boolean delObject(String key);
/* - 从缓存中清除对象
*/
void clearObject();
}
所有要保存到 redis 数据库中的对象需要先序列号为二进制数组,这个类的作用是将 Java 对象序列号为二级制数组或者将二级制数组反序列化为对象。
package com.x9710.common.redis;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
- 对象序列化工具类
public class SerializeUtil {
/*
- 将一个对象序列化为二进制数组
- @param object 要序列化的对象,该必须实现java.io.Serializable接口
- @return 被序列化后的二进制数组
/
public static byte[] serialize(Object object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/* - 将一个二进制数组反序列化为一个对象。程序不检查反序列化过程中的对象类型。
- @param bytes 要反序列化的二进制数
- @return 反序列化后的对象
*/
public static Object unserialize(byte[] bytes) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
package com.x9710.common.redis.impl;
import com.x9710.common.redis.CacheService;
import com.x9710.common.redis.RedisConnection;
import com.x9710.common.redis.SerializeUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import redis.clients.jedis.Jedis;
/**
- 缓存服务 redis 实现类
public class CacheServiceRedisImpl implements CacheService {
private static Log log = LogFactory.getLog(CacheServiceRedisImpl.class);
private RedisConnection redisConnection;
private Integer dbIndex;
public void setRedisConnection(RedisConnection redisConnection) {
this.redisConnection = redisConnection;
}
public void setDbIndex(Integer dbIndex) {
this.dbIndex = dbIndex;
}
public void putObject(String key, Object value) {
putObject(key, value, -1);
}
public void putObject(String key, Object value, int expiration) {
Jedis jedis = null;
try {
jedis = redisConnection.getJedis();
jedis.select(dbIndex);
if (expiration > 0) {
jedis.setex(key.getBytes(), expiration, SerializeUtil.serialize(value));
} else {
jedis.set(key.getBytes(), SerializeUtil.serialize(value));
}
} catch (Exception e) {
log.warn(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
}
public Object pullObject(String key) {
log.trace("strar find cache with " + key);
Jedis jedis = null;
try {
jedis = redisConnection.getJedis();
jedis.select(dbIndex);
byte[] result = jedis.get(key.getBytes());
if (result == null) {
log.trace("can not find caceh with " + key);
return null;
} else {
log.trace("find cache success with " + key);
return SerializeUtil.unserialize(result);
}
} catch (Exception e) {
log.warn(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
return null;
}
public boolean expire(String key, int expireSecond) {
log.trace("strar set expire " + key);
推荐阅读
- java|JAVA面试题 --- Redis篇(一)
- 手撕Gateway源码,今日撕工作流程、负载均衡源码
- java|SpringBoot+Redis搜索栏热搜过滤
- spring|Spring Boot + Redis 去除搜索栏热搜、不雅文字过滤功能
- 算法|SpringBoot+Redis 实现一个微博热搜!
- redis|springboot java+redis 实现简单实用的搜索栏热搜功能,不雅文字过滤功能。
- ES2018 最新 理解Javascript中的执行上下文和执行栈
- Java 中的对象池实现
- 反编译|手把手教你写JAVA反序列化的POC