目录
- 01 | 冷热分离:表数据量大读写缓慢如何优化?
- 02 | 查询分离:表数据量大查询缓慢如何优化?
- 03 | Elasticsearch 注意要点:这三点你不得不知
- 04 | 分表分库:单表数据量大读写缓慢如何解决?
- 05 | 读缓存:如何减少数据库读操作压力?
- 06 | 写缓存:如何节省数据库写操作资源?
- 07 | 数据收集:高频数据收集请求如何不影响主业务?
- 08 | 秒杀架构:设计秒杀架构必知必会的那些事
- 09 | 注册发现:如何对后台服务进行高效管理?
- 10 | 全链路日志:这个请求到底经历了什么?
- 11 | 熔断:如何预防一个服务故障崩掉整个系统?
- 12 | 限流:如何保障服务器承受亿级流量?
- 13 | 微服务的痛:用实际经历告诉你它有多坑(一)
- 14 | 微服务的痛:用实际经历告诉你它有多坑(二)
- 15 | 微服务的痛:用实际经历告诉你它有多坑(三)
- 16 | 数据一致性:下游服务失败上游服务如何独善其身?
- 17 | 数据同步:如何解决微服务之间的数据依赖问题?
- 18 | BFF:如何处理好微服务之间千丝万缕的关系?
- 19 | 接口 Mock:第三方服务还没好,功能设计如何继续?
- 20 | 一人一套测试环境:测试环境何时能释放出来使用?
01 | 冷热分离:表数据量大读写缓慢如何优化?
-
业务场景:平台有一个订单功能,里面的主表有几千万的数据量,加上关联表。数据量达到上亿。这么庞大的数据量,让平台的查询订单变得格外迟缓,查询一次都要二三十秒,而且多点击几次就会出现宕机。
-
解决方案:因为平台查询大多是近期常用数据,这样可以将数据库分为冷库和热库两个库,常用数据在热库,不常用数据在冷库
-
可选架构方案:
- 修改写操作的业务代码:建议在业务代码比较简单,并且不按照时间区分冷热数据时使用。
- 监听数据库变更日志:建议在业务代码比较复杂,不敢随意变更,并且不按照时间区分冷热数据时使用。
- 定时扫描数据库:建议在按照时间区分冷热数据时使用。
-
需要考虑的问题
- 任何一步出错后如何保证数据的一致性?
- 解决办法:保证每一步都可以进行失败重试以及保证幂等性
- 1在热数据库中,给要搬运的数据增加搬运标识,ColdFlag=待搬运
- 2每次搬运找出所有待搬运数据
- 3冷数据做幂等判断,并且通过事务包裹起来,保证重复数据不会多次插入以及出现重复数据不影响后续操作
- 4从热数据库中删除对应的数据
- 数据量很大,一次处理不完,如何进行批量处理。
- 定时扫描数据库可能会出现数据量过大情况,假设每次搬运50条数据
- 1在热数据库中给要搬的数据加个标识:ColdFlag=待搬运;
- 2找出前50条待搬运的数据
- 3在冷数据库中保存一份数据
- 4从热数据库中删除对应的数据
- 5循环2、3、4
- 当数据量大到需要用多线程如何处理
- 如何创建多个线程?启动多个定时器或者维护一个线程池
- 共享数据需要加锁
- **获取锁的原子性:**当一个线程发现某个待处理的数据没有加锁,然后给它加锁,这 2 步操作必须是原子性的,即要么一起成功,要么一起失败。实际操作为先在表中加上 LockThread 和 LockTime 两个字段,然后通过一条 SQL 语句找出待迁移的未加锁或锁超时的数据,再更新 LockThread=当前线程,LockTime=当前时间,最后利用 MySQL 的更新锁机制实现原子性。
- 获取锁必须与开始处理保证一致性: 当前线程开始处理这条数据时,需要再次检查下操作的数据是否由当前线程锁定成功,实际操作为再次查询一下 LockThread= 当前线程的数据,再处理查询出来的数据。
- 释放锁必须与处理完成保证一致性: 当前线程处理完数据后,必须保证锁释放出去。
- 线程失败退出了,并没有释放锁怎么办(锁超时)?
- 给锁设置一个超时时间,同时也要保证冷数据处理的幂等性
- 任何一步出错后如何保证数据的一致性?
-
整体方案
-
历史数据如何迁移
- 我们只需要给所有的历史数据加上标识:ColdFlag=待搬运 后,程序就会自动迁移了
02 | 查询分离:表数据量大查询缓慢如何优化?
-
业务场景:工单查询功能,主表和子表数据量都在千万级别,并且因为诉讼问题需要继续保持更新,因此我们无法将这些旧数据封存到别的地方,也就没法通过前面的冷热分离方案来解决。
-
解决方案:更新得数据放在一个数据库里,而查询得数据放在另外一个系统里。因为数据的更新都是表单更新,不需要关联也没有外键,所以更新速度可以保证,数据的查询则通过一个专门处理大数据量的查询引擎来解决,这样查询效率也可以保证。
-
什么情况下使用查询分离
- 修改数据请求效率尚可,但是需要优化查询
- 所有的数据都可能会修改
- 业务需要优化查询的性能
-
可选架构方案:
- 修改业务代码同步更新查询数据库:建议在业务代码比较简单,对写操作的响应速度要求不高,需要保证查询数据的实时一致性的。
- 修改业务代码异步更新查询数据库:建议在业务代码比较简单,对写操作响应速度有要求
- 监控数据库日志:业务代码比较复杂,或者改动代价太大
-
此处我们讨论第二种方案,第三种后续再进行讨论
-
需要考虑的问题
- 1写操作较多且线程太多,最终撑爆 JVM
- 2建查询数据的线程出错了,如何自动重试
- 3多线程并发时,很多并发场景需要解决
- 1-3可以通过引入MQ来解决上述问题
- MQ宕机或者是更新查询数据库的线程失败了如何保证数据最终一致性
- 可以在主数据增加NeedUpdateQueryData=true标识,这样MQ存储的消息也就可以仅仅作为通知就可以,消费者批量更新,更新成功之后将NeedUpdateQueryData置为false
- 保证消费端幂等
- 消息的时序性问题:一条数据被先后更新了两次(从1改为了2又改成了3),消费者两个线程分别对两次更新操作进行处理,要保证两个线程处理的先后顺序
- 在主数据库及查询数据库中增加last_update_time字段
-
整体方案
-
历史数据如何迁移
- 我们只需要把所有的历史数据加上这个标识:NeedUpdateQueryData=true,程序就会自动处理了。
03 | Elasticsearch 注意要点:这三点你不得不知
04 | 分表分库:单表数据量大读写缓慢如何解决?
05 | 读缓存:如何减少数据库读操作压力?
-
业务场景:电商平台在某一个时间段策划了一场预约活动,因为是临时活动,要通过最小的代价来保证系统平稳运行
-
解决方案:通过写缓存将单条写入数据合并进行批量写入,来缓解数据库压力
-
需要考虑的问题
- 写请求和批量落库两个操作是同步还是异步?
- 同步:业务代码简单但是等待合并请求会有延迟
- 异步:提示用户预约成功后用户刷新界面可能看不到预约结果,可以在预约详情页增加可能出现延迟的提示以及定时查询数据库刷新界面
- 如何触发批量落库
- 开启一个定时,每隔1s执行一次落库
- 对数量进行监控,达到20条记录进行一次落库
- 缓存数据存储在哪里?
- 使用reids集群
- 缓存层并发操作需要注意什么
- 我们只需要在并发性的设计方案中保证一次仅有一个线程批量落库就行。
- 批量落库失败了怎么办
- 从缓存中获取数据失败,无需进行处理,下次落库会自动将数据进行处理
- 批量保存数据库失败,通过事务回滚控制
- 从缓存删除失败,不需要管他,但是需要保证数据库幂等
- 写请求和批量落库两个操作是同步还是异步?
-
整体方案
- 批量写入请求进行合并,存入缓存redis中
- 触发落库操作方式
- 定时器定时触发
- 达到固定数量进行触发
-
方案不足
- 如果批量落库时的写数据量依然非常大,则无法解决
- 此方案适合每个写操作都是独立的情况,如果写操作之间存在竞争资源,比如商品库存,这个方案就无法覆盖。