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耗时,减轻了数据库压力,也可以支持更高的并发。

相关推荐
一眼万年0415 分钟前
Redis Cluster模式
redis·微服务
丶意冷28 分钟前
mybatisPlus分页方言设置错误问题 mybatisPlus对于Oceanbase的Oracle租户分页识别错误
java·数据库·oracle·oceanbase
deriva2 小时前
.netcore+ef+redis+rabbitmq+dotcap先同步后异步再同步的方法,亲测有效
redis·rabbitmq·.netcore
时序数据说2 小时前
为什么时序数据库IoTDB选择Java作为开发语言
java·大数据·开发语言·数据库·物联网·时序数据库·iotdb
戒不掉的伤怀2 小时前
【Navicat 连接MySQL时出现错误1251:客户端不支持服务器请求的身份验证协议;请考虑升级MySQL客户端】
服务器·数据库·mysql
cv高级工程师YKY2 小时前
服务器 - - QPS与TPS介绍
数据库
nbsaas-boot3 小时前
高可扩展属性建模设计:架构师的全局思考与落地方案
数据库
爱上语文3 小时前
Redis基础(5):Redis的Java客户端
java·开发语言·数据库·redis·后端
陈敬雷-充电了么-CEO兼CTO4 小时前
推荐算法系统系列>推荐数据仓库集市的ETL数据处理
大数据·数据库·数据仓库·数据挖掘·数据分析·etl·推荐算法
MeshddY4 小时前
(超详细)数据库项目初体验:使用C语言连接数据库完成短地址服务(本地运行版)
c语言·数据库·单片机