当前位置: 首页 > news >正文

电商项目_性能优化_数据同步

1、缓存同步

使用场景

1. Redis缓存当做数据库

        大促等场景,并发请求数量的基数过大,即便只有很小比率的请求直接访问数据库其绝对数量也仍然不小,还是会存在系统雪崩的风险。

        构建Redis集群后,由于集群可以水平扩容,因此只要集群足够大,理论上支持海量并发就不是问题。

        所以可将将Redis当做数据库使用,将所有数据都缓存到Redis。解决缓存不命中的问题。

2. 缓存一致性

电商项目_性能优化_高并发缓存一致性-CSDN博客

在缓存一致性章节讲到过,为了保证DB和缓存的数据一致性,DB变更时,需要删除Redis缓存。

思考:Redis缓存了这么多的DB数据,如何保证数据一致性呢?

1. 分布式事务保证DB和缓存一致性。 缺点:性能低,另外缓存的异常也不应该影响业务。

2. 写DB时发一条消息,消费端更新Redis。缺点:对代码侵入大,还要实现消息发送接收。

3. 使用开源组件,订阅Binlog日志,更新缓存。 GOOD!

使用Binlog实时更新Redis缓存

Canle组件

官方主页:https://github.com/alibaba/canal

        它通过模拟MySOL主从复制的交互协议,把自己伪装成一个MySOL的从节点,向 MySOL主节点发送dump请求。MySOL收到请求后,就会向 Canal开始推送Binlog,Canal解析Binlog字节流之后,将其转换为便于读取的结构化数据,供下游程序订阅使用。实际运用后的运行架构如图:

电商项目实现过程

1. 安装运行Canal服务

2.Mysql打开binlog日志

        开启 Binlog 写入功能,配置 binlog-format 为 ROW 模式

3. 给Canal设置一个用来复制数据的MySQL账号

-- 定这个用户名为Canal,密码Canal
CREATE USER canal IDENTIFIED BY 'canal';GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';FLUSH PRIVILEGES;

4. 启动canel服务端

配置中的修改点:要同步的数据表、数据库

代码实现:

public class PromotionData implements IProcessCanalData {   private final static String SMS_HOME_ADVERTISE = "sms_home_advertise";private final static String SMS_HOME_BRAND = "sms_home_brand";private final static String SMS_HOME_NEW_PRODUCT = "sms_home_new_product";private final static String SMS_HOME_RECOMMEND_PRODUCT = "sms_home_recommend_product";@Autowired@Qualifier("promotionConnector")private CanalConnector connector;@PostConstruct@Overridepublic void connect() {tableMapKey.put(SMS_HOME_ADVERTISE,promotionRedisKey.getHomeAdvertiseKey());tableMapKey.put(SMS_HOME_BRAND,promotionRedisKey.getBrandKey());tableMapKey.put(SMS_HOME_NEW_PRODUCT,promotionRedisKey.getNewProductKey());tableMapKey.put(SMS_HOME_RECOMMEND_PRODUCT,promotionRedisKey.getRecProductKey());connector.connect();if("server".equals(subscribe))connector.subscribe(null);elseconnector.subscribe(subscribe);connector.rollback();}@Async@Scheduled(initialDelayString="${canal.promotion.initialDelay:5000}",fixedDelayString = "${canal.promotion.fixedDelay:5000}")@Overridepublic void processData() {try {if(!connector.checkValid()){log.warn("与Canal服务器的连接失效!!!重连,下个周期再检查数据变更");this.connect();}else{Message message = connector.getWithoutAck(batchSize);long batchId = message.getId();int size = message.getEntries().size();if (batchId == -1 || size == 0) {log.info("本次[{}]没有检测到促销数据更新。",batchId);}else{log.info("本次[{}]促销数据本次共有[{}]次更新需要处理",batchId,size);/*一个表在一次周期内可能会被修改多次,而对Redis缓存的处理只需要处理一次即可*/Set<String> factKeys = new HashSet<>();for(CanalEntry.Entry entry : message.getEntries()){if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN|| entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {continue;}CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());String tableName = entry.getHeader().getTableName();if(log.isDebugEnabled()){CanalEntry.EventType eventType = rowChange.getEventType();log.debug("数据变更详情:来自binglog[{}.{}],数据源{}.{},变更类型{}",entry.getHeader().getLogfileName(),entry.getHeader().getLogfileOffset(),entry.getHeader().getSchemaName(),tableName,eventType);}factKeys.add(tableMapKey.get(tableName));}for(String key : factKeys){/*此处未做删除缓存失败的补偿操作,可以自行加入*/if(StringUtils.isNotEmpty(key))  redisOpsExtUtil.delete(key);}connector.ack(batchId); // 提交确认log.info("本次[{}]处理促销Canal同步数据完成",batchId);}}} catch (Exception e) {log.error("处理促销Canal同步数据失效,请检查:",e);}}
}

2、跨系统的数据同步

        在大型互联网企业中、其核心业务数据,以不同的数据结构和存储方式,保存几十甚至上百份,都是非常正常的。

        那么如何才能做到让这么多份数据实时地保持同步呢?分布式事务解决不了大规模数据的实时同步问题。

        Canal同样可以应用到这个场景。但是并不能直接将Canal出来的 Binlog 数据肯定不能直接写入下游的众多数据库中。原因也很明显:一是写不过来;二是下游的每个数据库,在写入之前可能还要处理一些数据转换和过滤的工作。一般会增加一个消息队列来解耦上下游。

      

更换数据库

业务场景:

  1. 单库单表升级成多库多表
  2. 系统从传统部署方式向云上迁移的时候,也需要从自建的数据库迁移到云数据库上
  3. 在线分析类的系统,需要使用专门的分析类数据库,比如HBase

        设计迁移方案的时候,一定要保证每一步都是可逆的。也就是必须保证,每执行完一个步骤, 一旦出现任何问题,都能快速回滚到上一个步骤。        

更换数据库,已订单表为例,更换步骤:

1. 数据同步到新库

  • 历史数据同步:提供数据同步任务,同步某个时间点之前的历史数据
  • 实时数据同步:提供实时同步任务,利用Binlog实现两个异构数据库之间数据的实时同步。

2. 订单服务升级

1)写:支持双写新旧两个库,并且预留热切换开关,能通过开关控制三种写状态:只写旧库、只写新库和同步双写。

2)读:支持读取新旧两个库,同样预留热切换开关,控制读取旧库还是新库。

3. 上线新版的订单服务

仍旧读写旧库一段时间,验证新版订单服务的稳定性,验证新库与旧库数据一致性。

4. 开启双写开关

同时要停掉实时同步程序。双写是先写旧库再写新库:

  • 如果旧库成功、新库失败,业务返回成功并记录日志;
  • 如果旧库失败,直接返回失败。

出现任何形式的失败,都应该回滚到值写旧库开关。

        双写开关需要运行一段时间,这段时间需要不断验证 新库数据与旧库数据一致性。 

        一致性的验证需要提供个数据对比服务,用于比对旧库最近的数据变更,然后检查新库中的数据是否一致,如果不一致,则需要进行补偿。

5. 双读:类似灰度发布的方式把读请求逐步切换到新库上

运行一段时间,中间出现问题即使回滚到只读旧库开关

6. 读请求全部切换到新库

        至此,读写请求全部都到新库了。稳定一段时间后,就可以停掉 数据对比服务,写开关也切换到只写新库(不可逆),旧库服务下线。

如果要实现 写开关切换到只写新库 这一步也可逆,把实时同步服务 和 数据对比服务,反过来再执行一段时间,保证这段时间旧库中也是完成的数据。做到一旦出现问题,就直接切回到旧库上。

数据比对服务

        业务系统运行的期间,数据在不断变化,可以选择比当前时间早一点的时间,一段时间内的数据做比对。

        订单这种时效性比较强的表,比对和补偿程序就可以根据订单完成时间,每次只比对这个时间窗口内完成的订单。

     

http://www.dtcms.com/a/310342.html

相关文章:

  • 18.若依框架中的xss过滤器
  • Java 24 新特性解析与代码示例
  • 牛客——取数游戏2
  • UE5 动态扫描波
  • 【C#学习Day15笔记】拆箱装箱、 Equals与== 、文件读取IO
  • iPhone查看App日志和系统崩溃日志的完整实用指南
  • 深入理解C语言指针:从回调函数到数组指针笔试题全解析(下)
  • 遥控器信号捕获
  • [CISCN 2022 初赛]online_crt
  • 基于react的YAPI实战指南
  • JavaWeb--Student2025项目:增删改查
  • 光纤网络FTTx(光接入网的应用类型)
  • 标准项目-----网页五子棋(4)-----游戏大厅+匹配+房间代码
  • Qt Quick 性能优化方法
  • WPF TreeView自带自定义滚动条
  • 云计算k8s集群部署配置问题总结
  • 铁皮矫平机冷知识·第三弹
  • 网站QPS多少才算高并发
  • A∗算法(A-star algorithm)一种在路径规划和图搜索中广泛使用的启发式搜索算法
  • 利用CompletableFuture优化查询效率
  • 1.2.4 砌体结构设计构造要求
  • Dify知识库分段策略详解:通用分段 vs 父子分段
  • 开源框架推荐:API数据批处理与爬虫集成
  • 前端开发一百问(动态更新)
  • 【0基础PS】PS工具详解--仿制图章工具
  • RustFS:高性能文件存储与部署解决方案(MinIO替代方案)
  • MySQL锁的分类 MVCC和S/X锁的互补关系
  • QT6.5.3 vs2022 pcl1.14.1窗体界面打开pcd点云文件
  • PAT 1022 Digital Library
  • nodejs最近开发过程中的总结