Redis实践—全国地址信息缓存

一、背景

在涉及全国地址的应用中,地址信息通常被频繁地查询和使用,例如电商平台、物流系统等。为了提高系统性能和减少对数据库的访问压力,可以使用缓存来存储常用的地址信息,其中 Redis 是一个非常流行的选择。

本次在一个企业入驻场场景中,需要选择企业服务区域,用户经常需要查询和使用全国省市地址信息(如下所示)。

如果每次查询都直接访问数据库,会增加数据库的负载,尤其是在高并发情况下。相较于其他数据,地址信息相对稳定,通常不会频繁变动。通过缓存常用的地址信息,可以加快查询速度,提高系统性能。

二、设计

数据库:字段及数据如下(需要sql文件,可私信联系)

Redis:使用List数据类型,把每条地址对象转换为json格式,存到Redis。

避免数据库更新,而缓存是老数据,导致数据不一致。设置过期时间7天,超过7天删除缓存,查询最新库中数据

三、代码

controller

Java 复制代码
@RestController
@RequestMapping("/pre_cook/client/address")
public class AddressClientController {

    @Resource
    private AddressClientService addressClientService;


    /**
     * 全国地址查询
     *
     * @return
     */
    @PostMapping("/list")
    public List<AddressVO> addressList() {
        return addressClientService.addressList();
    }


}

impl实现类

Java 复制代码
@Override
    public List<AddressVO> addressList() {

        String addressKey = AddressEnum.Address_PREFIX.getValue();
        Boolean exist = redisUtil.hasKey(addressKey);

        //1.如果缓存有数据,取缓存数据
        if (exist) {
            List<String> jsonList = redisUtil.lRange(addressKey, 0, -1);
            log.info("缓存查询地址信息-json格式:{}", jsonList);

            List<AddressVO> addressVOS = jsonList.stream()
                    .map(json -> JSON.parseObject(json, AddressVO.class))
                    .collect(Collectors.toList());
            return addressVOS;
        }


        //2.缓存无数据,查询数据库
        List<AddressInfoDO> infoDOList = addressInfoService.lambdaQuery()
                .in(AddressInfoDO::getLevel, CompanyConstant.COUNTRY_LEVEL, CompanyConstant.PROVINCE_LEVEL, CompanyConstant.CITY_LEVEL, CompanyConstant.DISTRICT_LEVEL)
                .eq(AddressInfoDO::getStatus, CompanyConstant.ADDRESS_ENABLED)
                .eq(AddressInfoDO::getEnableFlag, EnableFlagEnum.ENABLE.getCode())
                .list();

        List<AddressVO> addressVOS = infoDOList.stream().
                map(e -> AddressVO.builder()
                        .id(e.getId())
                        .addressCode(e.getCode())
                        .addressName(e.getName())
                        .level(e.getLevel())
                        .parentCode(e.getParentCode())
                        .key(PinYinUtils.getStringFirstName(e.getName()))
                        .build()
                ).collect(Collectors.toList());


        List<AddressVO> voList = addressVOS.stream()
                .sorted(Comparator
                        .comparingInt(AddressVO::getLevel)
                        .thenComparing(AddressVO::getKey, (s1, s2) -> s1.compareToIgnoreCase(s2)))
                .collect(Collectors.toList());

        //2.2存入缓存
        List<String> jsonList = voList.stream()
                .map(AddressVO -> JSON.toJSONString(AddressVO))
                .collect(Collectors.toList());

        redisUtil.lRightPushAll(addressKey, jsonList);
        redisUtil.expire(addressKey, DigitalConstant.SEVEN, TimeUnit.DAYS);

        return voList;
    }

RedisUtil​​工具类方法

Java 复制代码
    /**
     * 获取列表指定范围内的元素
     *
     * @param key   key
     * @param start 开始位置, 0是开始位置
     * @param end   结束位置, -1返回所有
     * @return
     */
    public List<String> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }



    /**
     * @param key   key
     * @param value val
     * @return
     */
    public Long lRightPushAll(String key, Collection<String> value) {
        return redisTemplate.opsForList().rightPushAll(key, value);
    }



    /**
     * 设置过期时间
     *
     * @param key     key
     * @param timeout
     * @param unit
     * @return
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

四、测试

第一次,查数据库,耗时4秒多

第二次,通过第一次查询,redis已存有数据,只需要200多毫秒

第三次,耗时200多毫秒

第四次,删除缓存,再次查库,耗时700多毫秒

通过上述测试,第一次查询Mysql数据库,耗时4秒多,后续查询耗时700多毫秒,说明MySQL 也有自身的缓存机制,其中包括查询缓存。

由于查询缓存的存在,第一次执行某个查询时可能会比较慢,因为需要执行实际的查询操作并将结果存入缓存中。但是,当相同的查询再次执行时,如果查询的条件和数据没有发生变化,就可以直接从查询缓存中获取结果,因此查询时间会明显减少。

这里使用Redis缓存,在首次从Mysql查询后,存入Redis。通过Redis查询,耗时只需200多毫秒,明显少于Mysql耗时,减轻了数据库压力,也可以支持更高的并发。

相关推荐
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭10 分钟前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
工业甲酰苯胺10 分钟前
分布式系统架构:服务容错
数据库·架构
独行soc1 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍08-基于时间延迟的SQL注入(Time-Based SQL Injection)
数据库·sql·安全·渗透测试·漏洞挖掘
White_Mountain1 小时前
在Ubuntu中配置mysql,并允许外部访问数据库
数据库·mysql·ubuntu
Code apprenticeship1 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存
百度智能云技术站2 小时前
广告投放系统成本降低 70%+,基于 Redis 容量型数据库 PegaDB 的方案设计和业务实践
数据库·redis·oracle
装不满的克莱因瓶2 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
梦想平凡3 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
TianyaOAO3 小时前
mysql的事务控制和数据库的备份和恢复
数据库·mysql
fpcc4 小时前
跟我学c++中级篇——C++中的缓存利用
c++·缓存