同样是笔记摘录自---极客时间 李运华 《从0开始学架构》。这部分本人接触不多,也不太懂,只是大概了解了个概念。
1.1 读写分离的基本实现是:
1.2 两个细节点将引入设计复杂度:主从复制延迟和分配机制。
1)解决主从复制延迟有几种常见的方法:
将读写操作区分开来,然后访问不同的数据库服务器,一般有两种方式:程序代码封装和中间件封装。
2)程序代码封装
指在代码中抽象一个数据访问层(所以有的文章也称这种方式为“中间层封装”),实现读写操作分离和数据库服务器连接的管理。例如,基于 Hibernate 进行简单封装,就可以实现读写分离。
3)中间件封装
指的是独立一套系统出来,实现读写操作分离和数据库服务器连接的管理。事实上在业务服务器看来,中间件就是一个数据库服务器。由于数据库中间件的复杂度要比程序代码封装高出一个数量级,一般情况下建议采用程序语言封装的方式,或者使用成熟的开源数据库中间件。
常见的分散存储的方法“分库分表”,其中包括“分库”和“分表”两大类。
2.1 业务分库指的是按照业务模块将数据分散到不同的数据库服务器。
业务分库能够分散存储和访问压力,但同时也带来了新的问题,业务分库后,表之间的 join 查询、数据库事务无法简单实现了。1)业务分库后,原本在同一个数据库中的表分散到不同数据库中,导致无法使用 SQL 的 join 查询。
2)原本在同一个数据库中不同的表可以在同一个事务中修改,业务分库后,表分散到不同的数据库中,无法通过事务统一修改。虽然数据库厂商提供了一些分布式事务的解决方案(例如,MySQL 的 XA),但性能实在太低,与高性能存储的目标是相违背的。
2.2 更进一步,规模进一步增大,单个表的存储都太大,需要分表。(这个估计只有几家头部互联网公司一级的业务会触及)
单表数据拆分有两种方式:垂直分表和水平分表。
1. 垂直分表
垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。垂直分表引入的复杂性主要体现在表操作的数量要增加。
2.水平分表
水平分表适合表行数特别大的表,相比垂直分表,会引入更多的复杂性:
和数据库读写分离类似,分库分表具体的实现方式也是“程序代码封装”和“中间件封装”,但实现会更复杂。读写分离实现时只要识别 SQL 操作是读操作还是写操作,通过简单的判断 SELECT、UPDATE、INSERT、DELETE 几个关键字就可以做到,而分库分表的实现除了要判断操作类型外,还要判断 SQL 中具体需要操作的表、操作函数(例如 count 函数)、order by、group by 操作等,然后再根据不同的操作进行不同的处理。例如 order by 操作,需要先从多个库查询到各个库的数据,然后再重新 order by 才能得到最终的结果。
2、K-V 存储
优势: 解决关系数据库无法存储数据结构的问题
劣势:Redis 的缺点主要体现在并不支持完整的 ACID 事务,Redis 虽然提供事务功能,但 Redis 的事务和关系数据库的事务不可同日而语,Redis 的事务只能保证隔离性和一致性(I 和 C),无法保证原子性和持久性(A 和 D)
3、文档数据库
目前绝大部分文档数据库存储的数据格式是 JSON(或者 BSON)。
优势:
1. 新增字段简单 业务上增加新的字段,无须再像关系数据库一样要先执行 DDL 语句修改表结构,程序代码直接读写即可。
2. 历史数据不会出错 对于历史数据,即使没有新增的字段,也不会导致错误,只会返回空值,此时代码进行兼容处理即可。
3. 可以很容易存储复杂数据。
劣势:1、不支持事务。2、无法实现关系数据库的 join 操作。
4、列式数据库
优势: 特定场景下,a)节省 I/O;b)具备更高的存储压缩比,能够节省更多的存储空间。
劣势:频繁地更新多个列情况下,性能会很低。
适用场景:一般将列式存储应用在离线的大数据分析和统计场景中,因为这种场景主要是针对部分列单列进行操作,且数据写入后就无须再更新删除。原理就是大数据中的稀疏矩阵,和数据的低价值密度。
5、全文搜索引擎
全文搜索引擎能够基于 JSON 文档建立全文索引,然后快速进行全文搜索
缓存就是为了弥补存储系统在这些复杂业务场景下的不足,其基本原理是将可能重复使用的数据放到内存中,一次生成、多次使用,避免每次使用都去访问存储系统。缓存能够带来性能的大幅提升。
缓存的架构设计要点:
缓存穿透
缓存穿透是指缓存没有发挥作用,业务系统虽然去缓存查询数据,但缓存中没有数据,业务系统需要再次去存储系统查询数据。通常情况下有两种情况:
缓存雪崩
缓存雪崩是指当缓存失效(过期)后引起系统性能急剧下降的情况。缓存雪崩的常见解决方法有两种:更新锁机制和后台更新机制。
缓存热点
由于缓存的各种访问策略和存储的访问策略是相关的,因此上面的各种缓存设计方案通常情况下都是集成在存储访问方案中,可以采用“程序代码实现”的中间层方式,也可以采用独立的中间件来实现。
理解与思考
缓存很有用。软件系统中随处可见各种缓存。硬件,操作系统,数据库,web系统中都能看到缓存的应用。
制定缓存和失效的策略,是个技术活,也是用好缓存系统的关键。稍有不慎就自废武功。
1、PPC 每次有新连接就新开一个进程。
缺点:
2、TPC 每次有新连接就新开一个线程。
与PPC相比,线程更轻量级,创建线程的消耗比进程要少得多;同时多线程是共享进程内存空间的,线程通信相比进程通信更简单
缺点:
3、Reactor:I/O 多路复用结合线程池,完美地解决了 PPC 和 TPC 的问题,中文是“反应堆”。
Reactor 模式的核心组成部分包括 Reactor 和处理资源池(进程池或线程池),其中 Reactor 负责监听和分配事件,处理资源池负责处理事件。初看 Reactor 的实现是比较简单的,但实际上结合不同的业务场景,Reactor 模式的具体实现方案灵活多变,主要体现在:
Reactor 的数量可以变化:可以是一个 Reactor,也可以是多个 Reactor。
资源池的数量可以变化:以进程为例,可以是单个进程,也可以是多个进程(线程类似)。
最终 Reactor 模式有这三种典型的实现方案:
单 Reactor 单进程 / 线程
单 Reactor 多线程
多 Reactor 多进程 / 线程
以上方案具体选择进程还是线程,更多地是和编程语言及平台相关。例如,Java 语言一般使用线程(例如,Netty),C 语言使用进程和线程都可以。例如,Nginx 使用进程,Memcache 使用线程。
4、Proactor
“前摄器”,与其类似的单词是 proactive,含义为“主动的”。Reactor 可以理解为“来了事件我通知你,你来处理”,而 Proactor 可以理解为“来了事件我来处理,处理完了我通知你”。这里的“我”就是操作系统内核,“事件”就是有新连接、有数据可读、有数据可写的这些 I/O 事件,“你”就是我们的程序代码。
高性能集群的复杂性主要体现在需要增加一个任务分配器,以及为任务选择一个合适的任务分配算法。对于任务分配器,现在更流行的通用叫法是“负载均衡器”。
1、负载均衡分类
2、算法
负载均衡算法数量较多,而且可以根据一些业务特性进行定制开发,抛开细节上的差异,根据算法期望达到的目的,大体上可以分为下面几类。
任务平分类:负载均衡系统将收到的任务平均分配给服务器进行处理,这里的“平均”可以是绝对数量的平均,也可以是比例或者权重上的平均。
负载均衡类:负载均衡系统根据服务器的负载来进行分配,这里的负载并不一定是通常意义上我们说的“CPU 负载”,而是系统当前的压力,可以用 CPU 负载来衡量,也可以用连接数、I/O 使用率、网卡吞吐量等来衡量系统的压力。
性能最优类:负载均衡系统根据服务器的响应时间来进行任务分配,优先将新任务分配给响应最快的服务器。
Hash 类:负载均衡系统根据任务中的某些关键信息进行 Hash 运算,将相同 Hash 值的请求分配到同一台服务器上。常见的有源地址 Hash、目标地址 Hash、session id hash、用户 ID Hash 等。