美文网首页大数据Java
看完这篇,数据同步还不会,还能怪谁

看完这篇,数据同步还不会,还能怪谁

作者: 热衷技术的Java程序员 | 来源:发表于2020-06-17 16:59 被阅读0次

应用开发中,为了提升查询性能或者做服务降级方案时,我们会使用缓存作为解决方案,像分布式缓存方案,比如 Redis、Memcache等;本地缓存方案,比如 Guava、Caffeine等。如果仅仅对当前服务的执行结果的缓存,用于下次相同查询时加快查询效率来说,还相对简单一点。只需要将查询条件作为key,返回的结果作为 value 即可实现,复杂一点会加上缓存失效机制等。

但还有一种可能缓存,可能是需要进行数据的同步的操作的。比如笔者之前做过的用户权限中心,由于对响应实时性方面有很大的要求,虽然使用了异步非阻塞编程方式以提高性能,但如果涉及到数据库的操作,其实性能并不能达到目标值。由于权限的相关配置项通过字节估算,对资源消耗并不算大,因而,笔者考虑使用本地缓存方案实现。

同步方案

做数据同步需要考虑同步方案和数据格式。同步方案常见有主动同步(启动初始化、定时任务)和被动同步(消息通知、回调)两种模式。应用一般会在启动的时候初始化一份基准数据,之后的数据更新都基于这份基准数据进行修改。对数据实时性要求不高的场景,可以通过定时任务方式主动拉取数据,在这种方式中存在全量和增量两种模式。全量是最简单的方案,只需要对原先的缓存进行清空操作,填充最新的数据即可,适合数据量比较小的场景。增量方式相对来说比较复杂,需要依照不同的更新维度做相应的修改。还是拿权限例子来说,一般存在Tenant、AppId、 User、Role、Group、Resource等内容,这里存在层级关系, {User、Role、Group、Resource} 存在于 AppId 下,AppId 又同时存在于 Tenant 中,其中广义上来说 API、Tag、Menu 都是属于 Resource范畴,具体设计这里不进行展开,那么缓存格式可以是这样的:

Tenant -> Appid -> Method -> Path -> UserId -> RoleId

在用户登录的时候,会携带 tenant、appid、user、role 等信息,同时,当前请求的 Method 和 Path 也是可以知晓的。假设用户在配置请求路径 Path 的时候配置错误了,现在需要在后台进行修改,修改之后就会进行数据的同步,我们先不关心用哪种方式触发同步,我们去修改缓存的时候,需要从左到右一层层进行判断,进行修改,这样还不是最麻烦的,麻烦的是上面的每一层级都是一个可以变化的单元,都可能存在新增、修改和删除的情况,是不是想想就会觉得头大了呢。那么有哪些解决方案可以供参考:

全量同步,简单粗暴且高效,但不适合数据量大且获取更新数据比较复杂麻烦的场景

拆分多个缓存,例如 Tenant->Appid->Method->Path->RoleId, Tenant->Appid->Method->Path->UserId(这里只是举例说明,实际并非如此)

简化操作,一般缓存都是存在增删改的操作,这三者中改操作往往是最复杂的一种,如果只有增删会简单很多

再回过来讲一下消息通知的同步方式,消息通知存在 RabbitMQ、RocketMQ、Kafka 等消息中间件解决方案。在一致性方面要求高的场景,可以使用 RabbitMQ 和 RocketMQ,能确保数据量比较大的场景推荐使用 Kafka 方案,毕竟 Kafka 是为大数据而生的。使用消息通知的方式就需要引用消息中间,相对 API 方式来说比较笨重且引入了一个不稳定因素,对于小项目来说得不偿失,同时,如果是公司外部应用,不会提供消息中间件作为数据同步方案。

接着说说回调,这种方式被广泛用于对外业务中,HTTP 或者 HTTPS 方式比较轻量级、接受度高,当然回调这种概念不局限于通讯协议方式,RPC 方式也是可以的。回调方式与消息通知方式进行对比的话,回调需要自行实现幂等和重试机制,在编码方面需要投入更多,这也是大家为什么异步的场景青睐消息队列的原因。

数据格式

数据格式需要结合同步方案和业务要求。如果是增量的方式,需要考虑修改前与修改之后,比如这样:

[

    {

"id":"UUID",

"op":"U", // 操作,U、D

"t":1590730661263, // 时间戳

"prev":{

"id":"XXX", // 更新前ID

"name":"zhansan", // 更新前名称

"time":"1590730661124"// 更新前更新时间

        },

"cur":{

"id":"XXX", // 更新后ID

"name":"lisi", // 更新后名称

"time":"1590730661263"// 更新后时间

        }

    },

    {

    ......

    }

]

全量方式则不需要这么复杂,只要最新结果集即可。同步方案的不同也会存在字段的考量,一般会从幂等性、数据一致性、服务稳定性、可用性、实时性等方面出发。一般我建议:

字段尽可能短

必须有id和时间戳信息

Type 类型字段值,尽可能使用 Int 类型或者短字符串映射,例如上面的op字段使用短字符串方式

一些建议

正向不行,可以试试反向。在设计缓存结构时候,由于人的大脑擅长正向思维,可能设计的结果并不特别的理想(在查询和更新性能方面),这个时候可以考虑反向试试,可能会豁然开朗。Tenant->Appid->Method->Path->UserId 数据格式,在某些场景不如 UserId->Method->Path->Appid->Tenant 。

稳定节点在前,多变的在后。数据量少在前,数据量多在后。 上面的例子中,Tenant 相对比较稳定,变更的比较少且数据量相对于 UserId 肯定比较少。这样在修改或者查找的时候,性能相对好。

空间与时间互换。 这个想必大家经常听到,时间换空间或者空间换时间。对于性能有要求的业务场景,通过冗余缓存方案可以提高查询性能;在资源紧张的场景但对时间有包容性,那适当在实时性方面进行取舍。

不要忽视数据提供方的性能问题。 实时性不仅仅依赖于需要数据的那方或者中间件,数据提供方也是可能存在性能瓶颈的。如果数据的数据格式要求特别变态,需要数据提供方联表查询 3 张表以上,性能可想而知,所以同步的数据要进行取舍,从而节省网络带宽和IO,提升性能。

相关文章

  • 看完这篇,数据同步还不会,还能怪谁

    应用开发中,为了提升查询性能或者做服务降级方案时,我们会使用缓存作为解决方案,像分布式缓存方案,比如 Redis、...

  • 谁,没谁

    谁没接受过赞誉 没谁 谁还敢接受相同的赞誉 曾经谁如此沾沾自喜 讽刺,怪谁 还能怪谁 你不说,没谁懂 你不做,没谁...

  • Linux_295_Sersync工具介绍

    ftp:效率低,不支持差异化,实时同步实时数据同步:rsync + inotify-toolssersync:还能...

  • 心里作怪

    A:就是写不出好文章,怪谁了还能怪谁,怪你呀 B:不是应该怪你嘛,不好好看书,不好好学习,每次安排的计划,都不去完...

  • 看完这篇再买房,还能多省钱

    重要提示!!!本文仅适用于成都新房住宅市场,其他城市欢迎大家贡献绝招 不说废话,先放大招:看新房尽量不要留真实电话...

  • 自己不操心还能怪谁

    半路下班车,再麻烦别人接。代价太大了,只能怪自己不操心。

  • 买鞋一时爽,一直买一直爽!

    单身怪谁? 一直存不下来钱怪谁? 通宵排队睡不好觉怪谁? 还不是因为买鞋! 对不起,我不会戒掉这个爱好的 我要一直...

  • 你一定要幸福,这样我就不会责怪自己没有争取到陪你到老的权利

    故事以“我能和你做朋友吗”开头,却以“还能做朋友吗”结尾 生气吗?不生气,怪谁呢?怪自己,还是自己...

  • 我应该怪谁?

    怪天气太冷,我居然没有日更故事了。 怪年味太重,突然故事被生活琐事掩盖。 还能怪谁?我想下,曦妃的穿越还没写,哦,...

  • Go:Memory Model

    Go的内存模型 看完这篇文章你会明白 一个Go程序在启动时的执行顺序 并发的执行顺序 并发环境下如何保证数据的同步...

网友评论

    本文标题:看完这篇,数据同步还不会,还能怪谁

    本文链接:https://www.haomeiwen.com/subject/tubsxktx.html