利用Redis进行数据缓存

1. 引言

缓存有啥用?

  1. 降低对数据库的请求,减轻服务器压力
  2. 提高了读写效率

缓存有啥缺点?

  1. 如何保证数据库与缓存的数据一致性问题?
  2. 维护缓存代码
  3. 搭建缓存一般是以集群的形式进行搭建,需要运维的成本

2. 将信息添加到缓存的业务流程

在这里插入图片描述
上图可以清晰的了解Redis在项目中所处的位置,是数据库与客户端之间的一个中间件,也是数据库的保护伞。有了Redis可以帮助数据库进行请求的阻挡,阻止请求直接打入数据库,提高响应速率,极大的提升了系统的稳定性。

3. 实现代码

在这里插入图片描述
下面将根据查询商铺信息来作为背景进行代码书写,具体的流程图如上所示。

3.1 代码实现(信息添加到缓存中)

public static final String SHOPCACHEPREFIX = "cache:shop:";
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // JSON工具
    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public Result queryById(Long id) {
   
        //从Redis查询商铺缓存
        String cacheShop = stringRedisTemplate.opsForValue().get(SHOPCACHEPREFIX + id);

        //判断缓存中数据是否存在
        if (!StringUtil.isNullOrEmpty(cacheShop)) {
   
            //缓存中存在则直接返回
            try {
   
                // 将子字符串转换为对象
                Shop shop = objectMapper.readValue(cacheShop, Shop.class);
                return Result.ok(shop);
            } catch (JsonProcessingException e) {
   
                e.printStackTrace();
            }
        }

        //缓存中不存在,则从数据库里进行数据查询
        Shop shop = getById(id);

        //数据库里不存在,返回404
        if (null==shop){
   
            return Result.fail("信息不存在");
        }
        //数据库里存在,则将信息写入Redis
        try {
   
            String shopJSon = objectMapper.writeValueAsString(shop);
          stringRedisTemplate.opsForValue().set(SHOPCACHEPREFIX+id,shopJSon,30,TimeUnit.MINUTES);
        } catch (JsonProcessingException e) {
   
            e.printStackTrace();
        }
        //返回
        return Result.ok(shop);
    }

3.2 缓存更新策略

数据库与缓存数据一致性问题,当数据库信息修改后,缓存的信息应该如何处理?

内存淘汰 超时剔除 主动更新
说明 不需要自己进行维护,利用Redis的淘汰机制进行数据淘汰 给缓存数据添加TTL 编写业务逻辑,在修改数据库的同时更新缓存
一致性 差劲 一般
维护成本

这里其实是需要根据业务场景来进行选择

  1. 高一致性:选主动更新
  2. 低一致性:内存淘汰和超时剔除

3.3 实现主动更新

此时需要实现数据库与缓存一致性问题,在这个问题之中还有多个问题值得深思

  1. 删除缓存还是更新缓存?
    当数据库发生变化时,我们如何处理缓存中无效的数据,是删除它还是更新它?
    更新缓存:每次更新数据库都更新缓存,无效写操作较多
    删除缓存:更新数据库时删除缓存,查询时再添加缓存
    由此可见,选择删除缓存是高效的。

  2. 如何保证缓存与数据库的操作的同时成功或失败?
    单体架构:单体架构中采用事务解决
    分布式架构:利用分布式方案进行解决

  3. 先删除缓存还是先操作数据库?

在这里插入图片描述
在并发情况下,上述情况是极大可能会发生的,这样子会导致缓存与数据库数据库不一致。
请添加图片描述
先操作数据库,在操作缓存这种情况,在缓存数据TTL刚好过期时,出现一个A线程查询缓存,由于缓存中没有数据,则向数据库中查询,在这期间内有另一个B线程进行数据库更新操作和删除缓存操作,当B的操作在A的两个操作间完成时,也会导致数据库与缓存数据不一致问题。

完蛋!!!两种方案

  • 10
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是一个简单的钉钉程序中写数据缓存的示例: ```java import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.dingtalk.open.client.api.model.corp.CorpUserDetail; import com.dingtalk.open.client.api.model.corp.CorpUserDetailList; import com.dingtalk.open.client.api.service.corp.CorpUserService; import com.dingtalk.open.client.common.SdkInitException; import com.dingtalk.open.client.common.ServiceException; import com.dingtalk.open.client.common.ServiceRuntimeException; import com.dingtalk.open.client.service.ServiceFactory; import com.dingtalk.open.client.service.corp.CorpUserService; import com.dingtalk.open.client.service.corp.impl.CorpUserServiceImpl; import java.util.HashMap; import java.util.List; import java.util.Map; public class DataCache { private static final String CACHE_KEY = "corp_user_detail"; private static CorpUserService corpUserService = null; private static Map<String, Object> cacheMap = new HashMap<>(); static { try { ServiceFactory serviceFactory = ServiceFactory.getInstance(); corpUserService = serviceFactory.getOpenService(CorpUserService.class); } catch (SdkInitException e) { e.printStackTrace(); } } public static CorpUserDetailList getCorpUserDetailList(String corpId, Long departmentId) { String cacheKey = CACHE_KEY + "_" + corpId + "_" + departmentId; if (cacheMap.containsKey(cacheKey)) { return (CorpUserDetailList) cacheMap.get(cacheKey); } CorpUserDetailList corpUserDetailList = new CorpUserDetailList(); try { int offset = 0; int size = 100; while (true) { CorpUserDetailList response = corpUserService.getCorpUserList(corpId, departmentId, offset, size, null); if (response == null || response.getUserlist() == null || response.getUserlist().isEmpty()) { break; } corpUserDetailList.getUserlist().addAll(response.getUserlist()); offset += size; } } catch (ServiceException e) { throw new ServiceRuntimeException(e); } cacheMap.put(cacheKey, corpUserDetailList); return corpUserDetailList; } public static void clearCache() { cacheMap.clear(); } } ``` 这是一个缓存企业微信用户信息的示例,使用了一个静态变量 `cacheMap` 存储缓存数据缓存的 key 为 `CACHE_KEY + "_" + corpId + "_" + departmentId`,其中 `corpId` 和 `departmentId` 分别为企业微信的企业 ID 和部门 ID,`CACHE_KEY` 为缓存 key 的前缀。 `getCorpUserDetailList` 方法用于获取企业微信用户信息列表,如果缓存中已经存在相应的数据,则直接返回缓存数据,否则调用钉钉开放平台提供的接口获取数据,并将数据存入缓存中。 `clearCache` 方法用于清空缓存中的数据。 注意:此示例仅供参考,具体实现需要根据实际需求进行调整。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值