三级缓存
nginx本地缓存 + redis分布式缓存 + tomcat堆缓存
时效要求高的数据如库存等,redis\数据库双写
商品基本信息时效性要求不高,异步
nginx + lua脚本,作为第一层
nginx -> redis -> tomcat
nginx缓存是为了抗热数据,内存有限
redis缓存抗海量离散数据
tomcat堆缓存是抗redis宕机的场景
cache aside pattern
读数据先读缓存
更新时先删除缓存,再更新数据库
删除而非更新的原因是,缓存有大量冷数据,频繁更新没有必要
双写一致性
写数据库删除redis缓存,可能数据不一致,如数据库修改成功,但缓存删除失败
初级解决思路,先删除缓存,再修改数据库
高并发场景,更新库存数据库与读取缓存并发发生,读取数据加载到缓存,数据库修改后与缓存不一致
解决思路,队列串行化,商品id hash取值对内存队列数量取模,路由到队列,上述读请求进入队列
优化点,同一数据短时间进入多个读请求,可以做排重优化,设置hang时长
缓存是空的,如果队列中没有更新操作即数据库中没有,则没必要hang
如果一个内存队列积压多个商品更新操作,会导致长时阻塞,需根据实际情况压测,加机器解决
如果读和写操作路由到不同的内存队列,也会不一致
可以使用nginx hash路由的功能
热点商品路由问题,某商品读写请求特别高,全部打到相同机器,可能导致某台机器压力过大
linux部署mysql
yum install -y mysql-server
service mysqld start
chkconfig mysqld on
yum install -y mysql-connector-java
spring boot整合jedis
依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
入口类注入bean
@Bean
public JedisCluster JedisClusterFactory() {
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
jedisClusterNodes.add(new HostAndPort("192.168.68.103", 7005));
jedisClusterNodes.add(new HostAndPort("192.168.68.101", 7002));
jedisClusterNodes.add(new HostAndPort("192.168.68.103", 7007));
jedisClusterNodes.add(new HostAndPort("192.168.68.102", 7003));
JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes);
return jedisCluster;
}
redis测试类
@Repository
public class RedisDaoImpl implements RedisDao {
@Resource
private JedisCluster jedisCluster;
@Override
public void set(String key, String value) {
jedisCluster.set(key, value);
}
@Override
public String get(String key) {
return jedisCluster.get(key);
}
}
数据库脚本
create database if not exists eshop;
grant all privileges on eshop.* to 'eshop'@'%' identified by 'eshop';
use eshop;
create table user(name varchar(255), age int)
insert into user values('zhangsan', 25)
mapper service controller略.
内存队列实现
内存队列-RequestQueue
/**
* 请求的内存队列
*/
public class RequestQueue {
private List<ArrayBlockingQueue<Request>> queues = new ArrayList<>();
private static class Singleton {
private static RequestQueue instance;
static {
instance = new RequestQueue();
}
public static RequestQueue getInstance() {
return instance;
}
}
public static RequestQueue getInstance() {
return RequestQueue.Singleton.getInstance();
}
public void addQueue(ArrayBlockingQueue<Request> queue) {
this.queues.add(queue);
}
}
工作线程-RequestProcessorThread
/**
* 执行请求的工作线程
*/
public class RequestProcessorThread implements Callable {
/**
* 自己监控的内存队列
*/
private ArrayBlockingQueue<Request> queue;
public RequestProcessorThread(ArrayBlockingQueue queue) {
this.queue = queue;
}
@Override
public Boolean call() throws Exception {
while(true) {
break;
}
return true;
}
}
线程池-RequestProcessorThreadPool
public class RequestProcessorThreadPool {
/**
* 线程池
* 实际项目中线程池大小,每个线程监控的内存队列大小可以写到内存队列中
* 此处方便起见写死
*/
private ExecutorService threadPool = Executors.newFixedThreadPool(10);
public RequestProcessorThreadPool() {
RequestQueue requestQueue = RequestQueue.getInstance();
for (int i = 0; i < 10; i++) {
ArrayBlockingQueue<Request> queue = new ArrayBlockingQueue(100);
requestQueue.addQueue(queue);
threadPool.submit(new RequestProcessorThread(queue));
}
}
/**
* 静态内部类单例,绝对线程安全
*/
private static class Singleton {
private static RequestProcessorThreadPool instance;
static {
instance = new RequestProcessorThreadPool();
}
public static RequestProcessorThreadPool getInstance() {
return instance;
}
}
public static RequestProcessorThreadPool getInstance() {
return Singleton.getInstance();
}
public static void init() {
getInstance();
}
}
网友评论