高性能
存储高性能
关系数据库
读写分离
- 主从模式
- 存在的问题
- 从节点同步主节点数据可能存在延迟,导致读不到。
- 解决方法
- 先读从节点,无数据再读主节点,关键数据可直接读取主节点
- 存在的问题
分库分表
- 使用场景
- 数据量达到千万以上
- 即使有索引索引也会很慢
- 文件很大,备份和恢复时间过长
- 文件过大,丢失数据量大的风险高
- 业务分表
- join 问题
- 事务问题
- 成本问题
- 分表
- 垂直分表
- 例如:婚恋网站人物列表只需要有 name,age等属性即可,无需过的属性导致查询度过慢
- 水平分表:会引入更多的复杂度
- 路由
- 范围路由
- hash 路由
- 配置路由
- join
- 只能多次 join 解决
- count
- 多次 count
- 记录数表,新增表进行处理
- order by
- 多次 order by 进行聚合
- 路由
- 垂直分表
实现方法
- 程序代码封装
- 特点
- 实现简单,可根据业务进行定制
- 开发工作量大
- 故障问题不好处理
- 开源方案
- 淘宝的 TDDL
- 中间件封装
- 特点
- 支持多种编程语言
- 支持完整的数据库协议和 SQL 规范,内容丰富导致 bug 可能会较多
- 所有的数据库操作都变成对中间件的操作,会影响性能
- 主从切换无感知,有中间件解决
- 特点
- 方案
- MySQL Proxy
- MySQL Router
- Atlas
- ShardingSphere
- 实现复杂度
- 因为分表的 order by 等需要聚合还要识别 SQL 的关键字,所以实现比较困难
NoSQL
关系数据库的缺点
- 1.行记录无法存储数据结构
- 2.关系数据库的 schema 扩展不方便
- 3.关系数据库大数据情况下 I/O较高
- 4.关系数据库的文档搜索能力较弱
- 全文搜索的条件可以随意排列组合,如果通过索引满足则会添加大量的索引
- 模糊匹配的方式无法满足,只能用 like,like 是全表扫描速度较慢
分类
- K-V 存储: Redis
- 文档数据库: MongoDB
- 列式数据库:Hbase
- 全文搜索引擎:Elasticsearch
K-V存储
redis 目前问题:不支持完整的 ACID
文档数据库
- 优势
- 新增字段简单
- 历史数据不会出错
- 可以很容易存储复杂的数据结构
列式数据库
- 优势
- 业务同时读取多列效率较高,因为这些列式存储在一起的,一次磁盘读写就能把一数据的所有列都读取到内存中
- 可以一次写多个列
全文搜索引擎
- 原理是倒排序,即内容和文件建立关联关系
缓存
使用场景
- 需要复杂的运算之后得到结果,存储系统也无能为力
- 读多写少,存储系统有心无力
缓存穿透:缓存中没有数据导致去查询数据库
- 存储数据不存在,可通过设置默认值解决
- 缓存数据生成耗费大量时间,
缓存雪崩:缓存失效后导致系统性能的下降
- 更新锁,只能一个线程去更新
- 后台更新,缓存本身设置为永久有效,定时任务或消息通知去更新缓存数据
缓存热点:热点数据进行缓存
计算高性能
单服务器高性能
关键之一是网络编程模型
- 服务器如何管理连接
- 服务器如何处理请求
- 解决方向
- I/O 模型
- 进程模型
PPC(Process per Connection )
- 流程
- 1.父进程接受连接
- 2.父进程 fork 子进程
- 3.子进程处理请求的读写
- 4.子进程关闭连接
- 弊端
- fork 代价过高
- 父子进程通信复杂
- 进程数量增大后多操作系统压力过大
prefork
- 进行连接时才 fork 会让用户感觉卡段,所以出现了预fork优化流程,但是也会出现“惊群”问题,即只会有一个子进程去连接,但是会唤起所有的子进程去尝试,浪费线程上下文切换
- 也存在父子线程通信复杂问题;进程数量增大后导致操作系统压力过大
TPC(Thread per Connection)
- 流程
- 1.父进程接受连接
- 2.父进程创建子线程
- 3.子线程处理请求的读写
- 4.子线程关闭连接
- 虽然解决了线程通信复杂和fork 代价过高问题,但是也引入的新的问题
- 高并发情况下依然存在性能问题
- 虽无需进程间通信了,但线程间的互斥和共享又引入了新的复杂度,可能一不小心出现死锁问题
- 可能出现某个线程异常导致整个进程的结束
- 还是存在 CPU 线程调度和切换代价问题
PreThread
- 流程
- 1.主线程accept,然后将连接交给某个线程处理
-
- 子线程都尝试去accept,最终只有一个子线程accept成功
Reactor
- 要解决的问题
- 为了解决PPC多次创建进程问题,可以通过进行池化,实现资源的节约,但是引入后出现了新的问题
- 进程如何高效的处理多个连接:一个连接一个进程时可以通过阻塞进行读取,但是一个进程连接多个连接时就不能阻塞到某个进程上,解决这个问题的方法就是将read改为非阻塞的,进行不断的轮询多个连接,但是解决方式并不优雅,轮询也是小号CPU的,如果一个进程的连接数过多,轮询的效率也不高,为了解决这个问题想到了只有连接上有数据的时候进程采取处理,这就是I/O多路复用技术的来源
- 两个关键实现点
- 当多条连接共用一个阻塞对象后,进程只需要在一个阻塞对象上等待,而无需再轮询所有的连接
- 当某条连接有新的数据可以处理时,操作系统会通知进程,进程从阻塞状态返回,开始进行业务处理
- 为了解决PPC多次创建进程问题,可以通过进行池化,实现资源的节约,但是引入后出现了新的问题
- 模式的核心
- reactor:监听和分配时间
- 处理资源线程池:负责处理时间
- 实现方案
- 单Reactor 单进程/单线程
- Reactor对象通过select监控连接事件,收到事件通知后通过dispatch进行分发
- 如果是连接建立的事件,则由Acceptor处理,Acceptor通过accept接受连接,并创建一个Handler来处理连接后续的各种事件
- 如果不是连接建立事件,则Reactor会调用连接对象的Handler(第2步中创建的hadnler)进行响应
- Handler会完成read-->业务处理-->send的完整业务流程
- 优点:简单,没有进程间通信,没有进程竞争,全部都在同一个进程中完成
- 缺点
- 只有一个进程,无法发挥多核CPU的性能
- 只能部署多个系统来利用多核CPU,这样会带来运维复杂度
- handler在处理某个连接的业务时,整个进程无法处理其他连接的事件,很容易导致性能瓶颈
- 适用场景
- 业务处理速度非常快的,例如: Redis
- 单Reactor 多线程
- 1.主线程中,Reactor对象通过select监听连接事件,收到事件后通过dispatch进行分发
- 多Reactor 多进程/线程
- 单Reactor 单进程/单线程
Proactor
集群高性能
负载均衡器的分类
- DNS负载均衡:不同地区的用户访问同一个域名返回不同的ip
- 优点
- 简单、成本低
- 就近访问,提升访问速度
- 缺点
- 更新不及时
- 扩展性差
- 分配策略比较简单
- 优点
- 硬件负载均衡:F5和A10
- 优点
- 功能强大
- 性能强大
- 稳定性高
- 支持安全防护
- 缺点
- 价格昂贵
- 扩展能力差
- 优点
- 软件负载均衡
- 分类
- nginx:7层负载均衡
- LVS:是linux内核的4层负载均衡
- 优点
- 简单
- 便宜
- 灵活
- 缺点
- 性能一般
- 功能没有硬件负载均衡那么强大
- 一般不具备防火墙和防DDOS攻击等安全功能
- 分类
负载均衡器架构
-
- DNS负载均衡实现地理级别的负载均衡
-
- F5实现集群级别的负载均衡
-
- 软件负载均衡实现机器级别的负载均衡
负载均衡器算法
- 任务平分类:可以决定平均分配到各个服务器上,也可以是按照比例或者权重
- 负载均衡类:根据服务器的负载来进行分配,这里的负载不一定是CPU负载,而是系统当前压力,可以是CPU负载来衡量,也可以是连接数、I/O使用率、网卡吞吐量等来衡量系统的压力
- 性能最优类:根据服务器的响应时间来进行任务分配,优先分配给响应最快的服务器
- Hash类:根据某些信息进行Hash运算,分配到指定服务器上。常见的有源地址Hash、目标地址Hash、session id hash、用户id Hash等