热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

聊一聊异构系统间数据一致性

之前忙活过一个多方合作的项目,异构系统间维护数据一致性有时候是个头疼但必须解决的问题,今天我从背景、问题、解决思路、技术方案等几个方面浅谈一下,抛砖引玉。背景异构系统近两年我




之前忙活过一个多方合作的项目,异构系统间维护数据一致性有时候是个头疼但必须解决的问题,今天我从背景、问题、解决思路、技术方案等几个方面浅谈一下,抛砖引玉。


背景

异构系统

近两年我司承接了某个持续性的会议项目,即每季度或月不定期举行会议。本项目目前有三个主要供应方(面向用户的A方,数据中间B方,会议数据同步C方【我司】)。

为了方便演示问题,以下流程和职责都做了裁剪。

简化流程如下:



简化职责如下:




  • A方职责: 用户通过官网/小程序进行报名,A方调用B方的标准接口,不存储数据



  • B方职责:作为ISP,提供标准查询、新增、修改等相关接口,几乎不提供定制。基于表单和表单数据,完成数据存储与流转。



  • C方职责:提供导入/更新/审核/注销等入口,新数据会通知到B方,B方数据新增/更新也会通知到C方。


从图例来看,B方/C方数据存储方面是冗余的。但B方只存储了核心数据,提供不了太多业务行为,C方具有业务需要的全套流程,但在此项目中作为后方支持及后续现场支持,三方形成了一种生态和谐。本篇博客主旨在讨论多方异构系统之间如何保证数据的一致性。


产品/项目

从标准Sass系统来讲,这样的多方交互,不利于系统稳定性,有诸多不可控因素。但从项目角度,这是各方考虑/斗争/谈判/费用等综合因素下友好协商的结果。

当然这是一个私有部署项目,所以会有很多坚持和妥协。

大领导提到一个说法:项目是要交付的,功能完美是产品考虑的。在功能不完善的情况下,如何去交付?


最后的兜底

哎,一言难尽。是通宵了几次核对/修复数据的,这是最后的办法了。为了苦逼不再重现,今年要对整个线动一动手术。(说好的.net 不996呢?)(拿着白菜价操着卖白粉的心)。


问题

请求无序






  • C方 需要所有子会报名前,主会必须报名。



  • B方 各会之间的报名数据是无序到达的。



循环更新




  • B方 任意报名数据更新或新增都会推送到C方,C方收到更新也会更新B方。这里有一些措施进行了拦截中断,但仍会频繁循环更新问题。这是目前现状(为什么会出现?太赶工?)





排错困难




  • 无开发环境,需盲写代码,发到测试环境进行联调测试。



  • 调用链太长,日志过多,排错时需要根据调用各服务接口来判断走到了哪步,出现了一个问题。调用链能查到一些问题,但不容批量定位问题。单个查太难。



bug




  • 高并发下,redis组件出现各种问题(timeout等)



  • token问题



  • 数据丢失



  • 更新失效



  • 数据重复



  • 队列积压



  • 接口请求时间超长



  • 其他问题...



数据很大,也很小

大部分数据能对上,偶尔几十个或断断续续产生新问题的数据需要及时人工修复。功能有缺陷,人工也是一种交付办法,但不可持续,太他妈的累了。数据不一致,也是导致通宵核对/修复数据的一大原因。如果数据全一致,就不会那么辛苦了。


解决思路

管理层




  • 明确项目是要继续做的



  • 目标产品化/更方便维护方向发展。一团队养一项目。



  • 有改进想法提出来,拉会推进



  • 缺人,招人(遥遥无期...)



技术层




  • 针对请求无序问题,引入延时队列,先处理主会、子会延迟几秒钟在处理。



  • 针对循环更新问题,记录B方数据来源,非必要情况下,不回更B方。必须终止掉。【冤冤相报何时了】



  • 针对排错困难问题,引入mysql记录新增报名的请求以及处理结果,可以更快查询处理结果。



  • 针对bug,测试根据各测试场景进行复测,按10/100/1000/3000/万级规模压测。提前发下问题。



  • 推进客户方一起做必要去重逻辑。



其他因素

无论是标准产品还是交付项目,做任何改动都要评估。




  • 多沟通,大家都是站在一条线的。有利于事情解决的方案认同度会更高。



  • 预估花多少时间,有多少资源。



  • 能挤出来的空窗期有多久,客户方/产品方对于需求的急迫性有多强。



  • 基于场景测试,把缺陷优先级先列出来,根据空窗期先修复紧急缺陷。


把紧急且影响范围广的问题解决了,风险就小了很多了。80%的问题是由20%的因素造成的。 这也正符合程序优化中的时间/空间局部性。


进程运行时,在一段时间里,程序的执行往往呈现高度的局部性,

包括时间局部性和空间局部性。

时间局部性是一旦一个指令被执行了, 则在不久的将来,它可能再被执行。

空间局部性是一旦一个指令一个存储单元被访问,那么它附近的单元也将很快被访问.


技术方案

mysql实现延迟队列




  • 优先处理主会,子会延时处理



    由于隐私问题,这里只列部分字段





  • 数据库轮询获取未处理数据



    这里如何提高消费速度,可以参考《计算机系统结构》中标量处理机的流水线的一些知识。






  • 首先要无相关,即按AccountId分组,分组内的数据是无冲突/相关的,可以分批进行。记录各任务状态,最后统一提交数据库状态,然后1s后继续轮询。这种类似静态流水线。动态流水线较为复杂,这里暂不做实现。




do
{
    var groupTemps = groupDatas.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
    var currentRecords = new List();
    foreach (var item in groupTemps)
    {
        currentRecords.AddRange(item.ToList());
    }
    var temp = taskFunc(currentRecords);
    taskList.Add(temp);
    pageIndex++;
}
while ((pageIndex - 1) * pageSize <= groupCount);
//等待全部执行
await Task.WhenAll(taskList.ToArray());
await _dbContext.CommitAsync();
Thread.Sleep(1);



  • 如果1s轮询觉得太浪费,后续可以根据请求发送标记位(下次轮询时间),有数据时,可以快速轮询,无数据时放宽时间。极端处理方式,当主会请求过来处理完成后,直接发起子会处理,但要考虑数据库是否能承受的住这种并发压力。





  • 如果考虑请求会重复执行,可以在执行内加redis锁。慎用for update,并发一大就over.




/// 


/// 锁定执行。
/// 

/// "T">
/// 

"key">
/// 

"func">
/// 

"timeSpan">
/// 
public async Task> LockExcute(string key, Func>> func, int timeSpan)
{
  var db = (this._cacheClient as RedisClient).Db;
  var mutexKey = string.Format("mutex:", key);
  if (await db.StringSetAsync(mutexKey, "1", TimeSpan.FromSeconds(timeSpan), When.NotExists))
  {
      try
      {
          var item = await func.Invoke();
          return item;
      }
      catch (Exception ex)
      {
          _logger.LogError("LockExcute:Exception:" + ex.Message);
          return BizResult.BusinessFailed(-1, $"执行失败,Message:{ex.Message}");
      }
      finally
      {
          await db.KeyDeleteAsync(mutexKey);
      }
  }
  else
  {
      _logger.LogWarning($"LockExcute:Key:{key},正在处理中,请稍候");
      return BizResult.BusinessFailed(-1, "正在处理中,请稍候");
  }
}

redis实现延迟队列




  • 由于业务中一个Account同时只能处理一个主会,如果在处理子会的时候,主会请求突然过来了,就会有问题,这里就需要加锁主会。引入了Redis延迟队列



  • 基于Redis ZSet有序集合实现。



  • 思路:当前时间戳和延时时间相加,也就是到期时间,存入Redis中,然后不断轮询,找到到期的,拿到再删除即可。



  • 目前实现缺点:不利于监控,未发起http请求处理业务,导致调用链有缺。


/// 


 /// 3.入队列
 /// 

 /// 

"redisModel">
 /// 
 public async Task EnqueueZset(DataToModel redisModel)
 {
     redisModel.UpdateTime = redisModel.UpdateTime.AddSeconds(5);// 最后更新时间 + 5秒
     var redisDb = _redisConnectionService.GetRedisConnectionMultiplexer().GetDatabase(0);//默认DB0
     if (redisDb != null)
     {
         IsoDateTimeConverter timeFormat = new IsoDateTimeConverter();
         timeFormat.DateTimeFormat = "yyyy-MM-dd HH:mm:ss.fff";
         await redisDb.SortedSetAddAsync(ZSet_Queue, JsonConvert.SerializeObject(redisModel, Formatting.Indented, timeFormat), redisModel.UpdateTime.ToTimeStamp());//得分 --放入redis
         _logger.LogInformation($"数据排队--入队列!redisModel:{JsonConvert.SerializeObject(redisModel)}");
     }
 }

rabbmit实现延迟队列




  • 死信队列过期--》重推信队列?暂未实现。



数据更新方案




  • 核心原则:先查询对比,有变更再更新。从B方数据过来的,尽量不再更新回去。减小并发量,控制复杂度。





数据核对方案




  • 待补充。未实现自动化。后期可以获取双方系统数据,汇总对比。





部署/压测/监控

Jmeter(来自于测试同学提供的脚本)

这里只做简单截图




  • 配置预定义参数





  • 必要情况下配置后置处理程序






  • 配置好thread group,http request后,执行调用观察接口









  • 查询请求执行是否成功





  • 查看聚合报告





kubernetes




  • kubectl get nodes 获取所有节点



  • kubectl get pod -A 查看所有服务,观察status和age




  • kubectl logs [-f] [-p] POD [-c CONTAINER] 查看日志信息。



-c, --cOntainer="": 容器名

-f, --follow[=false]: 指定是否持续输出日志

--interactive[=true]: 如果为true,当需要时提示用户进行输入。默认为true

--limit-bytes=0: 输出日志的最大字节数。默认无限制

-p, --previous[=false]: 如果为true,输出pod中曾经运行过,但目前已终止的容器的日志

--since=0: 仅返回相对时间范围,如5s、2m或3h,之内的日志。默认返回所有日志。只能同时使用since和since-time中的一种

--since-time="": 仅返回指定时间(RFC3339格式)之后的日志。默认返回所有日志。只能同时使用since和since-time中的一种

--tail=-1: 要显示的最新的日志条数。默认为-1,显示所有的日志

--timestamps[=false]: 在日志中包含时间戳


mysql监控(来自于运维同学的反馈)

这里只截图简单信息




  • 通过云监控查看mysql状态[最大连接数/cpu/内存/慢查询/索引建议/锁]







调用链/日志

此处暂不截图。



失控


  • 一期方案





  • 二期方案




  • 三期方案



    当然那是进展顺利的情况下,不顺利的情况下就变成了这样




某些时候也会听到如下言论:




  • 一定要保证xx的信誉。



  • 今晚就不要睡觉了吧?大家多坚持一下。


就如现在的疫情封控一样,做好了精准防控一片赞歌,失控了就好好居家、共渡难关。

网络和现实都会告诉你什么就是人间。


总结

以上是关于定制化需求的一些解决方案,希望对未来类似产品或项目做个参考。本篇从问题着手,分析有利于解决/消除异构系统数据一致性办法。当然数据一致性也依赖于自身系统的高可用,这里未做过多描述,以后再说。

到此结束,谢谢观看!




推荐阅读
  • 本文详细介绍了 InfluxDB、collectd 和 Grafana 的安装与配置流程。首先,按照启动顺序依次安装并配置 InfluxDB、collectd 和 Grafana。InfluxDB 作为时序数据库,用于存储时间序列数据;collectd 负责数据的采集与传输;Grafana 则用于数据的可视化展示。文中提供了 collectd 的官方文档链接,便于用户参考和进一步了解其配置选项。通过本指南,读者可以轻松搭建一个高效的数据监控系统。 ... [详细]
  • 服务器部署中的安全策略实践与优化
    服务器部署中的安全策略实践与优化 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • 在CentOS 7环境中安装配置Redis及使用Redis Desktop Manager连接时的注意事项与技巧
    在 CentOS 7 环境中安装和配置 Redis 时,需要注意一些关键步骤和最佳实践。本文详细介绍了从安装 Redis 到配置其基本参数的全过程,并提供了使用 Redis Desktop Manager 连接 Redis 服务器的技巧和注意事项。此外,还探讨了如何优化性能和确保数据安全,帮助用户在生产环境中高效地管理和使用 Redis。 ... [详细]
  • 在使用 Cacti 进行监控时,发现已运行的转码机未产生流量,导致 Cacti 监控界面显示该转码机处于宕机状态。进一步检查 Cacti 日志,发现数据库中存在 SQL 查询失败的问题,错误代码为 145。此问题可能是由于数据库表损坏或索引失效所致,建议对相关表进行修复操作以恢复监控功能。 ... [详细]
  • 本文介绍了如何利用Shell脚本高效地部署MHA(MySQL High Availability)高可用集群。通过详细的脚本编写和配置示例,展示了自动化部署过程中的关键步骤和注意事项。该方法不仅简化了集群的部署流程,还提高了系统的稳定性和可用性。 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • 如何优化MySQL数据库性能以提升查询效率和系统稳定性 ... [详细]
  • 数字图书馆近期展出了一批精选的Linux经典著作,这些书籍虽然部分较为陈旧,但依然具有重要的参考价值。如需转载相关内容,请务必注明来源:小文论坛(http://www.xiaowenbbs.com)。 ... [详细]
  • 在2015年1月的MySQL内核报告中,我们详细探讨了性能优化和Group Commit机制的改进。尽管网上已有大量关于Group Commit的资料,本文将简要回顾其发展,并重点分析MySQL 5.6及之前版本中引入的二进制日志(Binlog)对性能的影响。此外,我们还将深入讨论最新的优化措施,如何通过改进Group Commit机制显著提升系统的整体性能和稳定性。 ... [详细]
  • 在深入掌握Spring框架的事务管理之前,了解其背后的数据库事务基础至关重要。Spring的事务管理功能虽然强大且灵活,但其核心依赖于数据库自身的事务处理机制。因此,熟悉数据库事务的基本概念和特性是必不可少的。这包括事务的ACID属性、隔离级别以及常见的事务管理策略等。通过这些基础知识的学习,可以更好地理解和应用Spring中的事务管理配置。 ... [详细]
  • 提升 Kubernetes 集群管理效率的七大专业工具
    Kubernetes 在云原生环境中的应用日益广泛,然而集群管理的复杂性也随之增加。为了提高管理效率,本文推荐了七款专业工具,这些工具不仅能够简化日常操作,还能提升系统的稳定性和安全性。从自动化部署到监控和故障排查,这些工具覆盖了集群管理的各个方面,帮助管理员更好地应对挑战。 ... [详细]
  • 本文详细介绍了使用 Python 进行 MySQL 和 Redis 数据库操作的实战技巧。首先,针对 MySQL 数据库,通过 `pymysql` 模块展示了如何连接和操作数据库,包括建立连接、执行查询和更新等常见操作。接着,文章深入探讨了 Redis 的基本命令和高级功能,如键值存储、列表操作和事务处理。此外,还提供了多个实际案例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • 尽管我们尽最大努力,任何软件开发过程中都难免会出现缺陷。为了更有效地提升对支持部门的协助与支撑,本文探讨了多种策略和最佳实践,旨在通过改进沟通、增强培训和支持流程来减少这些缺陷的影响,并提高整体服务质量和客户满意度。 ... [详细]
  • SSAS入门指南:基础知识与核心概念解析
    ### SSAS入门指南:基础知识与核心概念解析Analysis Services 是一种专为决策支持和商业智能(BI)解决方案设计的数据引擎。该引擎能够为报告和客户端应用提供高效的分析数据,并支持在多维数据模型中构建高性能的分析应用。通过其强大的数据处理能力和灵活的数据建模功能,Analysis Services 成为了现代 BI 系统的重要组成部分。 ... [详细]
author-avatar
阿依古丽丹_736
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有