IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 关于缓存细粒度化后的查询优化 -> 正文阅读

[大数据]关于缓存细粒度化后的查询优化

?高性能MYSQL中有这部分的描述。此处主要记录下细粒度后结合持久型DB查询的实现问题。

场景为普通的对象查询,定义一个接口方法,目的是查询id列表对应的映射数据,key为id(唯一标识即可),value为对应值对象

Map<Long, DemoDTO> list(Set<Long> idSet);

?定义好一个通用方法

/**
 * 结合MYSQL的管道查询
 *
 * @param targetSet         目标集合
 * @param dbResultMapGetter 未命中缓存时,从MYSQL获取数据映射
 * @param cacheKeyPrefix    缓存前缀
 * @param objectNull        避免缓存穿透时null值默认的空对象
 * @param <K>               map result key
 * @param <V>               map result value
 * @return 最终查询结果映射
 */
<K, V> Map<K, V> pGetCombineDb(Set<K> targetSet, DbResultMapGetter<K, V>    dbResultMapGetter, String cacheKeyPrefix, V objectNull);

?其中targetSet即为查询的id列表,DbResultMapGetter 定义为从MYSQL等持久化库中获取的未命中缓存的对象映射数据接口,cacheKeyPrefix为定义的缓存前缀,objectNull为避免缓存穿透而缓存的空对象。具体实现代码参考如下:

public interface DbResultMapGetter<K, V> {

    Map<K,V> getDbResult(Set<K> dbSearching);
}
    @SuppressWarnings("unchecked")
    @Override
    public <K, V> Map<K, V> pGetCombineDb(Set<K> targetSet, DbResultMapGetter<K, V> dbResultMapGetter, String cacheKeyPrefix, V objectNull) {
        List<String> keys = targetSet.stream().map(e ->
                RedisUtils.mergeKey(cacheKeyPrefix, e))
                .collect(Collectors.toList());
        List<V> cacheResult = (List<V>) pGet(keys);
        Map<K, V> resultMap = new HashMap<>(targetSet.size());
        Map<K, V> dbResultMap = new HashMap<>();
        boolean allHitCache = putCacheResult(new ArrayList<>(targetSet), cacheResult, resultMap, dbResultMap);
        if (allHitCache) {
            return resultMap;
        }
        // 更新真实的Mysql查询结果
        dbResultMap.putAll(dbResultMapGetter.getDbResult(dbResultMap.keySet()));
        updateMap(resultMap, dbResultMap, objectNull);
        // 缓存mysql查询结果
        List<Tuple2<String, Object>> setCommands = new ArrayList<>(dbResultMap.size());
        dbResultMap.forEach((k, v) -> setCommands.add(new Tuple2<>(
                // 缓存参数拼接策略,一般以:隔开
                RedisUtils.mergeKey(cacheKeyPrefix, k), v)));
        pSet(setCommands);
        return resultMap;
    }

    /**
     * @param targetSet   查询目前列表
     * @param cacheResult 管道查询缓存结果有序列表
     * @param resultMap   最终查询结果
     * @param dbResultMap mysql查询结果
     * @param <K>         map result key
     * @param <V>         map result value
     * @return 是否全部命中缓存
     */
    private <K, V> boolean putCacheResult(List<K> targetSet, List<V> cacheResult, Map<K, V> resultMap,
                                          Map<K, V> dbResultMap) {
        for (int i = 0; i < targetSet.size(); i++) {
            if (cacheResult.get(i) == null) {
                // mysql查询的各个结果先视为空
                dbResultMap.put(targetSet.get(i), null);
            } else {
                resultMap.put(targetSet.get(i), cacheResult.get(i));
            }
        }
        return dbResultMap.keySet().size() == 0;
    }

    /**
     * 更新查询结果
     *
     * @param resultMap   最终查询结果
     * @param dbResultMap mysql查询结果
     * @param objectNull  避免缓存穿透时null值默认的空对象
     * @param <K>         map result key
     * @param <V>         map result value
     */
    private <K, V> void updateMap(Map<K, V> resultMap, Map<K, V> dbResultMap, V objectNull) {
        // 避免缓存穿透
        dbResultMap.replaceAll((k, v) -> {
            if (v == null) {
                return objectNull;
            }
            return v;
        });
        // 更新最终查询结果
        resultMap.putAll(dbResultMap);
    }

?这边以redis的pipeline为例,也可用mGet方式

    public List<Object> pGet(List<String> keys) {
        return stringRedisTemplate.executePipelined((RedisCallback<Object>) redisConnection -> {
            for (String key : keys) {
                redisConnection.get(serializeKey(key));
            }
            return null;
        }, RedisSerializer.json());
    }


    public void pSet(List<Tuple2<String, Object>> kvs) {
        stringRedisTemplate.executePipelined((RedisCallback<Object>) redisConnection -> {
            for (Tuple2<String, Object> kv : kvs) {
                byte[] value = RedisSerializer.json().serialize(kv.getSecond());
                if (value == null) {
                    continue;
                }
                redisConnection.set(serializeKey(getRealKey(kv.getFirst())), value,
                        Expiration.milliseconds(24 * 3600 * 1000L +
                                // 避免同一时间过期较多缓存
                                RandomUtils.nextLong(1, 6 * 3600 * 1000L)),
                        RedisStringCommands.SetOption.UPSERT);
            }
            return null;
        }, RedisSerializer.json());
    }

具体到业务查询时就可以这么使用:

    @Override
    public Map<Long, DemoDTO> list(Set<Long> idSet) {
        return redisExtendService.pGetCombineDb(idSet, dbSearching ->         {
            // 用IN查询之类的把未命中缓存的数据查询出来
            List<DemoDTO> dbResult = demoDao.findAll(dbSearching);
            return dbResult.stream().collect(Collectors.toMap(DemoDTO::getId,demo-> demo));
        }, "demo", new DemoDTO());
    }

?其中,具体的mysql业务查询、缓存key前缀、缓存空对象等就可以似具体情况自行编写了。

这边提供一个思路,有更优雅的实现可以分享下哈。

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-10-22 11:00:15  更:2021-10-22 11:02:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:46:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码