本文已参与「新人创作礼」活动,一起开启掘金创作之路。
技术总结(不定期更新)
记录我的日常工作的技术总结,属于个人感悟,不定期更新
代码开发
接口篇
- 接口支持高级分页(数据量过大深分页问题)
分页可以减少客户端接收的数据数目,但是当你需要将分页结果与不断接收的新条目结合时,通常的限制limit和偏移offset分页参数是低效的,因为每次当有个新条目在服务端被添加到先前的集合时,先前发送到客户端的偏移offset都变得无效,而且客户端无法得知在两次请求间新增了多少条目。保持客户端同步一个比较好的办法是使用before_id和after_id参数组合,比如客户端将已知的最新条目的id作为after_id请求参数,然后检索之后创建的新条目(当分页结果总数比较大时,获取靠后的分页结果会及其的慢,因为offset分页已经效率很低了,即使已经加了索引——实际工作场景中遇到的是要获取一个总数为10万的分页结果[该表做了8个分库操作],每页500条数据,当获取靠后页码的分页结果时,查询效率已经很低了,有的甚至超过了5秒,这对于分布式系统是无法接受的)
- 提供了分页的外部接口就不要再提供查询总数接口
过多的给外部提供接口会让后期维护成本变得更高,会使得难以管理和改动
- 为什么服务提供方不适合提供大批量的调用接口
- 背景:根据多区域查询用户数据信息的接口,可传区域数量限制是5个,对于某些业务场景来说太少,比如运营有上百个区域,调用方就需要调用多次,会影响性能,想要服务提供方将这个限制放大些(简单的来说,就是提供了一个批量查询的接口,为了兼顾能快速响应给服务调用方,入参中批量的列表只支持5个,但是调用方因为业务场景需要调用多次,希望调用提供方来调大批量的个数限制减少调用次数)
- 解释:该问题可以从技术维度探讨一下,建议可以设置个阈值做并行的循环调用(需要客户端和服务端支持动态调整阈值,该阈值用来调整批量的数量限制)。我们不妨使用反向论证法来剖析一下,考量点是这样,假如把大批量的逻辑放到服务提供方,可以有两种实现方式:其一是串行,其二是并行。串行肯定不能满足性能要求,会把调用端拖死,基本不能解决超时的问题,并行会存在计算量大,CPU压力大,会把CPU跑满。基于以上弊端,建议把并行放在客户端,客户端不存在计算的压力,循环后通过RPC的负载均衡会打到服务提供方集群的多个机器上,这样服务提供方的压力做了分散,理论上可以通过该集群的扩展获取想要的流量
MQ篇
- 如果MQ由于种种原因无法保证数据库操作和发MQ在同一个事务下保证,则必须先进行数据库事务操作,再发MQ
- 如果先发送MQ再进行数据库事务操作,则可能会造成MQ已经发送成功,数据库事务提交失败,此种错误不易进行补偿
- 如果先进行数据库事务操作,则发送MQ,当MQ发送失败,需要记录失败日志,同时进行告警或者通知到相关人员,进行MQ重发操作进行补偿
- 如果是使用的MQ本地消息表或者其他方式能够保证数据库事务和MQ在同一事务,则无需关心此问题(RocketMQ、Seata、Hmily等来解决)
- MQ同一业务数据状态保证顺序:保证同一业务主键的MQ能落到同一个分区上,需要有一个路由策略组件,由它决定消息该放到哪个分区中。生产者可以根据topic的路由信息选择具体发到哪个queue上,consumer订阅消息时,会根据负载均衡策略决定订阅哪些queue的消息
- 用户的等级状态:根据用户的行为(如GMV、下单件数、下单笔数、注册时间等)圈定用户,并根据圈定阶梯发送MQ为用户更新等级,但是用户在多个圈定行为中重复存在,这时就要根据发送MQ的顺序确定一个最终等级,这时就需要用户的id作为主键,将同一用户id的多个等级按照顺序进行发送MQ到同一个分区上
- 用户的订单状态:一个订单会产生多条MQ消息,下单、付款、发货、买家确认收货,消费端需要严格按照业务状态机的顺序处理,否则,就会出现业务问题,只要保证一个订单的多条状态消息在同一个分区,便可以满足业务需求,所以我们可以以订单id作为MQ的业务主键,保障同一订单的多个MQ消息可以落到同一个分区上
- MQ生产速度大于消费速度产生消息积压:增加分区数(同时消费者数要大于等于分区数)、增加消费方每次拉取待消费的MQ数
- 如果仅仅只有一个分区或者较少的分区造成了消息的积压,可考虑增加分区的方式,提高消费方的消费速度。对于同一个消费组,一个分区只支持一个消费线程来消费消息。过少的分区,会导致消费速度大大落后于消息的生产速度,可能会造成消息的积压。所以在实际生产环境中,一个topic会设置成多分区的模式,来支持多个消费者
- 适量增加每次一次性从broker里面取的待消费的消息的个数
- 过大量级的MQ消息量一定要进行业务前置过滤,否则可能造成消息挤压或者消费者机器宕机
- 当比较通用的MQ(比如订单、红包、优惠券、答题结果等)需要接入时,一定要根据业务类型进行过滤,通用型的MQ一般数据量都会特别大,每天的量可能达到上亿级
- 接入埋点上报的MQ要进行业务前置过滤:埋点的MQ消息量也十分的庞大,可以进行业务逻辑的校验提前过滤掉无需往下处理的MQ
- MQ并发消费需要根据业务主键进行加锁和解锁,锁的粒度一定要最小
如订单类MQ接入,同一个订单有可能取消的消息先到,所以我们需要对这样的订单MQ进行并发处理,根据订单id进行加锁(同时要指定过期时间,过期时间长短可以视业务而定),当该订单的业务处理完毕,再根据订单id进行解锁,当同一个订单(重复或其他状态)的MQ过来,可以保障并发时的先后顺序
PageHepler篇
- PageHelper对需要分页的方法不生效
- 简略写法可不使用PageInfo
- 查询出的列表结果额外属性赋值
- PageHelper深分页效率问题
指导文章
专题文章
梁的主页
非专题文章
大厂技术
美团技术 (github)
- 配置中心——MCC
- PRC——Mthrift(基于thrift)
- 任务调度——Crane
- 分布式监控——CAT【开源】
- 内部知识网——学城(大佬技术文章都有,有广度有深度)
- 缓存——squirrel(基于Redis)
- MySQL访问——Zebra【开源】
京东技术(gitee)
- 配置中心——DUCC
- PRC——JSF(基于Dubbo)
- 任务调度——天鸽(基于xxl-job)
- 分布式监控——UMP
- 内部知识网——CF
- 缓存——JIMDB(基于Redis)