还没有专栏
减轻数据库查询压力建立redis缓存已经是IT业务场景里老生常谈的话题了,我周围的大多方案都是初始化表数据到缓存,建立过期时间,定时跑批,再去库里查询后进行更新,我把它认为被动更新缓存数据,先查库发现不同了或者定时跑批再去跟新热数据有点太被动了,我喜欢主动,我就有了这么个不成熟的想法哈,能不能数据库更新了一条 redis里就随着更新一条,让数据实时保持随着表的变动而更新,经过今天不成熟的实践和测试,感觉可行! 因为我们Web 应用的关系型数据库都已经从MYSQL 迁移到了 Postgresql 或者 Gaussdb ,废话不多说为各位好汉叨咕叨咕我的实现过程和测试后的结果。
PostgreSQL中的 Listen/Notify 是用来提供客户端之间通过服务段进行消息通信的机制。
listen:监听消息通道
unlisten:取消先前的监听
notify:发送消息到消息通道中
pg_notify():与 notify 命令的功能相同,也可以发送消息到消息同道中。
pg_listening_channels():调用此函数可以查询当前 session 已注册了哪些消息监听。
利用PG的这个功能,再加上触发器,去主动告诉外界数据的变化,从而可以触发其它动作,感叹PG的强大,很多国产数据库厂商的内核都是基于PG,那我个人感觉可以用上这个,我之前看到有个国产库厂商推出了Pg_Cron的特性,也很实用,因为在数据对接时我们考虑成本存储问题,是会定时删除掉一个周期之前的数据。
CREATE TABLE "public"."test" (
"id" int4 NOT NULL,
"name" varchar(50) COLLATE "pg_catalog"."default" NOT NULL,
"py_code" varchar(50) COLLATE "pg_catalog"."default",
"seq_no" int4 NOT NULL,
"description" varchar(200) COLLATE "pg_catalog"."default",
CONSTRAINT "pk_zd_business_type" PRIMARY KEY ("id")
)
;
ALTER TABLE "public"."test"
OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."notifyshipment"()
RETURNS "pg_catalog"."trigger" AS $BODY$
DECLARE
BEGIN
PERFORM pg_notify(
CAST('test' AS text),
row_to_json(NEW)::text); #test 是要监听的订阅名称,自行更改就是了
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
这个函数什么意思呢,有懂的,但也有没用过PG或不懂的,我得吧一下,阿门!这个NEW就是表的一行记录的数据,可以理解是个对象,NEW.字段名就出这个属性的值,做类似于实体类哈!新插入一条记录,就以json 的形式推送出去。客户端,也就是我们的代码端去实时监就能拿到
CREATE TRIGGER t_cdc_notify AFTER INSERT OR UPDATE ON test
FOR EACH ROW EXECUTE PROCEDURE notifyshipment();
#大概意思 : 触发器绑定刚才的函数作用到test表上
public void run() {
while (true) {
try {
PGConnection pgConn = (PGConnection) conn.unwrap(PGConnection.class);
PGNotification[] notify = pgConn.getNotifications();
if (null != notify) {
for (int k = 0; k < notify.length; k++) {
String jsonStr = notify[k].getParameter();
JSONObject jsonObj = JSON.parseObject(jsonStr);
System.out.println("------------NEW 记录------------------");
System.out.println(jsonObj.toJSONString());
System.out.println("------------NEW 记录------------------");
// redisRWService.set("a",jsonObj.toJSONString());
//连接本地的 Redis 服务
Jedis jedis = new Jedis("192.168.56.103",6379);
System.out.println("redis连接成功");
//查看服务是否运行
System.out.println("redis服务正在运行: "+jedis.ping());
System.out.println("======================key==========================");
//清除当前数据库所有数据
jedis.flushDB();
//设置键值对
jedis.set("test",jsonObj.toJSONString());
//查看存储的键的总数
System.out.println(jedis.dbSize());
//取出设置的键值对并打印
System.out.println(jedis.get("test"));
}
}
Thread.sleep(300);
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端代码可以使用多线程的方式实时读取队列里推送过来的数据,进行redis的更新存储,我这里是单类启动,pom.xml 使用的是jedis,value没做序列化操作, 没走springBoot自带的redisTemplate模板。只是为了展示下value内容的更新变化。
第一步 启动客户端的监听程序
第二步 往这张表里插入一条测试记录
第三步 查看实时数据
客户端
redis端
第四步 更新表记录
最后一步 查看redis数据是否随着记录的变化马上变化
查看后,redis 端数据已经改变,随着数据库记录的变化,redis数据紧接着就跟着变化。满足了我想要的实时性。
作为一个数据治理从业人员,数据的实时性是很重要的一点需求。也经过了今天休息日的测试呢证明,目前这种初级的设想还行,能满足。也必然存在一些没谈及到的问题,比如说,删除记录数据,是没办法去redis 里马上清除对应key。这样就涉及到了redis端的过期超市设置和跑批任务。
产生这种场景的想法,自己也是设想能解决redis 缓存能随着数据库表记录的主动跟新,从而能去主动触更新缓存数据,达到数据实时性。在俩套系统对接过程中,如果要实时查询对方库里的一张表中记录的最新状态,这种高频次的查询,如果换做我是对方,在考虑负载性能时也不会同意,如果使用今天测试的这种方案,较低了查询负载也实现了双方应用系统原本的健壮性。但,目前这种方案还只是适用于pg内核的数据库。如果是mysql的话或者oracle 还想实现的话,就得用FLINK CDC 实时读取源数据到pg库再去实现。当然,既然用FLINK了,那方法自然就多了!今天的节目就到这了,见笑 见笑~~~
后期会一直更新CSDN,总结工作整理和个人想法测试等,还恳切各位大哥和小仙女们 ,多多提出指导意见,严厉不怕,促使大弟我进步就行!
东北礼仪 ~~~之~~~~~
抱拳了老铁!!!