一、CDN负载均衡的几种分流方案
1、A记录
2、CDN节点
3、正向代理、反向代理
4、负载均衡
二、服务 高并发、高并行 方案
1.单体服务服(务内并发、并行)
在服务内部搭建
2.单体服务做集群
同质的(相同的配置,运行相同的程序、对外提供同样的服务)
有状态、无状态
缺点:对于有状态的服务比如定时任务,存在问题
3.单一服务节点做集群
例如:定时任务抽离出单独的服务,单独放在一个节点上部署。
优点:能解决有状态的的问题
缺点: 容错性差(单一节停止数据就会丢失)
4.信息共享节点集群
例如:多节点访问相同的信息池(多节点连接相同DB,Redis…)
优点:提升计算能力(能解决瓶颈在计算能力的服务中)
缺点:存储会成为瓶颈,需要加入分布式锁对数据一致性的保护
5.信息一致节点集群(能解决瓶颈在数据存储的集群中)
即服务集群、DB集群,DB数据同步,不同的服务节点连接不同的DB节点
读多写少时适用(强一致性、最终一致性) 二选一
6.分布式系统(应用之间有从属关系)
拆应用,如果子应用还是扛不住并发,那么做集群。
优点:
缺点:分布式事务,分布式锁
7.微服务系统
服务和服务之间是独立的
三、服务内高并发
1、CAP :分布式系统的起点
-
Consistency 一致性
强一致:
弱一致:能容忍部分或全部看不到最新的数据
最终一致:经过一段时间后一致
-
Availability 可用性;
在数据同步的时间,如果要保证一致性,那么同步的时刻就不能保证可用性;如果保证可用性那么读取数据时正值数据同步,那么读取的数据就有可能不是最新数据,所以 一致性和可用性不能同时满足。
-
Partition tolerance 分区容错性;
分区容错是必须具备的,因为如果不具备就如单体应用一样了。
AP: euraka
CP: zk
2、进程、线程、协程
多进程:docker
多线程:
提升效率:IO读数据,更多利用cpu计算
实现异步:主线程提前释放,不重要的耗时操作子线程延后处理,降低平均响应时间,并发能力就会提升
线程的计算:线程数=cpu核数 x cpu使用率 x (1+w/c) .实际工作cpu使用率不要超过70%
协程:线程中的线程
四、缓存
1、使用场景:
缓存只是 在 调用方和 数据提供方之间的一个 暂存方
1.1、读多写少用缓存
1.2、查询元数据时间特别长的场景
2、缓存位置
2.1、前端
2.2、CDN 静态数据存储做缓存
2.3、服务端缓存(redis,localcache,guawa)
2.4、数据缓存(创建宽表、添加冗余字段)
3、缓存中 数据来源 和 更新机制
2.1、缓存中数据来源
查询的时候如果缓存中没有就从数据提供方获取,然后存入缓存并返回结果。
2.2、更新机制:
-
被动更新,固定时间过期
缺点:数据一致性差
-
主动更新
1、cache aside:
先更新数据库再删缓存(小概率数据不一致,因为读比写快)
2、双写一致性
延迟双删
3、Read/Write Through(能保证强一致性)
直接将结果写入缓存,再从缓存同步到数据库。调用方只需要和缓存交互。
4、Write Behind(不能保证一致性)
直接将结果写入缓存,再异步到数据库。
4、清理机制
1、定期任务扫描删除
2、自动时效清理
3、数目阈值清理
FIFO:
LRU:
4、软应用:
5、缓存风险点
清理机制+更新机制 两方面考虑解决问题
6、缓存穿透
缓存没有,数据库也没有。
解决方案:
①在缓存中存一份空数据 key有值,value为null
7、缓存雪崩
大量缓存突然失效,引发数据库压力骤增
解决方案:
①阈值式清理:阈值设置的高可以缓解
②时效式清理:避免缓存再同一时刻失效,过期时间=固定值+随机值,实现错峰失效
③软引用清理:当空间紧张的时候,缓存占据的空间会被回收。
8、缓存击穿
高频访问的数据缓存过期
解决方案
使用如下更新机制
Read/Write Through(能保证强一致性)
Write Behind(不能保证一致性)
9、缓存预热 解决方案
1、启动系统时,提前加载一些数据,预加载。
2、缓存服务启动后,脚本直接灌入数据,init
10、缓存方式
1、客户端缓存
- 浏览器
- Android
- IOS
- cdn:通用性,可有作为静态数据的内容放在CDN
2、服务端缓存:
redis、localcache、guava
3、数据库缓存
11、写缓存
流量的【削峰、填谷、限流、熔断、降级】
写缓存的实践:redis (发布订阅),消息队列(发布订阅),数据库(先生成数据后期再计算统计,适用于耗时高但对于实时性不高的场景)
灰度发布:配置灰度规则,规则中请求的阈值根据阈值的不同调用不同的系统。
五、数据库
1、数据库设计要考虑的几点要素
1.1、数据库选型(MySQL,Orocal)
散列数据库-HBase
键值数据库-Redis,memcached
文档数据库mongoDB
时序-influxDB
搜索-ES
1.2、存储引擎的选择
考虑(事务、索引-innodb不支持hash索引、锁类型,字段类型)
1.3、表结构设计
表、字段、索引
1.4、数据库优化
索引、历史数据拆分
2、设计的几种方式
2.1、先设计表结构——>生成对象 ;
优点:扩展性高适合工期不急,稳定性高,扩展性要求高的项目。
2.2、先设计对象——> 对象关系——> 表结构
允许数据冗余时,存在数据不一致(因为存在冗余字段更新一部分表的情况)
3、数据库设计的三范式
属性:字段
记录:一条记录
超键:在关系中能唯一标记记录的属性集
候选键:不含多余属性的超键
主键:从候选键中任选一个叫主键
主属性:
外键:
第一范式:原子性,即列不可再分割;
从业务出发分析列时否不可在分割,否则有可能会导致数据表不可用,后期拆分改动太大甚至无法实现;最好有前瞻性的拆分属性,以及与产品需求的理解程度。
第二范式:非主属性必须完全依赖与候选键,否则会出现数据冗余,进而出现数据不一致
第三范式:非主属性不依赖于其他非主属性,即可确保每一列都是和主键相关,而不是列于列之间存在间接相关,否则维护关系时有损耗的
BCNF巴斯范式
第四范式:禁止主键和非主键有一对多的关系,否则会出现一个主键对应多条数据库,增加记录数据量,出现存储瓶颈
如果存在则用逗号分隔等等方式
第五范式:表不能再差分为多表
反范式:符合三范式的表在关联查询时会存在多个表的关联查询,增加查询时间,此时可以不遵循三范式做冗余字段优化查询速度
缺点:增大了存储空间,增加了风险,如果操作有事务保证,那么效率会变得很低,不用事务又会出现数据不一致
4、数据库优化
4.1、读
加索引——》 表分区——》分库分表——》读写分离
4.2、写
一致性 | 高效率 二选一
保证一致性=事务
保证高并发的一致性=锁
不同的隔离级别加锁情况不同
悲观锁:利己锁
S锁:share lock 共享锁,读锁
加了S锁可以再加S锁,但是不可以加X锁
select * from user lock in share mode
X锁:Exclusive Lock 排他锁,写锁
加了X锁后其他对象拿不到任何锁
死锁:
U锁
行锁: where id= 1
表锁: update set ss=1 不写where
页锁:锁定相邻的记录
乐观锁:读多写少时适合用乐观锁
乐观锁机制:重复读写(牺牲操作方性能而提升被操作方并发性的策略即时利用它的一种策略)
①读取被操作对象记录某个特征信息(版本号、操作的属性)
②使用读取到的对象进行业务处理得到新的对象
③新对象写回数据库
④数据校验(校验特征信息)
⑤如果校验一致则成功写回,如果校验不一致则退回在重新读写
5、事务
5.1、ACID是如何保证的?
-
原子性(Atomicity) write a head log
-
一致性(Consistency) 外键唯一索引等
-
隔离性(Isolation) 锁
5.2、事务的并发问题
脏读(Dirty read)
当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
不可重复读
(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
幻读
(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读
不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读
5.3、事务的隔离级别
事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。
事务的隔离级别从低到高依次为
READ UNCOMMITTED:(一级封锁协议)查询时不加锁,写时加S锁
READ COMMITTED:(二级封锁协议)读时加S锁,读完后立即释放。写时加X锁,事务结束后释放。
REPEATABLE READ:(三级封锁协议)更新前加X锁,读时加S锁,事务完成后释放锁。
SERIALIZABLE,更新数据前对整个表加X锁,读数据时整个表加S锁。给操作的行加X锁能实现串行化,但是性能急剧下降
隔离级别越低,越能支持高并发的数据库操作。
6、查询优化
6.1、索引
6.2、表分区
概念:将一张表的文件可以查分成多个文件用navicat即可拆分,差分后对外提供依然是一张表(分区是一种分流时实现)
优点:不影响业务,放在不同的磁盘上,可以备份、回复、逐步进行。
分区策略:Range,List,Hash,key
注意事项:结合分区策略,查询规则。尽量保证查询只会落到一个分区中,减少跨区合并的开销。
6.2、分库分表
6.2.1:分库
将一个数据库拆分为多个库,拆分后每个库或表数据少了那么检索就会变快,同事行锁表锁的范围也就缩小了。从而提高效率。
分库方式①:
分库方式②:
6.2.2:分表
分表方式①:水平拆分-》拆分后每个表的结构相同
分表方式②:垂直拆分-》拆分后每个表只包含原表的部分列
场景:更具业务选择拆分方式
如果获取全部列的查询多-》水平拆分
如果获取某个列的查询多-》则垂直拆分
原则:避免跨表操作
路由操作:将指向一个表的请求分到多个表指定表
拼接操作:将多个表的查询结果集拼接
6.3、读写分离
读写分离也是数据库备份的过程
6.3.1、为什么使用读写分离
X锁:操作阻止了其他操作的读和写;S锁:可以并发读取;致使并发量低,所以需要读写分离。
主库负责写,从库负责读
6.3.2、路由:
针对select操作指到从库;针对修改操作指到主库
6.3.3、数据同步:
将主库修改的数据同步到从库中,避免从库的写操作。
Ⅰ、同步的主要方式:binlog
1、主库开启体制记录功能,写操作记录到binlog
2、binlog被发到从库中,写入从库的RelayLog
3、从库解析RelayLog重现效果。
Ⅱ、同步模式:
1、statement:日志都是SQL语句,如果涉及到now() 这样的时间函数就会数据不一致
2、Row模式:记录的是数据记录
3、mixed:混合模式
Ⅳ、主从复制的延迟问题:
日志:主库记录——》 传输到从库 ——》 从库解析——》写入从库
①异步复制:会降低写的响应时间,可能导致数据不一致或丢失
②半同步赋值:写入主库后,日志至少被送到一个从库上,写才返回,防止数据丢失。
实现方式:MySQL有Goog开发的半同步插件
③全同步复制:全写完才响应用户
六、可靠性
串联系统的可靠性、并联系统的可靠性(即做负载均衡,解决单点问题)
提高可靠性的方案:
1、消除单点依赖(即做并联)
2、Read Write Through 此方式调用方之和缓存打交道,而缓存负责保证自身和数据提供方一致(也是串联)
3、Cache Aside 调用方先读缓存,如果缓存中没有数据就去数据库中读取(相当于有旁路并联)
4、主备方式
5、等价式
6、分流
7、并行
8、并发
9、缓存
10、隔离:
隔离属于牺牲自己的方式,串联系统中,一个方法顺序调用了多个方法,而某个方法响应有问题(如超时)此时可以将此方法的调用放入线程池中,利用线程池来控制此方式调用的并发数。
缺点:线城市隔离时会有线程池的创建、回收、切换、等耗资源的情况
11、限流
①固定时间窗口限流
为了避免突刺,可以尽量将时间窗口缩小,小到只允许一个请求。
漏桶限流算法:小到给的请求都能处理
②令牌桶限流
12、降级:
只保留必要功能
降级策略:
①同步转异步
②功能裁剪
③禁止写
④分用户降级
⑤工作量证明降级Row
⑥禁止读库(读缓存)
出发手段:
自动:因失败概率过高、因限流
手动:
13、熔断:
当其他被当前服务调用的服务有问题,则牺牲其他被调用的服务,熔断一般式自动完成的
14、恢复:
概念:
隔离、限流、降级、熔断都是暂时性手段,保护系统。恢复就是撤出限流、消除降级、关闭熔断。当系统感知到了正常,则恢复。
恢复的手段:
1、预测(先少量开放)
2、恢复阶级:逐渐增加请求(本质参考限流)