notebooks
  • notebooks
  • _planning
    • 2022 OKR
    • basketball
    • swimming
  • communication
    • Dubbo
    • Kafka
    • Messaging
    • RPC
    • Thrift
  • computation
    • map-reduce
  • cs-basic-knowledge
    • computer-architecture
    • data-structure-and-algorithms
    • networks
    • os
  • devops
    • Docker
    • Linux
    • Prometheus
    • operations
    • security
    • trouble-shooting
  • distributed-knowledge
    • Zookeeper_CMD
    • distributed-system
  • game-engine
    • Unity
  • others
    • appium使用
  • protocols
    • http(s)协议
    • 官方链接
    • sip
  • storage
    • Elasticsearch
    • GuavaCache
    • MySQL
    • MySQL_CMD
    • NoSQL
    • Redis
    • Redis_CMD
  • system-design
    • system-design
  • tools
    • Git
    • IDEA
    • Mac
    • VScode
    • Vim
  • _working
    • doc-template
      • backend-design-review
      • correction-of-error
      • service-review
    • process
      • domain-backup
      • oncall
  • blogs
    • history
      • 8088/8086微处理器
      • 8088/8086指令系统
      • CSS-DOM
      • CSS定位
      • CSS工作原理
      • CSS控制背景
      • CSS浮动布局
      • CSS盒模型
      • Chrome开发者工具使用方法
      • DOM
      • Django Model模型层学习
      • Django-REST-framework Serializers学习
      • Django-REST-framework Views和ViewSets学习
      • Django View视图层学习
      • Gvim下Emmet安装及使用教程
      • HTTP协议简介
      • HashMap原理初探
      • JavaScript简史
      • JavaScript语法
      • Java内存模型和GC机制
      • Java基础——Lambda学习
      • Java基础——方法引用
      • Java基础——枚举类型
      • Java类加载机制
      • KMP算法
      • Kafka学习
      • Linux下用命令行编译Java程序
      • MathJax简介和基本用法
      • Python实现常见数据结构
      • Python装饰器总结
      • TCP协议的三次握手和四次挥手
      • Thrift学习
      • asyncio学习
      • markdown的常用语法
      • 修改hosts文件实现翻墙
      • 充实文档的内容
      • 关系数据库
      • 关系数据库标准语言SQL(一)
      • 关系数据库标准语言SQL(二)
      • 关系数据理论
      • 关系查询处理和查询优化
      • 内联元素和块级元素
      • 剑指offer算法题练习
      • 动态创建标记
      • 图形化用户界面
      • 在Eclipse中使用Maven构建Java Web项目
      • 增加微博秀遇到的一些问题
      • 处理机调度
      • 如何用github和hexo搭建个人博客
      • 存储管理
      • 存储系统的层次结构
      • 学习模仿lionhit网站首页的过程总结
      • 实用的GitHub小技巧
      • 并发控制
      • 循环与分支程序设计
      • 指令系统的设计
      • 指令级并行及其开发——硬件方法
      • 搭建自己的VPN服务器
      • 操作系统用户界面
      • 数据库安全性
      • 数据库完整性
      • 数据库恢复技术
      • 数据库绪论
      • 数据库编程
      • 数据库设计
      • 数据抽象
      • 文件系统
      • 文法和语言
      • 最佳实践
      • 案例研究:JavaScript图片库
      • 案例研究:图片库改进版
      • 汇编语言程序格式
      • 汇编语言程序设计基础知识
      • 流水线技术
      • 深度优先搜索和广度优先搜索
      • 牛客网——网易2017秋招编程题集合
      • 用JavaScript实现动画效果
      • 第一篇博客
      • 经典排序算法总结(Java实现)
      • 经典查找算法总结(Java实现)
      • 综合示例
      • 编译原理引论
      • 背包、队列和栈
      • 虚拟机安装Linux系统及常用软件
      • 计算机操作系统绪论
      • 计算机系统结构的基础知识
      • 设备管理
      • 设计模式之代理模式
      • 设计模式之单例模式
      • 设计模式之工厂模式
      • 设计模式之策略模式
      • 设计模式之观察者模式
      • 词法分析
      • 进程管理
      • 闭包
      • 阻止Google自动跳转到香港服务器的方法
      • 项目部署过程
  • programming-language
    • C#
      • C#
    • C&C++
      • C
    • C&C++
      • C++
    • Java
      • GoogleGuice
    • Java
      • JVM
    • Java
      • Java
    • Java
      • Maven
    • Java
      • Mybatis
    • Java
      • Spring知识
    • Java
      • SpringBoot
    • Java
      • Tomcat
    • Python
      • Python
    • Shell
      • Shell
  • wheels
    • dcc
      • 产品调研
      • 方案设计
    • red-envelope
      • 方案设计
    • short-url
      • 短链接服务
    • sso
      • 方案设计
Powered by GitBook
On this page
  • 设计模式六大原则
  • 设计模式
  • 文章海量评论分页设计
  • 海量数据出现频率
  • 分库分表下的分页查询
  • 缓存重建
  • 六边形架构
  • SOLID原则
  • 缓存穿透、缓存击穿和缓存雪崩
  • 缓存热点问题解决方案
  • 读写分离
  • 微服务
  • Service Mesh 服务网格
  • 接口加密方案
  1. system-design

system-design

设计模式六大原则

单一职责原则(Single Responsibility Principle, SRP)

就一个类而言,应该仅有一个引起它变化的原因。

如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。

开放‑封闭原则(Open-Closed Principle, OCP)

是说软件实体(类、模块、函数等待)应该可以扩展,但是不可修改(Open for extension, closed for modification)。

无论模块多么地“封闭”,都会存在一些无法对之封闭的变化。既然不可能完全封闭,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。在我们最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后发生的同类变化。面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。

依赖倒置原则(Dependence Inversion Principle,DIP)

高层模块不应该依赖底层模块。两个都应该依赖抽象。抽象不应该依赖细节。细节应该依赖抽象。针对接口编程,不要对实现编程。

举例:访问数据库逻辑写到一个函数(底层模块)用以复用,业务层(高层模块)进行调用。如果需要更换不同数据库或者不同连接池等等,但此时高层模块与底层模块耦合在一起。办法就是在高层模块和底层模块之间做一层抽象(接口或者抽象类)。

接口隔离原则(Interface Segregation Principle, ISP) 使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。

根据接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。每一个接口应该承担一种相对独立的角色,不干不该干的事,该干的事都要干。这里的“接口”往往有两种不同的含义:一种是指一个类型所具有的方法特征的集合,仅仅是一种逻辑上的抽象;另外一种是指某种语言具体的“接口”定义,有严格的定义和结构,比如Java语言中的interface。

里式替换原则(Liskov Substitution Principle, LSP)

子类型必须能够替换掉它们的父类型。

也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化。只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。

迪米特法则(Law of Demeter, LoD)

如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。根本思想是强调了类之间的松耦合。

接口隔离原则和单一职责原则的区别

  1. 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。

  2. 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

设计模式

简单工厂、工厂方法和抽象工厂区别

  • 简单工厂:唯一工厂类,一个产品抽象类,工厂类的创建方法依据入参判断并创建具体产品对象。(举例:一个汽车生产商承接了各种品牌汽车的生产,当为宝马汽车生产产品时只需要在调用该工厂的方法时传入参数“BM”即可创建相应产品,现用简单模式来模拟实现)

  • 工厂方法:多个工厂类,一个产品抽象类,利用多态创建不同的产品对象,避免了大量的if-else判断。(举例:一个汽车生产商承接了各种品牌汽车的生产,将其原有的汽车工厂进行分割,为每种品牌的汽车各自设立一个子工厂,宝马汽车工厂专门生产宝马汽车,奔驰汽车厂专门生产奔驰汽车,如果再次承接了其他品牌汽车的生产合作,只需要添加一个对应的工厂即可,无需对原有的工厂进行修改)

  • 抽象工厂:多个工厂类,多个产品抽象类,产品子类分组,同一个工厂实现类创建同组中的不同产品,减少了工厂子类的数量。(举例:一个汽车生产商承接了各种品牌汽车的生产,又可以生产多种种类的汽车,像公共汽车、小轿车等,相同品牌的汽车构成一个产品族,而相同类型的汽车又构成了一个产品等级结构,使用抽象工厂模式实现该应用,如宝马汽车厂生产宝马公汽和小轿车,奔驰汽车厂生产奔驰公汽和小轿车)

文章海量评论分页设计

场景:

文章的评论量非常大, 比如说一篇热门文章就有几百万的评论, 设计一个后端服务, 实现评论的时序展示与分页。

设计:

1.不支持页码跳转:传评论id 用 offset 实现翻页,(文章id, 评论id) 建联合索引,评论 id 需递增。

如果表中存在连续的数字列并为索引,那么通过页码即可计算出此字段的范围,直接作范围查询即可:

start = (page-1) * pagesize
end = page * pagesize
select * from table where id > start and id <= end

2.支持页码跳转:不能做精准深分页,否则压力太大,在50或100页后数据分页是否可以不完全精确,假如可以,那么缓存深页码的起始评论 id。

海量数据出现频率

TODO

分库分表下的分页查询

每张表查 N 条数据,数据在服务层进行内存排序,得到数据全局视野,再取目标页数据,便能够得到想要的全局分页数据。

缓存重建

  1. 互斥锁:只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可

  2. 永不过期:从缓存层面来看,确实没有设置过期时间,所以不会出现热点 key 过期后产生的问题,也就是“物理”不过期。从功能层面来看,为每个 value 设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存。

六边形架构

在六边形架构风格中,应用程序的内部(中间的橙色六边形)包含业务规则,基于业务规则的计算,领域对象,领域事件等。而外部的,也是我们平时最熟悉的诸如REST,SOAP,NoSQL,SQL,Message Queue等,都通过一个端口接入,然后在内外之间有一个适配器组成的层,它负责将不同端口来的数据进行转换,翻译成领域内部可以识别的概念(领域对象,领域事件等)。

内部不关心数据从何而来,不关心数据如何存储,不关心输出时JSON还是XML,事实上它对调用者一无所知,它可以处理的数据已经是经过适配器转换过的领域对象了。

SOLID原则

单一责任原则(The Single Responsibility Principle)

一个类只应承担一种责任。

开放封闭原则(The Open Closed Principle)

实体应该对扩展是开放的,对修改是封闭的。

举例:比如系统支付方式有多种,每新增一种需要修改原有代码,可以抽离出来一个PayHandler接口,这样新增支付方式只需要新增一个PayHandler实现类即可,不用改动原有代码。

里氏替换原则(Liskov Substitution Principle)

一个对象在其出现的任何地方,都可以用子类实例做替换,并且不会导致程序的错误。

接口分离原则(The Interface Segregation Principle)

客户(client)不应被强迫依赖它不使用的方法。即,一个类实现的接口中,包含了它不需要的方法。将接口拆分成更小和更具体的接口,有助于解耦,从而更容易重构、更改。

依赖倒置原则(The Dependency Inversion Principle) 高层次的模块不应依赖低层次的模块,他们都应该依赖于抽象;抽象不应依赖于具体实现,具体实现应依赖抽象。

举例:数据库连接Driver、六边形架构、MVC模式中面向接口编程

缓存穿透、缓存击穿和缓存雪崩

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

  • 接口层增加校验,如用户鉴权校验,id做基础校验

  • 布隆过滤器

  • 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)

缓存击穿是指缓存中没有但数据库中有的热点数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

  • 设置热点数据永远不过期

  • 加互斥锁

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

  • 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生

  • 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中

  • 设置热点数据永远不过期

  • 加互斥锁

缓存热点问题解决方案

问题描述: 如redis集群的热点key(一个key map到一台redis机器上)会导致局部机器是热的,其他机器是冷的

解决方案: 1.可以采用多级缓存,将热点key-value缓存到本地缓存上,这样不用再访问redis机器了,利用Redis自带的消息通知机制,对于热点Key客户端建立一个监听,当热点Key有更新操作的时候,客户端也随之更新。 2.可以在程序层面加工key,如key#redis1、key#redis2、key#redis3。

读写分离

读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。

优点

  1. 提高数据库的并发处理能力;

  2. 避免写请求锁表阻塞读请求;

  3. 避免单点,提高数据库的可用性;

读写分离和缓存比较

  1. 价格,缓存基于内存价格较贵,而主从库基于磁盘价格便宜

  2. 缓存存在未命中情况,仍然需要从数据库读取,数据库写入会影响读取性能

  3. 缓存更多的是抵挡请求对DB的压力,针对热点数据

微服务

微服务 (Microservices) 就是一些协同工作小而自治的服务。

  • 小,小就是简单,简单到通过名字或者定义就可以知道它大概能干什么事了,而且只干这一件事

  • 自治,独立部署、独立运行,并能独立完成一个业务闭环

优点

  1. 技术异构性。不同服务内部的开发技术可以不一致

  2. 隔离性。一个服务不可用不会导致另一个服务也瘫痪,因为各个服务是相互独立和自治的系统

  3. 可扩展性。可以只对那些影响性能的服务做扩展升级

  4. 简化部署。单体服务几百万行代码,即使修改了几行代码也要重新编译整个应用,这显然是非常繁琐的,部署风险更高

  5. 易优化。代码量不会很大,容易重构

缺点

  1. 分布式带来的复杂性,如分布式数据库、分布式事务

  2. 测试挑战。对微服务进行测试,需要启动它依赖的所有其他服务

  3. 涉及多个服务时部署复杂

Service Mesh 服务网格

TODO

服务网格(Service Mesh)是处理服务间通信的基础设施层。它负责构成现代云原生应用程序的复杂服务拓扑来可靠地交付请求。在实践中,Service Mesh 通常以轻量级网络代理阵列的形式实现,这些代理与应用程序代码部署在一起,对应用程序来说无需感知代理的存在。

如果用一句话来解释什么是 Service Mesh,可以将它比作是应用程序或者说微服务间的 TCP/IP,负责服务之间的网络调用、限流、熔断和监控。对于编写应用程序来说一般无须关心 TCP/IP 这一层(比如通过 HTTP 协议的 RESTful 应用),同样使用 Service Mesh 也就无须关心服务之间的那些原本通过服务框架实现的事情,比如 Spring Cloud、Netflix OSS 和其他中间件,现在只要交给 Service Mesh 就可以了。

接口加密方案

TODO

Previoussystem-designNexttools

Last updated 2 years ago

image