【架构学习(三)】高性能存储计划流程

[复制链接]
发表于 2026-2-12 04:30:38 | 显示全部楼层 |阅读模式

媒介


作为后端开辟应该对团体体系架构有肯定相识。以是必要学习有关软件体系架构知识。我接纳读书的方式去相识团体软件体系架构,所读书名《从零开始学架构》。
学习目标:
1.架构计划目标及复杂度泉源
2.架构计划流程
3.高性能架构
4.CAP理论和FMEA方法
5.高可用架构
6.可扩展架构
7.微服务架构最佳实践
8.互联网架构技能
写此博客的目标:
1.完成学习目标
2.对书中内容举行总结,得到本身的阅读心得
3.方便其他入门小同伴快速得到干货
4.方便本身回首架构知识
前几期帖子:
【架构学习(一)】架构计划目标及复杂度泉源
【架构学习(二)】架构计划流程

一、高性能DB集群

读写分离

什么是高性能数据库集群两大特点?
读写分离”,分散存储压力;
分库分表”,分散访问压力和和存储压力。
什么是读写分离实用场景呢?
实用场景:读多写少,举例:消息记载业务,比如微博品评。
不得当场景:实时性高的业务,比如登录注册(实时性和正确性高的业务可以接纳直接读主库,正确性不高可以引入缓存计划)。
读写分离的根本实现?
数据库服务器搭建主从集群,一主一从、一主多从都可以。
1)数据库主机负责读写操纵,从机只负责读操纵。
2)数据库主机通过复制将数据同步到从机,每台数据库服务器存储了全部的业务数据。
3)业务服务器将写操纵发给数据库主机,将读操纵发给数据库从机。
读写分离,既然分散了读写压力,那这种计划的复杂度体现在?
复制延长
主从复制延长大概到达1秒,假如有大量数据同步,延长1分钟也是有大概的。主从复制延长会带来一个标题:假如业务服务器将数据写入到数据库主服务器后立即(1秒内)举行读取,此时读操纵访问的是从机,主机还没有将数据复制过来,到从机读取数据是读不到最新数据的,业务上就大概出现标题。这大概有些实时业务场景要求读主库。
怎样办理复制延长标题?
办理主从复制延长有几种常见的方法:
1.读从机失败后再读一次主机
这就是通常所说的“二次读取”,二次读取和业务无绑定,只必要对底层数据库访问的API举行封装即可,实当代价较小,不敷之处在于假如有很多二次读取,将大大增长主机的读操纵压力。比方,黑客暴力破解账号,会导致大量的二次读取操纵,主机大概顶不住读操纵的压力从而瓦解。
2.关键业务读写操纵全部指向主机,非关键业务接纳读写分离
比方,对于一个用户管理体系来说,注册+登录的业务读写操纵全部访问主机,用户的先容、爱好、品级等业务,可以接纳读写分离,由于纵然用户改了本身的自我先容,在查询时却看到了自我先容照旧旧的,业务影响与不能登录相比就小很多,还可以忍受。
分配机制
将读写操纵区分开来,然后访问差异的数据库服务器,一样平常有两种方式:步伐代码封装和中央件封装。
1.步伐代码封装
步伐代码封装指在代码中抽象一个数据访问层,实现读写操纵分离和数据库服务器毗连的管理。个人明白就是在步伐与mysql交互的时间,是通过代码(较为底层方法操纵jdbc)编写。

2.中央件封装
中央件封装指的是独立一套体系出来,实现读写操纵分离和数据库服务器毗连的管理。中央件对业务服务器提供SQL兼容的协议,业务服务器无须本身举行读写分离。对于业务服务器来说,访问中央件和访问数据库没有区别,毕竟上在业务服务器看来,中央件就是一个数据库服务器。(万事不觉就加一层)

举例:
订餐体系,在高峰时期,操纵数据库很频仍。但当前mysql只有一台,用户必要去下单订餐,而配景管理职员也必要看当前体系的毕竟环境。出现磁盘IO报警标题。
需求分析:
必要办理磁盘IO报警,应对将来预期的业务需求增长。
办理方法:
读写分离,用户侧全部读写接纳主库,配景管理职员写走走主库,读走从库。
分库分表

分库分表分散存储压力
重要体现在这几个方面:
数据量太大,读写的性能会降落,纵然有索引,索引也会变得很大,性能同样会降落。
数据文件会变得很大,数据库备份和规复必要淹灭很长时间。
数据文件越大,极度环境下丢失数据的风险越高。(这也是为什么要求数据库为什么会有几万万归档)
单台服务器性能怎么样?
单台mysql服务器可以大概支持10w用户量级查询,当单台mysql服务带到60%承载本领就必要思量分库(正常mysql cpu到40%就会报警)
分库分表的总体思绪是什么样的?
空间-》时间
单表-》多表同库-》分库
业务分库
业务分库指的是按照业务模块将数据分散到差异的数据库服务器。
1.join查询标题,跨库不能利用join查询
2.变乱标题,同库差异表可以利用变乱,分布式变乱性能低不保举利用
3.本钱标题,增长呆板
分表方案
单表数据拆分有两种方式:垂直分表和水中分表。
为了形象地明白垂直拆分和水平拆分的区别,可以想象你手里拿着一把刀,面对一个蛋糕切一刀:
1)从上往下切就是垂直切分,由于刀的运行轨迹与蛋糕是垂直的,如许可以把蛋糕切成高度相称(面积可以相称也可以不相称)的两部门,对应到表的切分就是表记载数类似但包罗差异的列。
2)从左往右切就是水平切分,由于刀的运行轨迹与蛋糕是平行的,如许可以把蛋糕切成面积相称(高度可以相称也可以不相称)的两部门,对应到表的切分就是表的列类似但包罗差异的行数据。

1.垂直分表
垂直分表得当将表中某些不常用且占了大量空间的列拆分出去。
垂直分表引入的复杂性重要体现在表操纵的数目要增长。比方,原来只要一次查询就可以获取的字段,现在必要两次查询。
2.水中分表
水中分表相比垂直分表,会引入更多的复杂性,重要体现在下面几个方面:
1)范围路由:选取有序的数据列(比方,整形、时间戳等)作为路由的条件,差异分段分散到差异的数据库表中。以最常见的用户ID为例,路由算法可以按照1000000的范围巨细举行分段.范围路由的缺点就是数据分配不均。
2)Hash路由:选取某个列(大概某几个列组合也可以)的值举行Hash运算,然后根据Hash结果分散到差异的数据库表中。同样以用户ID为例,假如我们一开始就规划了10个数据库表,路由算法可以简朴地用user_id % 10的值来表现数据所属的数据库表编号,ID为985的用户放到编号为5的子表中,ID为10086的用户放到编号为6的字表中
hash路由的缺点就是增长新的表数据必要重新分配(存在数据迁移标题,头大了)
3)设置路由:设置路由就是路由表,用一张独立的表来记载路由信息。同样以用户ID为例,我们新增一张user_router表,这个表包罗user_id和 table_id 两列,根据user_id就可以查询对应的 table_id。
设置路由的缺点就是必须多查询一次,会影响团体性能。
4)join 操纵
水中分表后,数据分散在多个表中,假如必要与其他表举行join查询必要在业务代码大概数居库中央件中举行多次join查询,然后将结果归并。
5)count()操纵
count()相加:详细做法是在业务代码大概数据库中央件中对每个表举行count()操纵,然后将结果相加。这种方式实现简朴,缺点就是性能比力低。比方,水中分表后切分为20张表,则要举行20次count(*)操纵,假如串行的话,大概必要几秒钟才气得到结果。
记载数表︰详细做法是新建一张表,假如表名为“记载数表”,包罗table_name、row_count两个字段,每次插入大概删除子表数据乐成后,都更新“记载数表”,但必要每次更新业务表的同时更新记载表对数据库有压力。
6)order by 操纵
水中分表后,数据分散到多个子表中,排序操纵无法在数据库中完成只能由业务代码大概数据库中央件分别查询每个子表中的数据,然后汇总举行排序。(这点我想到了es)

二、NoSQL

nosql办理的是sql以下标题
1)关系数据库存储的是行记载,无法存储数据结构。
2)关系数据库的schema扩展很不方便,比如操纵不存在列会报错,扩展必要ddl。
3)关系数据库的全文搜刮功能比力弱,like扫表性能非常低(感觉这条才是必要nosql的核心)。
常见的NoSQL方案分为4类。
1)K-V存储:办理关系数据库无法存储数据结构的标题,以 Redis为代表。
2)文档数据库:办理关系数据库强schema束缚的标题,以MongoDB为代表。
3)列式数据库:办理关系数据库大数据场景下的I/O标题,以 HBase为代表。
4)全文搜刮引擎:办理关系数据库的全文搜刮性能标题,以Elasticsearch为代表。
K-V存储
K-V存储的全称是Key-Value存储,此中Key是数据的标识,和关系数据库中的主键寄义一样,Value 就是详细的数据。
Redis是K-V存储的范例代表,它是一款开源(基于BSD允许)的高性能K-V缓存和存储体系。Redis的 Value是详细的数据结构,包罗string、hash、list、set、sorted set、bitmap和hyperloglog,以是常常被称为数据结构服务器。
Redis的缺点重要体现在并不支持完备的ACID变乱,Redis固然提供变乱功能,但Redis的变乱和关系数据库的变乱不可等量齐观,RedliS的变乱不能包管原子性和恒久性(A和D)(可以明白redis的变乱就是一个脚本,当下令实验失败了,其他下令会继承实验)。
文档数据库
为相识决关系数据库schema带来的标题,文档数据库应运而生。文档数据库最大的特点就是no-schema,可以存储和读取恣意的数据。现在绝大部门文档数据库存储的数据格式是JSON(大概BSON),由于JSON数据是自形貌的,无须在利用前界说字段,读取一个JSON中不存在的字段也不会导致SQL那样的语法错误。
1.新增字段简朴
业务上增长新的字段,无须再像关系数据库一样要先实验DDL语句修改表结构,步伐代码直接读写即可。
2.汗青数据不会堕落
对于汗青数据,纵然没有新增的字段,也不会导致错误,只会返回空值。
文档数据库no-schema的特性带来的这些上风也是有代价的,最重要的代价就是不支持变乱,无法实现关系数据库的join操纵。
列式数据库
列式数据库就是按照列来存储数据的数据库,与之对应的传统关系数据库被称为“行式数据库”,由于关系数据库是按照行来存储数据的。
关系数据库按照行式来存储数据,重要有以下几个上风:
业务同时读取多个列时服从高,由于这些列都是按行存储在一起的,一次磁盘操纵就可以大概把一行数据中的各个列都读取到内存中。
可以大概一次性完成对一行中的多个列的写操纵,包管了针对行数据写操纵的原子性和划一性;否则假如接纳列存储,大概会出现某次写操纵,有的列乐成了,有的列失败了,导致数据不划一。
全文搜刮引擎
传统的关系型数据库通过索引来到达快速查询的目标,但是在全文搜刮的业务场景下,索引也无能为力,重要体现在:
1)全文搜刮的条件可以随意分列组合,假如通过索引来满足,则索引的数目会非常多。
2)全文搜刮的含糊匹配方式,索引无法满足,只能用like查询,而like查询是整表扫描,服从非常低。

1.全文搜刮根本原理(就是es的原理)
全文搜刮引擎的技能原理被称为“倒排索引”(Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,其根本原理是创建单词到文档的索引。之以是被称为“倒排”索引,是和“正排“索引相对的,“正排索引”的根本原理是创建文档到单词的索引。我们通过一个简朴的样例来阐明这两种索引的差异。
2.全文搜刮的利用方式
全文搜刮引擎的索引对象是单词和文档,而关系数据库的索引对象是键和行,两者的术语差异很大,不能简朴地等同起来。因此,为了让全文搜刮引擎支持关系型数据的全文搜刮,必要做一些转换操纵,即将关系型数据转换为文档数据。
现在常用的转换方式是将关系型数据按照对象的情势转换为JSON文档,然后将JSON文档输入全文搜刮引擎举行索引。我同样以步伐员的根本信息表为例,看看怎样转换。
总结
业务存储用db,淘汰db压力用redis,快速搜刮用es

三、高性能缓存架构


缓存穿透

缓存穿透是指缓存没有发挥作用,业务体系固然去缓存查询数据,但缓存中没有数据,业务体系必要再次去存储体系查询数据。通常环境下有两种环境:
1.存储数据不存在
第一种环境是被访问的数据确实不存在。一样平常环境下,假如存储体系中没有某个数据,则不会在缓存中存储相应的数据,如许就导致用户查询的时间,在缓存中找不到对应的数据,每次都要去存储体系中再查询一遍,然后返回数据不存在。缓存在这个场景中并没有起到分担存储体系访问压力的作用。
通常环境下,业务上读取不存在的数据的哀求量并不会太大,但假如出现一些非常环境,比方被黑客攻击,故意大量访问某些读取不存在数据的业务,有大概会将存储体系拖垮。
这种环境的办理办法比力简朴,假如查询存储体系的数据没有找到,则直接设置一个默认值(可以是空值,也可以是详细的值)存到缓存中,如许第二次读取缓存时就会获取到默认值,而不会继承访问存储体系。
(这里实在查询这种照旧没法设置默认值,明白这回导致缓存和db的不划一。明白恶意攻击数据库也是通过接口来攻击的,最好照旧有人恶意攻击数据库的时间,我们应该关注接口,qps等指标和mysql指标,做好降级限流操纵)
2.缓存数据天生淹灭大量时间大概资源
第二种环境是存储体系中存在数据,但天生缓存数据必要淹灭较长时间大概淹灭大量资源。假如刚幸亏业务访问的时间缓存失效了,那么也会出现缓存没有发挥作用,访问压力全部会合在存储体系上的环境。
缓存雪崩

缓存雪崩是指当缓存失效(逾期)后引起体系性能急剧降落的环境。当缓存逾期被扫除后,业务体系必要重新天生缓存,因此必要再次访问存储体系,再次举行运算,这个处置惩罚步调耗时几十毫秒以致上百毫秒。而对于一个高并发的业务体系来说,几百毫秒内大概会接到几百上千个哀求。由于旧的缓存已经被扫除,新的缓存还未天生,造成巨大的性能压力。这些压力又会拖慢整个体系,严肃的会造成数据库宕机,从而形成一系列连锁反应,造成整个体系瓦解。
缓存雪崩的常见办理方法有两种:更新锁机制和配景更新机制。
1.更新锁
必须必要分布式锁来控制更新,由于多台呆板照旧有大概同时更新缓存。
2.配景更新
由配景线程来更新缓存,而不是由业务线程来更新缓存,缓存本身的有用期设置为永世,配景线程定时更新缓存。(通过job大概mq去操纵)
配景定机遇制必要思量一种特殊的场景,当缓存体系内存不敷时,会“踢掉”一些缓存数据,从
缓存被“踢掉”到下一次定时更新缓存的这段时间内,业务线程读取缓存返回空值,而业务线程本身又不会去更新缓存,因此业务上看到的征象就是数据丢了。办理的方式有两种:
1)配景线程除了定时更新缓存,还要频仍地去读取缓存(比方,1秒大概100毫秒读取一次),假如发现缓存被“踢了”就立即更新缓存,这种方式实现简朴,但读取时间隔断不能设置太长,由于假如缓存被踢了,缓存读取隔断时间又太长,这段时间内业务访问都拿不到真正的数据而是一个空的缓存值,用户体验一样平常。
2)业务线程发现缓存失效后,通过消息队列发送一条消息关照配景线程更新缓存。大概会出现多个业务线程都发送了缓存更新消息,但实在对配景线程没有影响,配景线程收到消息后更新缓存前可以判定缓存是否存在,存在就不实验更新操纵。这种方式实现依赖消息队列,复杂度会高一些,但缓存更新更实时,用户体验更好。(这种思绪牛)
缓存热门

固然缓存体系本身的性能比力高,但对于一些特殊热门的数据,假如大部门以致全部的业务哀求都哀求同一个key,会出现性能降落标题。
1)锁竞争
某些数据结构访问时会加锁
2)内存访问
当大部门哀求都会合在少数几个key上时,这些key的数据大概会频仍地在CPU缓存和主内存之间移动,这会导致缓存未掷中率增长,进而低沉Redis的性能。(这里指的缓存未掷中值得是linux呆板的l1,l2,l3缓存,与redis举行交互,由于热门key并不一连,造成cpu缓存利用率低)
缓存热门的办理方案就是复制多份缓存副本,将哀求分散到多个缓存服务器上,减轻缓存热门导致的单台缓存服务器压力。(我明白就是将一个key,变为多个key,在key背面做编号,通过简朴的%大概hash去匹配)
缓存热门标题写的好的帖子:https://zhuanlan.zhihu.com/p/696632931

总结

存储这块的复杂度优化都是围绕着db来做的,当哀求量上升先对db举行优化,优化方案有读写分离,分库分表。当db不满足业务需求必要对热门数据快速访问可接纳缓存机制,但增长复杂度的同时也必要注意缓存热门,穿透,击穿以及雪崩标题。当出现大量含糊查询时,我们也可以接纳全文搜刮数据库去共同db搜刮。在db优化中可以看出多出利用了消息中央件,定时任务等包管业务的可用连贯性。

我的目标

盼望在年底学习一下内容:
java学习内容:
1.tomcat源码
2.dubbo源码
3.zookeeper源码
4.netty源码
go学习内容:
1.gin框架学习
2.简朴go项目
3.go根本知识进阶(gmp,gc,channel,map,slice源码等)
中央件学习内容:
1.kafka利用及源码
框架学习内容:
1.从零开始学架构
算法学习内容:
1.复习leetcode中top 100
我平常喜欢没事还打游戏,由于有宝宝以是盼望在平常时间能只管完成上述学习内容(盼望能戒掉游戏哈哈哈)。
也盼望有和我一样的一起学习的小同伴共同砚习进步,我建一个qq后端交换群:279868576,盼望小同伴们到场共同督促进步。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表