mysql有多级缓存,秒杀系统 | 多级查询缓存 | 基于 Guava Cache 的本地热点缓存

本地热点缓存的特点

只有热点数据才能进入本地热点缓存;

本地热点缓存对脏读要非常不敏感,比如商品名称在 MySQL 中的变更,这个变更没有更新到本地缓存中,这是 OK 的,用户下单时的商品名称和用户看到的商品名称不一样,这是 OK 的;

本地热点缓存内存要可控:

当商品信息在 MySQL 中变更时,Redis 中的过期数据只需清除即可;

但对本地热点缓存来说,很少有方法能清除掉 JVM 中的数据的,因为要清除 JVM 中的数据,需要每台应用服务器都清除掉数据才行,可以使用 MQ 做广播消息的发送来实现,但这种操作是得不偿失的;

本地热点缓存的生命周期不会特别长,比 Redis 中 key 的生命周期要短很多,这样才能做到被动失效造成的脏读非常少;

本地热点缓存的设计考量

本地热点缓存的本质其实就是维护一个 HashMap 的数据结构,但维护这个结构是不容易的:

首先要支持并发读写,ConcurrentHashMap 的性能不够理想;

要考虑失效时间和内存容量限制的淘汰机制,比如10分钟自动失效、先进先出、最少访问频次 LRU;

Guava Cache 的特点

Guava Cache 可控制大小和超时时间,本质上也是可支持并发的 HashMap;

Guava Cache 可配置 LRU(最近最少访问的 key 优先被淘汰) 的策略;

Guava Cache 是线程安全的;

基于 Cuava Cache 的本地热点缓存的实现

依赖

com.google.guava

guava

18.0

对本地缓存操作的封装

public interface CacheService {

void setCommonCache(String key, Object value);

Object getFromCommonCache(String key);

}

package com.lixinlei.miaosha.service.impl;

import com.google.common.cache.Cache;

import com.google.common.cache.CacheBuilder;

import com.lixinlei.miaosha.service.CacheService;

import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

import java.util.concurrent.TimeUnit;

@Service

public class CacheServiceImpl implements CacheService {

private Cache commonCache = null;

@PostConstruct

public void init() {

commonCache = CacheBuilder.newBuilder()

.initialCapacity(10)

.maximumSize(100)

.expireAfterWrite(60, TimeUnit.SECONDS).build();

}

@Override

public void setCommonCache(String key, Object value) {

commonCache.put(key, value);

}

@Override

public Object getFromCommonCache(String key) {

return commonCache.getIfPresent(key);

}

}

Controller 层引入本地缓存逻辑

@RequestMapping(value = "/get", method = {RequestMethod.GET})

@ResponseBody

public CommonReturnType get(@RequestParam(name = "id") Integer id) {

ItemModel itemModel = null;

// 先从本地缓存中找

itemModel = (ItemModel)cacheService.getFromCommonCache("item_" + id);

if (itemModel == null) {

// 再从 Redis 中找

itemModel = (ItemModel)redisTemplate.opsForValue().get("item_" + id);

if (itemModel == null) {

// 最后从 MySQL 中找

itemModel = itemService.getItemById(id);

redisTemplate.opsForValue().set("item_" + id, itemModel);

redisTemplate.expire("item_" + id, 10, TimeUnit.MINUTES);

}

cacheService.setCommonCache("item_" + id, itemModel);

}

ItemVO itemVO = this.convertFromItemModel(itemModel);

return CommonReturnType.create(itemVO);

}

引入本地缓存性能提升

相对于 Redis 缓存,在本地缓存全部命中的情况下,TPS 从 2000 提升到 4000 +(2核8G);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值