《软件架构设计 大型网站技术架构与业务融合之道》第 8 章学习笔记整理

本文主要是介绍《软件架构设计 大型网站技术架构与业务融合之道》第 8 章学习笔记整理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

高并发系统有哪些分类和例子

侧重于「高并发读」的系统

  • 1.搜索引擎
  • 2.电商的商品搜索
  • 3.电商系统的商品描述、图片和价格

侧重于「高并发写」的系统

  • 广告计费系统:C 端用户的每一次浏览或点击都会对广告主的账号余额进行一次扣减

同时侧重于「高并发读」和「高并发写」的系统

  • 1.电商的库存系统和秒杀系统
  • 2.支付系统和微信红包
  • 3.IM、微博和朋友圈

高并发读的应对策略

策略 1:加缓存

  • 案例 1:本地缓存或 Memcached/Redis 集中式缓存
  • 案例 2:MySQL 的 Master/Slave
  • 案例 3:CDN 静态文件加速(动静分离)

策略 2:并发读

  • 案例 1:异步 RPC
    • 没有耦合关系的 3 个调用可以并行去调
  • 案例 2:Google 的「冗余请求」
    • 客户端首先给服务端发送一个请求,并等待服务端返回的响应
    • 如果客户端在一定的时间内(定义为 95%请求的响应时间)没有收到响应,则马上给一台(或多台)服务器发送同样的请求
    • 客户端等待第一个响应到达之后,终止其他请求的处理

策略 3:重写轻读

  • 案例 1:微博 Feeds 流
    • 场景
      • 微博首页或微信朋友圈都存在类似的查询场景:用户关注了n个人(或者有n个好友),每个人都在不断地发微博,然后系统需要把这n个人的微博按时间排序成一个列表,也就是Feeds流并展示给用户
      • 用户也需要查看自已发布的微博列表
    • 原始方案(假设数据存在数据库中)
      • 表结构
        • 关注关系表(Following 表)
          • ID(自增主键)
          • user_id(关注者)
          • followings(被关注的人)
        • 微博发布表(Msg表)
          • ID(自增主键)
          • user_id(发布者)
          • msg_id(发布的微博 ID)
      • 查询 user_id = 1 发布的微博列表并分页
        • SELECT msg_ids from Msg where user_id = 1 limit offset, count
      • 查询 user_id = 1 用户的 Feeds 流,并按时间排序并分页,需要两条 SQL 语句
        • 先查询 user_id = 1 的用户的关注的用户列表:
        • SELECT followings from Following where user_id=1
        • 查询关注的所有用户的微博列表,按时间排序并分页I
        • SELECT msg_ids from Msg where user_id in (followings) limit offset, count
    • 重写轻读
      • 方案
        • 提前为每个 user_id 准备一个 Feeds 流,或叫收件箱
        • 每个用户都有一个发件箱和收件箱。用户发布 1 条微博后,只写入自己的发件箱就返回成功。然后后台异步地把这条微博推送到粉丝的收件箱,即「写扩散」
        • 这样,每个用户读取 Feeds 流的时候不需实时聚合,直接读取各自的收件箱即可
        • 若是收件箱数量超过一定的阈值,丢弃即可
      • 用户发布的微博如何全量保存数据
        • 仍然按 Msg 表存储,按 user_id 和时间范围进行数据库的分片
      • 分页后,如何快速查看某个 user_id 从某个 offet 开始的微博,如显示要第 50 页,每页有 100 个,即 offset = 5000 的位置开始之后的微博。如何快速定位到 5000 所属的库?
        • 借助二级索引:另外有一张表,记录<user_id,月份,count>。也就是每个 user_id 在每个月份发表的微博总数。基于这个索引表即可快速定位到 offset = 5000 的微博发生在那个月份
      • 用户粉丝多的优化:在读的时候实时聚合
        • 在写的一端,对于粉丝数量少的用户(假设定个阈值为5000,小于5000的用户),发布一条微博之后推送给5000个粉丝
        • 对于粉丝数多的用户,只推送给在线的粉丝们(系统要维护一个全局的、在线的用户列表)
        • 对于读的一端,一个用户的关注的人当中,有的人是推给他的(粉丝数少于5000), 有的人是需要他去拉的(粉丝数大于5000),需要把两者聚合起来,再按时间排序,然后分页显示,这就是“推拉结合”
  • 案例 2:多表的关联查询:宽表和搜索引擎
    • 提前把关联数据计算好,存在一个地方,读的时候直接去读聚合好的数据,而不是读取的时候再去做 join
    • 宽表实现:把要关联的表的数据算好后保存在宽表里。依据实际情况,可以定时算,也可能任何一张原始表发生变化之后就触发-次宽表数据的计算
    • ES 类的搜索引擎实现:把多张表的Join 结果做成一个个的文档,放在搜索引擎里面,也可以灵活地实现排序和分页查询功能

高并发写的应对策略

策略 1:数据分片

  • 案例 1:数据库的分库分表
  • 案例 2:JDK 的 ConcurrentHashMap 实现
    • ConcurrentHashMap在内部分成了若干槽( 个数是2的整数次方,默认是16 个槽),也就是若干子HashMap。这些槽可以并发地读写,槽与槽之间是独立的,不会发生数据互斥
  • 案例 3:Kafka 的 partition
    • 在Kafka中,一个topic表示-一个逻辑上的消息队列,具体到物理上,一个topic 被分成了多个partition,每个parition对应磁盘中的一个日志文件。partition 之间也是相互独立的,可以并发地读写,也就提高了一个topic 的并发量
  • 案例 4:ES 的分布式索引
    • 在搜索引擎里有一个基本策略是分布式索引。比如有10亿个网页或商品,如果建在一个倒排索引里面,则索引很大,也不能并发地查询
    • 可以把这 10 亿个网页或商品分成n份,建成n个小的索引。一个查询请求来了以后,并行地在n个索引上查询,再把查询结果进行合并

策略 2:任务分片

  • 案例 1:CPU 的指令流水线
  • 案例 2:Map/Reduce
  • 案例 3:Tomcat 的 1+N+M 网络模型
    • 把一个请求的处理分成了三个工序:监听、I/O、业务逻辑处理。1 个监听线程负责监听客户端的 Socket 连接;N 个 I/O 线程负责对 Socket 进行读写,N 通常约等于 CPU 核数;M 个 Work 线程负责对请求进行逻辑处理
    • 进一步来讲,Work 线程还可能被拆分成解码、业务逻辑计算、编码等环节,进一步提高并发度

策略 3:异步化

  • 解释
    • 站在客户端的角度来讲,是请求服务器做一个事情,客户端不等结果返回,就去做其他的事情,回头再去轮询,或者让服务器回调通知
    • 站在服务器角度来看,是接收到一个客户的请求之后不立即处理,也不立马返回结果,而是在“后台慢慢地处理”,稍后返回结果。因为客户端不等上一个请求返回结果就可以发送一个请求,可以源源不断地发送请求,从而就形成了异步化
  • 案例 1:短信验证码注册或登录
    • 通常在注册或登录App或小程序时,采用的方式为短信验证码。短信的发送通常需要依赖第三方的短信发送平台。客户端请求发送验证码,应用服务器收到请求后调用第三方的短信平台
    • 公网的HTTP调用可能需要1~2s,如果是同步调用,则应用服务器会被阻塞。假设应用服务器是Tomcat,一台机器最多可以同时处理几百个请求,如果同时来几百个请求,Tomcat就会被卡死了。
    • 改成异步调用就可以避免这个问题。应用服务器收到客户端的请求后,放入消息队列,并立即返回。然后有一个后台任务,从消息队列读取消息,去调用第三方短信平台发送验证码
  • 案例 2:电商的订单系统
    • 买多个商品,但只下了一个订单付一次款,因为后台会进行「拆单」等环节
  • 案例 3:广告计费系统
    • 用户的点击请求或浏览请求首先会以日志的形式进行落盘。落盘之后,立即给客户端返回数据。后续的所有处理,当然也包括扣费,全部是异步化的
  • 案例 4:LSM 树(写内存+Write-Ahead 日志)
    • LSM(Log Structured Merged Tree)的核心思想是「异步写」。LSM 树支撑的是 KV 存储,磁盘上实现了 Sorted HashMap,但插入时是无序的
    • 原理:LSM 在内存中维护一个 Sorted HashMap,并写一条日志(即 Write-Ahead 日志),该日志的关键点是顺序写入,只会在日志尾部追加,而不会随机地写入,再有一个后台任务定期地把内存中的 SortedHashMap 合并到磁盘文件中,后台任务会执行磁盘数据的合并排序
    • 拓展:上层业务高并发扣减 MySQL 中的账户余额,可以在 Redis 中扣,同时落一条日志(日志可以在一个高可靠的消息中间件或数据库中插入一条条日志,数据库可以分库分表)。当 Redis 宕机,把所有的日志重放完毕,再用数据库中的数据初始化 Redis 中的数据。当然,数据库中的数据不能比Redis落后太多,否则积压大量日志未处理,宕机恢复的时间会很长
  • 案例 5:Kafka 的 Pipeline
    • kafka 为了高可用性,会为每个 Topic 的每个 Partition 准备多个副本,其中一个被选举为 Leader,另外都是 Follower
    • 对于同步发送,客户端每发送一条消息,Leader 要把这条消息同步到所有 Follower 后,才会对客户端返回成功
    • 在实现上,Leader 并不会主动给两个 Follower 同步数据,而是等 Follower 主动拉取,并且是批量拉取。当 Leader 收到客户端的消息 msg1 并把它存到本地文件后,就去做别的事情了,如接收下一个消息 msg2,此时客户端仍处于阻塞状态,等待 msg1 返回。只有等所有 Follower 把消息 msg1 拖过去后,Leader 才会返回客户端说 msg1 接收成功了

策略 4:批量

  • 案例 1:Kafka 的百万 QPS 写入
    • kafka 快的三个策略:1.Partition 分片,2.磁盘的顺序写入,3批量,把多条合并成一条,一次性写入
    • kafka 的客户端在内存中为每个 Partition 准备了一个队列,称为 RecordAccumulator。Producer 线程一条条地发送消息,这些消息都进入内存队列。然后通过 Sender 线程从这些队列中批量地提取消息发送给 kafka 集群
    • 如果是同步发送,Producer 向队列中放入一条消息后会阻塞,等待 Sender 线程取走该条消息并发出去后,Producer 才会返回,这是没有批量操作
    • 如果是异步发送,Producer把消息放入队列中后就返回了,Sender线程会把队列中的消息打包,一次发送出去多个,这时就会用到批量操作
    • 对于具体的批量策略,Kafka提供了几种参数进行配置,可以按Batch的大小或等待时间来批量操作
  • 案例 2:广告计费系统的合并扣费
    • 例如可把同一账号扣 10 次,每次扣 1 块。改为 1 次扣 10 块
    • 扣费模块一次性地从持久化消息队列中取多条消息,对这多条消息按广告主的账号 ID 进行分组,同一个组内的消息的扣费金额累加合并,然后从数据库里扣除
  • 案例 3:MySQL 的小事务合并机制
    • 比如扣库存,对同一个SKU,本来是扣10次、每次扣1个,也就是10个事务;在MySQL内核里面合并成1次扣10个,也就是10个事务变成了1个事务

策略 5:串行化+多进程单线程+异步 I/O

  • 案例:Nginx、Redis 的单线程模型
    • 有了异步 I/O 后,可把请求串行化处理。这样,没有了锁的竞争和 I/O 的阻塞,单线程也非常高效。要利用多核优势可开多个实例

注:原始发布于个人博客:https://shiqi-lu.tech/soft-arch-design-ch8/

这篇关于《软件架构设计 大型网站技术架构与业务融合之道》第 8 章学习笔记整理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1064625

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

mybatis的整体架构

mybatis的整体架构分为三层: 1.基础支持层 该层包括:数据源模块、事务管理模块、缓存模块、Binding模块、反射模块、类型转换模块、日志模块、资源加载模块、解析器模块 2.核心处理层 该层包括:配置解析、参数映射、SQL解析、SQL执行、结果集映射、插件 3.接口层 该层包括:SqlSession 基础支持层 该层保护mybatis的基础模块,它们为核心处理层提供了良好的支撑。

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

百度/小米/滴滴/京东,中台架构比较

小米中台建设实践 01 小米的三大中台建设:业务+数据+技术 业务中台--从业务说起 在中台建设中,需要规范化的服务接口、一致整合化的数据、容器化的技术组件以及弹性的基础设施。并结合业务情况,判定是否真的需要中台。 小米参考了业界优秀的案例包括移动中台、数据中台、业务中台、技术中台等,再结合其业务发展历程及业务现状,整理了中台架构的核心方法论,一是企业如何共享服务,二是如何为业务提供便利。

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss