数仓DWS层之旁路缓存优化

2023-10-10 21:10
文章标签 优化 缓存 数仓 dws 旁路

本文主要是介绍数仓DWS层之旁路缓存优化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 优化原因:

外部数据源的查询常常是流式计算的性能瓶颈。以本程序为例,每次查询都要连接 Hbase,数据传输需要做序列化、反序列化,还有网络传输,严重影响时效性。可以通过旁路缓存对查询进行优化。

旁路缓存模式是一种非常常见的按需分配缓存模式。所有请求优先访问缓存,若缓存命中,直接获得数据返回给请求者。如果未命中则查询数据库,获取结果后,将其返回并写入缓存以备后续请求使用。

(1)旁路缓存策略应注意两点

a)缓存要设过期时间,不然冷数据会常驻缓存,浪费资源。

b)要考虑维度数据是否会发生变化,如果发生变化要主动清除缓存

(2)缓存的选型

一般两种:堆缓存或者独立缓存服务(memcache,redis)

堆缓存,性能更好,效率更高,因为数据访问路径更短。但是难于管理,其它进程无法维护缓存中的数据。

独立缓存服务(redis,memcache),会有创建连接、网络IO等消耗,较堆缓存略差,但性能尚可。独立缓存服务便于维护和扩展,对于数据会发生变化且数据量很大的场景更加适用,此处选择独立缓存服务,将 redis 作为缓存介质

(3)实现步骤

从缓存中获取数据。

① 如果查询结果不为 null,则返回结果。

② 如果缓存中获取的结果为 null,则从 Phoenix 表中查询数据。

a)如果结果非空则将数据写入缓存后返回结果。

b)否则提示用户:没有对应的维度数据

注意:缓存中的数据要设置超时时间,本程序设置为 1 天。此外,如果原表数据发生变化,要删除对应缓存。为了实现此功能,需要对维度分流程序做如下修改:

i)在 MyBroadcastFunction的 processElement 方法内将操作类型字段添加到 JSON 对象中。

ii)在 DimUtil 工具类中添加 deleteCached 方法,用于删除变更数据的缓存信息。

iii)在 MyPhoenixSink 的 invoke 方法中补充对于操作类型的判断,如果操作类型为 update 则清除缓存。

图解:

 

 代码方面:

思路:当我们需要使用外部数据源的表数据时,在第一次使用的时候,从Phoenix获取维表数据,并且将这些维表数据写入Redis缓存中,在后面我们需要再次使用维表数据的时候,我们先可以从Redis中获取,如果Redis中没有,在从Phoenix中获取维表数据并且写入Redis缓存中,主要这里要设置缓存过期时间,要不然会造成冷数据,而浪费资源。当我们修改维表中的数据时,要先删除Redis缓存中的数据,然后再对Phoenix进行更新。

(1)创建连接池(与Phoenix建立连接,即与HBASE建立连接)

package com.atguigu.utils;import com.alibaba.druid.pool.DruidDataSource;
import com.atguigu.common.GmallConfig;public class DruidDSUtil {private static DruidDataSource druidDataSource=null;public static DruidDataSource createDataSource() {// 创建连接池druidDataSource = new DruidDataSource();// 设置驱动全类名druidDataSource.setDriverClassName(GmallConfig.PHOENIX_DRIVER);// 设置连接 urldruidDataSource.setUrl(GmallConfig.PHOENIX_SERVER);// 设置初始化连接池时池中连接的数量druidDataSource.setInitialSize(5);// 设置同时活跃的最大连接数druidDataSource.setMaxActive(20);// 设置空闲时的最小连接数,必须介于 0 和最大连接数之间,默认为 0druidDataSource.setMinIdle(1);// 设置没有空余连接时的等待时间,超时抛出异常,-1 表示一直等待druidDataSource.setMaxWait(-1);// 验证连接是否可用使用的 SQL 语句druidDataSource.setValidationQuery("select 1");// 指明连接是否被空闲连接回收器(如果有)进行检验,如果检测失败,则连接将被从池中去除// 注意,默认值为 true,如果没有设置 validationQuery,则报错// testWhileIdle is true, validationQuery not setdruidDataSource.setTestWhileIdle(true);// 借出连接时,是否测试,设置为 false,不测试,否则很影响性能druidDataSource.setTestOnBorrow(false);// 归还连接时,是否测试druidDataSource.setTestOnReturn(false);// 设置空闲连接回收器每隔 30s 运行一次druidDataSource.setTimeBetweenEvictionRunsMillis(30 * 1000L);// 设置池中连接空闲 30min 被回收,默认值即为 30 mindruidDataSource.setMinEvictableIdleTimeMillis(30 * 60 * 1000L);return druidDataSource;}
}

 (二)先判断Redis缓存是否有数据,如果没有,则从Phoenix获取维表数据并且将在Phoenix中查到的数据放入Redis缓存中

package com.atguigu.utils;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.common.GmallConfig;
import redis.clients.jedis.Jedis;import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;public class DimUtil {//启动Redis//   bin/redis-server.sh ./redis.conf//   bin/redis-cli -h hadoop107 --rawpublic static JSONObject getDimInfo(Connection connection,String tableName,String key) throws SQLException, InvocationTargetException, InstantiationException, IllegalAccessException {//先查询RedisJedis jedis = JedisUtil.getJedis();String redisKey="DIM"+tableName+":"+key;String dimJsonStr = jedis.get(redisKey);//如果Redis缓存中有数据,则从缓存中读取数据,如果没有,则从Phoenix(Hbase)中获取数据if(dimJsonStr!=null){//重置过期时间jedis.expire(redisKey,24*60*60);//归还连接jedis.close();//返回维表数据return JSON.parseObject(dimJsonStr);}else{//拼接SQL语句String querySql="select * from " + GmallConfig.HBASE_SCHEMA +"."+tableName+"where id="+ key+"'";System.out.println("querySql>>>"+querySql);//查询数据List<JSONObject> queryList = JdbcUtil.queryList(connection, querySql, JSONObject.class, false);//将从Phoenix查询到的数据写入RedisJSONObject dimInfo = queryList.get(0);jedis.set(redisKey, dimInfo.toJSONString());//设置过期时间jedis.expire(redisKey,24*60*60);//归还连接jedis.close();//返回结果return dimInfo;}}//删除Redis中的缓存数据public static void delDimInfo(String tableName,String key){//获取连接Jedis jedis = JedisUtil.getJedis();//删除数据jedis.del("DIM"+tableName+":"+key);//归还连接jedis.close();}}

(三)当维表数据更新时,需要删除Redis对应的维表数据(删除方法在上一段代码中)

package com.atguigu.app.func;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.utils.DimUtil;
import com.atguigu.utils.DruidDSUtil;
import com.atguigu.utils.PhoenixUtil;import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;import java.sql.SQLException;public class DimSinkFunction extends RichSinkFunction<JSONObject> {private static DruidDataSource druidDataSource=null;@Overridepublic void open(Configuration parameters) throws Exception {druidDataSource = DruidDSUtil.createDataSource();}/*主流数据value数据格式:(消费的topic_db){"database":"gmall-211126-flink","table":"base_trademark","type":"insert","ts":1652499161,"xid":167,"commit":true,"data":{"id":13,"tm_name":"atguigu","logo_url":"/aaa/aaa"}}*/@Overridepublic void invoke(JSONObject value, Context context) throws Exception {//获取连接DruidPooledConnection connection = druidDataSource.getConnection();String sinkTable=value.getString("sinkTable");JSONObject data=value.getJSONObject("data");//获取数据类型String type=value.getString("type");//如果为更新类型,则需要删除Redis中的数据if("update".equals(type)){DimUtil.delDimInfo(sinkTable.toUpperCase(),data.getString("id"));}//写出数据PhoenixUtil.upsertValues(connection,sinkTable,data);//归还连接connection.close();}
}

这篇关于数仓DWS层之旁路缓存优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/183110

相关文章

SpringBoot中HTTP连接池的配置与优化

《SpringBoot中HTTP连接池的配置与优化》这篇文章主要为大家详细介绍了SpringBoot中HTTP连接池的配置与优化的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、HTTP连接池的核心价值二、Spring Boot集成方案方案1:Apache HttpCl

PyTorch高级特性与性能优化方式

《PyTorch高级特性与性能优化方式》:本文主要介绍PyTorch高级特性与性能优化方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、自动化机制1.自动微分机制2.动态计算图二、性能优化1.内存管理2.GPU加速3.多GPU训练三、分布式训练1.分布式数据

MySQL中like模糊查询的优化方案

《MySQL中like模糊查询的优化方案》在MySQL中,like模糊查询是一种常用的查询方式,但在某些情况下可能会导致性能问题,本文将介绍八种优化MySQL中like模糊查询的方法,需要的朋友可以参... 目录1. 避免以通配符开头的查询2. 使用全文索引(Full-text Index)3. 使用前缀索

C#实现高性能Excel百万数据导出优化实战指南

《C#实现高性能Excel百万数据导出优化实战指南》在日常工作中,Excel数据导出是一个常见的需求,然而,当数据量较大时,性能和内存问题往往会成为限制导出效率的瓶颈,下面我们看看C#如何结合EPPl... 目录一、技术方案核心对比二、各方案选型建议三、性能对比数据四、核心代码实现1. MiniExcel

Spring三级缓存解决循环依赖的解析过程

《Spring三级缓存解决循环依赖的解析过程》:本文主要介绍Spring三级缓存解决循环依赖的解析过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、循环依赖场景二、三级缓存定义三、解决流程(以ServiceA和ServiceB为例)四、关键机制详解五、设计约

Redis中6种缓存更新策略详解

《Redis中6种缓存更新策略详解》Redis作为一款高性能的内存数据库,已经成为缓存层的首选解决方案,然而,使用缓存时最大的挑战在于保证缓存数据与底层数据源的一致性,本文将介绍Redis中6种缓存更... 目录引言策略一:Cache-Aside(旁路缓存)策略工作原理代码示例优缺点分析适用场景策略二:Re

MySQL索引的优化之LIKE模糊查询功能实现

《MySQL索引的优化之LIKE模糊查询功能实现》:本文主要介绍MySQL索引的优化之LIKE模糊查询功能实现,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前缀匹配优化二、后缀匹配优化三、中间匹配优化四、覆盖索引优化五、减少查询范围六、避免通配符开头七、使用外部搜索引擎八、分

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

SpringBoot首笔交易慢问题排查与优化方案

《SpringBoot首笔交易慢问题排查与优化方案》在我们的微服务项目中,遇到这样的问题:应用启动后,第一笔交易响应耗时高达4、5秒,而后续请求均能在毫秒级完成,这不仅触发监控告警,也极大影响了用户体... 目录问题背景排查步骤1. 日志分析2. 性能工具定位优化方案:提前预热各种资源1. Flowable

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N