高可用/高并发/高性能解决方案(3H)
2021-01-15 13:42:34 2 举报
AI智能生成
高可用、高并发和高性能是构建现代互联网应用的三大关键要素。高可用性解决方案确保系统在面临故障时仍能保持正常运行,通过数据冗余、负载均衡和故障切换等技术实现服务的持续可用。高并发解决方案则关注如何应对大量用户同时访问,采用分布式架构、缓存技术和异步处理等手段提高系统的处理能力。高性能解决方案致力于优化系统性能,包括硬件升级、代码优化和数据库调优等方面,以满足用户对响应速度和资源利用率的需求。综合运用这三方面的解决方案,可以打造出稳定、高效、可扩展的互联网应用,满足不断增长的业务需求。
作者其他创作
大纲/内容
HighAvailability
高可用
高可用
高可用集群搭建
redis集群搭建
Redis系列:搭建Redis集群(哨兵模式)
Redis系列:搭建Redis集群(集群模式)
Redis如何实现异地多活?
方案一
让业务进行写入MQ, 通过DTS同步组件进行多地域数据同步,
再基于MQ进行数据的分发
再基于MQ进行数据的分发
缺点:业务方需要自行实现数据分发功能,写入到MQ,对业务有较大侵入
方案二
通过Reader对Redis进行两地的数据同步(读取slave,写入master),
每个需要同步的地域都需要有一个对应的Reader来接收数据。
每个需要同步的地域都需要有一个对应的Reader来接收数据。
缺点:可扩展性差, 压力随着同步地域数增加而线性增加
方案三
Netflix开源实现的dynomite,通过代理层接受数据后写入各个需要同步的节点,
上层业务无感知,底层redis数据自动同步
上层业务无感知,底层redis数据自动同步
缺点:客户端需要优化添加Dyno Client(例如基于hash切分的redis集群方案就需要接入)、
服务端每个节点需要部署Dyno Node、数据存在冗余同步(无需同步的数据会被纳入同步)、
如果不冗余就需要接入方标准不一致,运维不统一(有些部署Dynomite ,有些不部署)、read/write性能较原生差异较大,
主要体现在write上,之间的差异随着node节点数越多越严重
服务端每个节点需要部署Dyno Node、数据存在冗余同步(无需同步的数据会被纳入同步)、
如果不冗余就需要接入方标准不一致,运维不统一(有些部署Dynomite ,有些不部署)、read/write性能较原生差异较大,
主要体现在write上,之间的差异随着node节点数越多越严重
Rabbitmq集群搭建
HAProxy+Keepalived(VIP)搭建Rabbitmq高可用镜像队列
使用federation搭建异地双活的mq集群
数据库集群搭建
数据库系列:搭建SqlServer AlwaysOn
数据库系列:搭建Mysql集群(一主多从、多主多从、双活模式、MHA模式)
搭建TiDB
Keepalived(VIP)搭建Nginx高可用
K8S集群搭建
二进制搭建K8S
搭建K8S集群
部署服务至K8S
傻瓜式制作docker镜像
高可用方案设计
RabbitMQ实现消息100%投递的详细设计和测试方案
HighConcurrency
高并发
高并发
Java并发
A01_实现线程的方式
原子类
CAS和AQS
正确创建和停止线程的方式及Volatile失效场景
锁:Synchronized、Lock、Volatile、CAS、Concurrent包
线程池与阻塞队列
ThreadLocal的用法和坑
ThreadLocal的用法
多线程竞争同一个变量
同一个线程无需显式调用
ThreadLocal的原理
数据结构
set、get 和 remove方法
ThreadLocal的问题
线程不安全的场景
线程池复用
用完记得remove
内存溢出问题
强弱虚软 引用
用完记得remove
Java进阶系列:Future的使用
Stream的使用
守护线程
数据库并发
数据库事务和锁
数据库系列:连接池的设计和使用
数据库系列:读写分离
数据库系列:分库分表
并行计算
Flink 专题
Kafka专题
Hadoop 专题
Spark 专题
Kylin 专题
并发限流
限流专题:限流概述
限流专题:Redis限流
限流专题:Nginx限流
限流专题:Rabbitmq限流
限流专题:Gateway限流
限流专题:Sentinel限流
缓存专题
缓存专题概览
构建二级缓存:Caffeine+Mysql
Guava Cache与Caffeine的区别
单点登录及Session共享/一致性问题
Redis缓存问题及解决方案
缓存雪崩
原因
由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,
在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,
而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃
在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,
而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃
解决办法
一
大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,
从而避免失效时大量的并发请求落到底层存储系统上
从而避免失效时大量的并发请求落到底层存储系统上
二
还有一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,
比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件
比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件
三
给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存
缓存穿透
原因
缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,
在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。
这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题
在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。
这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题
解决办法
一
布隆过滤器
查找的bit值均存在才认可,虽然有一定误差
二
如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,
但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,
这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴
但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,
这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴
缓存预热
原因
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,
先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决办法
一
数据量不大,可以在项目启动的时候自动进行加载
二
定时刷新缓存
缓存更新
原因
缓存清理策略
解决办法
自定义缓存清理策略
缓存降级
原因
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,
仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
解决办法
缓存热点 key
原因
热点 key 失效后大量线程重建缓存
解决办法
一
重建线程设置互斥锁
二
热点数据永不过期
redis冷热数据分离
Redis系列:构建本地和分布式二级缓存
高并发方案设计
秒杀系统的设计与实现
分布式锁
redis实现分布式锁
分布式锁
原理
互斥
synchronized失效
使用redis第三方锁
redission中有个方法,提供锁机制:setnx(set if not exist)
锁使用:
执行前获取锁、
执行后释放锁
执行前获取锁、
执行后释放锁
setnx原理
map<key,value>
如果存在key,则返回false;
如果不存在,则存放键值对,返回true;
如果存在key,则返回false;
如果不存在,则存放键值对,返回true;
对应于springboot中的
stringRedisTemplate.opsForValue().setIfAbsent
stringRedisTemplate.opsForValue().setIfAbsent
锁释放
问题一:业务代码执行出现异常,无法释放锁,导致的超时异常
使用try finally
问题二:jvm异常导致finally不能执行到?
给锁加时间,过期失效
同时为了保证原子性,获取锁和给锁加时间,应该是原子操作
问题三:每个类都要写相似的加解锁代码?
为了减少代码冗余,把分布式锁相关的代码做成接口和实现类复用
因为互斥,锁需要做成单例
问题四:如何保证/控制该类的加解锁顺序是匹配的?
比如A线程调用了加锁方法,B线程调用了解锁方法
比如A线程调用了加锁方法,B线程调用了解锁方法
因为加解锁不能做成原子操作
生成uuid做凭据,
加锁时,生成uuid放入redission的value值,
解锁时,获取redission的alue值与线程的uuid比较,相同则释放锁
以此来保证同一把锁
加锁时,生成uuid放入redission的value值,
解锁时,获取redission的alue值与线程的uuid比较,相同则释放锁
以此来保证同一把锁
因为锁类是单例的,uuid在加锁方法内是随即生成的,所以存在线程安全问题
多线程访问的时候,调用加锁方法,会产生多个uuid,
setnx未失效前,访问返回false,虽然获取锁失败,
但是存在重写uuid的可能,所以uuid必须放在threadlocal中,
多线程访问的时候,调用加锁方法,会产生多个uuid,
setnx未失效前,访问返回false,虽然获取锁失败,
但是存在重写uuid的可能,所以uuid必须放在threadlocal中,
保证别的线程不会拿到/复写当前线程的uuid
又因为thread复用问题,存在threadlocal污染的问题
在解锁时,对threadlocal做阅后即焚,清除掉
支持阻塞和非阻塞
上述直接return的方式会导致,线程用完,但是值还有剩余的情况
非阻塞
获取不到锁,直接return false
阻塞
问题五:如何让线程拿不到锁时,一直尝试获取锁?
加锁过程写成while循环
缺点:线程循环占有CPU资源
代码
可重入性
问题六:进程运行过程中,遇见同一把锁?
将锁修改为可重入锁
重入锁实现
先判断锁是否存在,不存在则创建锁,
存在,则依据threadlocal的uuid来判断是否为同一把锁
存在,则依据threadlocal的uuid来判断是否为同一把锁
锁超时
问题七:如果设置的超时时间小于业务处理时间,或者因为网络和排队的原因,
延长了时间,锁会不会自动释放,引起新的线程安全问题?
问题7.1:锁自动释放后,新的线程继续加锁,新锁被前一个线程执行完,释放了,引发线程安全问题
延长了时间,锁会不会自动释放,引起新的线程安全问题?
问题7.1:锁自动释放后,新的线程继续加锁,新锁被前一个线程执行完,释放了,引发线程安全问题
问题7.1不存在,因为不同线程的uuid不同,无法去解锁
异步延续过期时间:
新线程获取锁前,另起线程(守护)延续过期时间,
例如:每隔10s,刷新过期时间为20s
新线程获取锁前,另起线程(守护)延续过期时间,
例如:每隔10s,刷新过期时间为20s
解锁时,关闭该延续线程
如何标识守护的延续线程并在解锁时关闭它?
创建线程时,将该守护线程的线程号放入uuid,
释放锁的时候,通过uuid获取线程号,做interrupt
释放锁的时候,通过uuid获取线程号,做interrupt
开源框架
redission官方文档
细说Redis分布式锁:setnx/redisson/redlock
Redis系列
zookeeper实现分布式锁
数据库系列:数据库实现分布式锁
HighPerformance
高性能
高性能
应用性能
Java进阶系列:Java GC
Java进阶系列:JVM 调优
线上排查步骤及性能工具
性能测试
使用JMH进行微基准测试
使用Jmeter进行并发测试
Java进阶系列:Jmeter集群进行万级高并发压测
数据库性能
索引及其优化
索引
名词
主键索引
唯一索引
聚簇索引和非聚簇索引
非聚簇索引一定会回表查询吗
回表查询
覆盖索引
联合索引
索引下推
innodb引擎的表,索引下推只能用于二级索引
索引代价
索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。索引不是万能的,索引可以加快数据检索操作,但会使数据修改操作变慢。每修改数据记录,索引就必须刷新一次。为了在某种程序上弥补这一缺陷,许多SQL命令都有一个DELAY_KEY_WRITE项。这个选项的作用是暂时制止MySQL在该命令每插入一条新记录和每修改一条现有之后立刻对索引进行刷新,对索引的刷新将等到全部记录插入/修改完毕之后再进行。
mysql执行计划:explain
新增、删除和修改会引起页分裂和合并
页分裂和合并:InnoDB会在索引树上加写锁(x-latch)。
在操作频繁的系统中这可能会是个隐患。它可能会导致索引的锁争用(index latch contention)。
如果表中没有合并和分裂(也就是写操作)的操作,称为“乐观”更新,只需要使用读锁(S)。
带有合并也分裂操作则称为“悲观”更新,使用写锁(X)
在操作频繁的系统中这可能会是个隐患。它可能会导致索引的锁争用(index latch contention)。
如果表中没有合并和分裂(也就是写操作)的操作,称为“乐观”更新,只需要使用读锁(S)。
带有合并也分裂操作则称为“悲观”更新,使用写锁(X)
ID选择的不好,如果以ID作为主键插入,会导致大量的分页和合并问题,从而造成性能问题
mysql的数据结构为什么选择b+,
它和b树的主要区别
它和b树的主要区别
b+树的非叶子节点不存其他信息,只存index,因此层高很低,存储的内容多
数据库系列:编程规范及调优语句
数据库系列:数据库磁盘空间管理
JVM性能
如何写出性能良好的程序(合理利用JVM内存模型)
如何判断是性能良好的程序(JVM性能查看工具的使用)
内存分析工具介绍及线上问题排查步骤
中间件性能
Redis系列:Redis内存回收
云原生性能
云原生:Harbor垃圾回收
监控专题
Zabbix应用监控工具的使用
手把手搭建EFK日志级别的监控系统
高性能方案设计
框架专题
Netty专题
预备知识:NIO及网络模型分析
Netty简单入门:获取请求、多客户端连接与通信、心跳检测、长链接
Kafka专题
Tomcat专题
微服务体系
服务发现
服务治理
网关
0 条评论
下一页