5.2.1 MySql语句性能分析
SQL执行频率
查看当前会话/全局服务器的状态信息:SELECT SESSION/GLOBAL STATUS;
查看当前会话/全局的SQL增删改查的执行频率:SELECT SESSION/GLOBAL STATUS LIKE 'Com_______';
SQL慢查询日志
查看慢查询日志开关是否打开:SHOW VARIABLES LIKE 'slow_query_log';
慢查询日志记录了执行时间超过指定参数(long-query-time,默认10s)的SQL语句的日志;
linux中慢查询日志
MYSQL配置文件在linux系统中的/etc/my.cnf中
#开启MySQL慢日志查询开关
slow_query_log = 1
#设置慢日志的时间为2s,SQL执行时间超过2s就会被视为慢查询
log_query_time = 2
#保存之后然后重启mysql服务
Linxu会自动生成相关的日志文件存放慢日志查询中记录的信息,存放的位置在/var/lib/mysql/localhost-slow.log中
windows中慢查询日志
Windows关于MySQL的配置文件在C:\ProgramData\MySQL\MySQL Server 8.0\my.ini中
slow-query-log=1 #开启MySQL慢日志查询开关
slow_query_log_file="慢查询日志的存放位置" #指定慢查询日志文件的存放位置
long_query_time=10 #设置慢日志的时间为10s
show variables like 'slow_%' 查看慢查询日志存储位置
Profile
查看当前MySQL是否支持profile操作以及是否启用了此操作:show variables like '%profil%';
在MySQL中开启Profile功能
set session/global profiling = True;
Window和Linux默认Profile是关闭的(不同的版本可能有区别)
查看会话中每一条SQL的耗时基本情况:SHOW PROFILES;
查看此query_id对应的SQL语句各个阶段的耗时情况:SHOW PROFILE FOR QUERY query_id;
查看此query_id对应的SQL语句的CPU使用情况:SHOW PROFILE CPU FOR QUERY query_id;
Explain
语法:在任意的SELECT语句之前加上关键字 Explain或者Desc
示例:EXPLAIN SELECT * FROM 表名;
SQL优化:
insert优化
插入多条数据时采用批量插入
插入数据超过百条时,采用事务进行优化
大批量插入数据时间,使用insert语句插入性能较低,可使用mysql的load进行插入,将.sql文件直接导入生成表
主键优化
对主键插入数据时,顺序插入性能高于乱序插入,可以使用AUTO_INCREMENT自增主键
满足业务需求,尽可能降低主键的长度(降低二级索引占用的空间)
业务操作时,避免对主键的修改
order by 优化
先使用Explain查看sql执行结果中的Extra字段内容,如果是Using Filesort,则需要进行优化,尽量将其优化成Using index(通过满足覆盖索引实现)
如果不可避免出现filesort,在大数据排序时,可适当增加排序缓冲区的大小sorg_buffer_size (默认大小256k)
Show variables like ‘sort_buffer_size’; #查看排序缓冲区的大小;当排序缓冲区占满之后会占用磁盘进行排序
group by 优化
先使用Explain查看sql执行结果中的Extra字段内容,如果是Using Temporary,则需要进行优化,尽量将其优化成Using index(通过满足覆盖索引实现)
通过索引提升效率,尽量实现覆盖索引,避免回表查询,避免索引失效
limit优化
大量数据,越往后分页,耗时越长,可能会造成资源浪费
可通过覆盖索引+子查询的方式进行查询:先找到对应数据的主键,然后通过主键查询
Select * from 表名 where 主键值 in (select 主键值 from 表名 order by limit 10000,10);
count优化
count(*):表中总记录行数
count(主键):主键总记录行数,不判断主键是否为null
count(字段):字段值不为null的总记录数
count(X):X表示数字,查询表的总记录行数
count(*) 约等于 count(X) > count(主键) > count(字段)
update优化
使用索引列作为更新条件,避免表锁
避免索引失效,行锁升级为表锁
多表连接查询优化
内连接时,尽量将大表对应字段添加索引
外连接时,尽量将小表作为驱动表,被驱动表字段添加索引
参考资料:https://blog.csdn.net/m0_49864110/article/details/132134139
https://blog.csdn.net/m0_49864110/article/details/132124128
5.2.2 索引(唯一、覆盖、聚簇…)
索引介绍
索引定义:帮助mysql高效获取数据的数据结构,加快数据库查询速度
索引存储位置:磁盘
分类:聚集索引、覆盖索引、组合索引、前缀索引、唯一索引等。
数据结构:无特殊说明,普遍采用B+树数据结构组织
优势:提高检索效率,降低IO成本,通过索引列数据进行排序
劣势:占据磁盘空间,降低更新表的效率(不单要更新表数据,还要更新表索引文件)
索引类型
主键索引:唯一性,不允许为空
普通索引:无限制,允许重复,允许为空
唯一索引:唯一性,允许为空
全文索引:只适用于CHAR、VARCHAR、TEXT 文本类型
空间索引:5.7之后版本支持空间索引,遵循OpenGIS几何数据模型规则
前缀索引:在文本类型CHAR、VARCHAR、TEXT创建索引,可以指定索引类型长度
其他(按索引列数量分类):
单列索引:
组合索引:遵循最左前缀匹配规则
5.2.3 索引数据结构
hash表:查询效率高,时间复杂度0(1),不支持范围快速查找。不适合索引使用
二叉树:节点最多2个分叉,左子树和右子树顺序从小到大,容易出现树不分叉
平衡二叉树:左右两个子树层级最多相差1。不支持范围查询快速查找,范围查询需要从根节点多次遍历,效率较低
B树(改造二叉树):不支持范围查询快速查找
B+树(改造B树):和B树区别在于非叶子节点是否存数据,可以保证等值和范围查询的快速查找.MySql索引采用的即时B+树数据结构。
参考资料:https://blog.csdn.net/qq_35190492/article/details/109257302
5.2.4 优化sql性能
避免不走索引的场景
避免字段开头模糊搜索,会导致数据库引擎放弃索引而使用全表扫描;
避免使用in或者not in,会导致引擎走全表扫描;
避免使用or,会导致引擎走全表扫描;可使用union代替
避免进行Null值的判断,会导致引擎走全表扫描;可给字段添加默认值,用0代替
避免在where条件后的表达式进行函数等操作,会导致引擎走全表扫描;
避免使用where 1=1,使用此条件会导致引勤走全表扫描
查询条件不能用<>或者!=
where 条件仅包含符合索引非前置列
隐式类型转换造成不使用索引
order by要和where 中条件字段一致否则order by 不会利用索引排序
SELECT语句优化
避免select *,如果使用的话会使得优化器无法完成索引覆盖扫描这类优化,影响优化器对执行计划的选择,也会增加网络带宽和消耗,更会带来更多的I/O,内存和CPU消耗
避免出现不确定结果的函数,特别针对主从复制业务场景,如果使用不确定函数很可能导致主表和从表数据不一致
多表联查,小表在前,大表在后。因为from表后的关联查询是从左往右执行,第一张表会扫全表。
使用表的别名。减少解析的时间和语法错误
where替换having。having是在检索出记录后对结果进行过滤,where则是在聚合前刷选记录,通过where限制记录的数量,则可以减少这方面的开销。
调整where子句中的连接顺序:mysql采用从上往下,从左往右的顺序执行。根据此原理,应该将过滤数据多的条件往前放,最快缩小结果集。
增删改DML语句优化
大批量插入数据,使用insert批量插入。减少sql语句解析的操作;特定场景减少DB连接次数;SQL语句较短,减少网络I/O
适当使用commit,释放事务占用的资源而减少消耗
避免重复查询更新的数据,
查询条件优化
复杂查询,使用中间临时表暂存数据
优化group by 语句,group by 字段 orde by null,如果确实需要排序则显示使用order by 字段
优化Join语句。join查询效率比子查询效率更高
优化union 语句。除非确实需要去重,否则建议使用union all,如果只是union,会给临时表添加distinct关键字,会对临时表进行唯一性校验,消耗很高
拆分复杂sql为多个小sql,避免大事务。简单sql容易使用mysql的query cache,可使用发挥多核CPU的特性
使用truncate代替delete。使用delete语句的操作会记录到undo块中,删除记录也记录binlog,效率较低并且占用资源。使用truncate占用资源较少且效率较高。
使用合理的分页方式提高分页效率,对where和order by 都涉及到的字段有对应覆盖索引时,且中间结果很大时适用
建表优化
表中创建索引,优先考虑where ,order by使用到的字段
尽量使用数字类型字段,只含数值类型的字段尽量不要设计为字符串,会降低查询和连接的性能(查询和连接时会逐个比较字符中的每个字符),并会增加存储开销
使用varchar/nvarchar 代替char/nchar。变长字段存储空间小,且较小字段查询效率高一些。
参考资料:https://blog.csdn.net/qq_39390545/article/details/107020686
https://blog.csdn.net/liaowenxiong/article/details/120846042
5.2.5 分区表
定义:通常情况下,同一张表的数据在物理层面都是放在一起的,随着表数据量过大,会带来管理上的不便,分区特性可将一张表从物理层面根据一定的规则将数据划分为多个分区,多个分区可以单独管理,甚至放在不同的磁盘/文件系统上,从而提升效率
优点:跨磁盘/文件系统存储,适合存储大量数据;管理非常方便,区与区之间不会影响;数据查询在某些条件上可以利用分区裁剪特性,将搜索范围快速定位到特性分区,提升查询性能
数据划分的规则称为分区函数,数据写入表时,会根据分区函数运行结果决定写入那个分区
分区的类型
范围分区(Range partition):
根据分区表达式的运算结果,判断结果落在那个分区范围内,从而将数据存储在对应的分区。
各个分区之间连续且不能重叠,范围分区通过partition by range子句定义,分区的范围通过values less than划分
知道数据所处分区,可直接进行查询 select * from 表名 partition(分区名1,分区名2)
删除分区:alter table 表名 drop partition 分区名; 分区删除后,原有分区的数据全部删除,
新增分区:只能在最大范围上新增分区。alter table 表名 add partition (partition 分区名 vanlues less than(51)))
分区重组织:一定要在分区之间插入新的分区,可采用分区重组的方式,将已有分区的数据重新进行划分,达到创建新分区的效果
例如:将p2划分为2个分区
alter table 表名 reorganize partition p2 into (
partition p1 values less than(21),
partition p2 values less than(31));
分区定义存在上限,如果大于分区上限的值想插入表中,系统会返回错误,可以添加一个分区,上线为mxavalue,所有大于当前上限的值都会放入这个分区
alter table 表名 add partition(partition pmax values less than(maxvalue));
Range分区还存在一个变种,是Range column分区。此分区允许使用单个或多个字段作为分区范围条件。
Range分区如果插入数据为null,会被放到第一个分区中
列表分区(List partition)
和范围分区类似,区别是列表分区范围是预定好的一系列的值,而非连续的范围。
采用partition by list 和values in 子句定义
新增分区:alter table 表名 ADD PARTITION (PARTITION 分区名 VALUES IN (1,2,3,4))
删除分区:alter table 表名 DROP PARTITION 分区名;
list partitional:如果插入的值在list分区中不存在的话,语句会返回错误,如果表使用的是innodb事务引擎,则会完全回滚,如果使用的是myisam非事务引擎,已插入的不会回滚,但也会报错
list也存在变种分区list column,使用单个或多列作为分区条件
列表分区必须有一个分区显示包含null,否则null会条件失败
哈希分区(Hash partition)
主要应用场景:将数据平均分布在指定数量的hash分区中,数据存储那个分区由数据库自定决定,只需指定分区数量即可
创建hash分区使用partition by hash(expr) expr是整数类型的列或返还整数的表达式、partitions num(指定hash分区数量,如果忽略,默认为1个分区)
变种:线性哈希分区 Liner Hash PARTITION,区别在于使用了更复杂的计算方式来确定数据的分布。优点是重新计算分布数据速度很快,适合特别大的数据存储场景,缺点是数据分布可能没有哈希分区平均
移除分区:alter table 表名 COALESCE PARTITION 1; 移除1个分区
添加分区:alter table 表名 ADD PARTITION PARTITIONS 3;添加3个分区
null运算,会被当作0处理
键值分支(Key PARTITION)
与哈希分区类似,区别在于键值分区的hash函数是MySql提供的,且使用主键(或非空唯一键)作为分区列
创建键值分区使用partition by key(s1)和partitions num;s1前提是非空唯一键,如果是key()自动使用主键进行分区
键值分区变种:哈希键值分区 PARTITION BY LINAER KEY();
移除分区:alter table 表名 COALESCE PARTITION 1; 移除1个分区
添加分区:alter table 表名 ADD PARTITION PARTITIONS 3;添加3个分区
null运算,会被当作0处理
子分区(subparitioning)
定义:在原有分区基础上,对每个分区再次进行分区。子分区的类型可以和父分区不同,也称作复合分区。
分区的基本维护
重建分区:清除区中所有记录,并重新插入
ALTER TABLE 表名 REBUILD PARTITION 分区名,分区名/ALL;
检查分区:check 指定分区是否存在损坏
ALTER TABLE 表名 CHECK PARTITION 分区名/ALL;
键值分布统计:统计指定分区的键值分布
ALTER TABLE 表名 ANALYZE PARTITION 分区名/ALL;
分区修复:repair 修复损坏的分区
ALTER TABLE 表名 REPAIR PARTITION 分区名/ALL;
分区优化:OPTIMIZE 。相当于在分区上执行检查分区、键值分布统计、分区修复
ALTER TABLE 表名 OPTIMIZE PARTITION 分区名;
参考资料:https://blog.csdn.net/frostlulu/article/details/122304238
5.2.6 视图
定义:视图中的1个或多个表导出的虚拟表,便于用户对表的操作
查看创建视图的权限:SELECT Select_priv,Create_view_priv FROM mysql.user WHERE user='用户名';
创建视图:CREATE [OR REPLACE] [ALGORITHM={UNDEFINED|MERGE|TEMPTABLE}]
VIEW 视图名[(属性清单)]
AS SELECT语句
[WITH [CASCADED|LOCAL] CHECK OPTION];
参数说明:
(1)ALGORITHM:可选项,表示视图选择的算法。
(2)视图名:表示要创建的视图名称。
(3)属性清单:可选项,指定视图中各个属性的名词,默认情况下与SELECT语句中的查询的属性相同。
(4)SELECT语句:表示一个完整的查询语句,将查询记录导入视图中。
(5)WITH CHECK OPTION:可选项,表示更新视图时要保证在该视图的权限范围之内
修改视图:修改数据库中已存在的表的定义
ALTER VIEW 视图名 AS SELECT ...
删除视图:删除视图定义,不会删除数据
DROP VIEW IF EXISTS 视图名;
参考资料:https://blog.csdn.net/pan_junbiao/article/details/86132535
5.2.7 游标
概述:可使我们对结果集中的每条记录进行定位,并对指向记录中的数据进行操作;
在sql中,游标是临时数据库对象;充当指针作用;
可在存储过程和函数中使用
使用步骤
声明游标:DECLARE 游标名字 CURSOR FOR 查询语句;
打开游标:open 游标名
使用游标:fetch 游标名 into var_name...
关闭游标:close 游标名
示例:查看参考资料
参考资料:https://blog.csdn.net/qq_52797170/article/details/125112047
5.2.8 字符集和校验规则
概述
字符集是一套符号和编码;校验规则是在字符集内比较字符的一套规则,即字符集的排序规则
mysql查看字符和方法:
查看支持的字符集:show character set;
查看支持的字符集:select character_set_name, default_collate_name, description, maxlen from information_schema.character_sets;
查看字符集的校对规则:show collation;show collation like 'utf8';select * from information_schema.collations where collation_name like 'utf8%';
查看当前数据库字符集:show variables like 'character%';
查看当前数据库校验规则:show variables like 'collation%';
参考资料:https://blog.csdn.net/wen811651208/article/details/136978241
5.2.9 全文索引
概念:为了解决关键字匹配相似度查询过滤的场景设计的,在大量数据文本事,比like 速度快n倍
版本支持:5.6以前只有myisam引擎支持全文索引,5.6及之后的版本innodb和Myisam都支持全文索引;只有char,varchar,text数据类型支持全文索引
操作全文索引:
创建表时创建全文索引:FULLTEXT KEY 索引名(字段名,字段名) 字段可以单个或者多个,多个字段使用,分隔,作为联合全文索引
已经存在的表创建全文索引:create fulltext index 索引名 on 表名(字段名,字段名)
alter table创建全文索引:alter table 表名 add fulltext index 索引名(字段名,字段名)
drop index删除全文索引:drop index 索引名 on 表名;
alter table删除全文索引:alter table 表名 drop index 索引名;
使用全文索引:select * from 表名 where match(字段名,字段名) against('xxx','xxx');
注意:使用全文索引,表中至少4条以上数据;
mysql全文索引存在最小搜索长度和最大搜索长度的概念,如果使用全文索引,搜索条件长度必须位于两者之间
查看最小搜索长度和最大搜索长度命令:show variables like '%ft%';
修改最小搜索和最大搜索长度:/etc/my.cnf文件中,[mysqlId]后添加 innodb_ft_min_token_size = 1 ft_min_word_len = 1,然后重启并修复全文索引或删除重建索引
修复全文索引命令:repair table 表名 quick;
类型:
自然语言全文索引:match对自然语言集合进行自然语言搜索
布尔全文索引:可在查询中定义某个被搜索词语的相关性
示例:select * from 表名 where match(字段名) against('a*',in boolean mode);
参考资料:https://blog.csdn.net/mrzhouxiaofei/article/details/79940958
5.2.10 分布式事务
前提了解:
集群:解决某服务处理不了过多请求,将某服务多复制几份
消息队列:某服务需要处理的消息,暂时存储在某处,一部分一部分推送给某服务,而非一次全部推送过去
分库分表:数据库存储不下,数据分开存储到多台服务器的数据库中
读写分离:数据库读取压力太大,将数据复制到其他一些数据库,专门负责读
本地事务:用关系型数据库控制事务
分布式事务:在分布式系统中,一个操作由多个系统协调完成,一个事务操作涉及多个系统通过网络协同完成的过程称为分布式事务。
分布式事务:
分布式事务解决问题:分库分表带来的数据一致性问题。一个系统对接多个数据库。
分布式事务定义:分布式环境中,不同的节点,整体事务的一致性叫做分布式事务
分布式事务类型:
强一致性:数据永远一致,需要引入某种协调机制。金融交易类场景。数据库协议是XA。
弱一致性:一段时间内数据可以不一致,后续通过定时补偿机制,柔性事务框架(tcc,saga)达到数据最终一致性。隔天转账到账等场景。通过业务冲正实现。
XA协议:为了规范分布式事务的管理,X/OPEN提出了XA协议,此协议规范了TM(事务管理器)与RM(资源管理器)之间的通信接口,在TM和多个RM之间形成了一个双向通信桥梁,从而在多个数据库资源下保证ACID4个特性。oracle、db2、mysql都实现了XA协议。
XA是数据库的分布式事务,强一致性,整个过程中,数据都处于被锁定的状态,即从prepare到commit、rollback的整个过程中,TM一直拥有参与分布式事务RM对应数据库的锁,如果有其他人要修改数据库,必修等待锁的释放。存在长事务风险。
分布式事务(处理)模型:由X/OPEN定义,包括应用程序(AP)、事务管理器(TM)、资源管理器(RM)、通信资源管理器(CRM)
XA规范中分布式事务由于AP、TM、RM组成
AP:定义事务开始和结束,并访问事务边界内的资源
TM:负责管理全局事务,分配事务唯一标识,监控事务执行进度,负责事务提交、回滚、失败恢复等
RM:管理计算机共享的资源,例如数据库、文件系统等
XA协议执行步骤
第一阶段:事务管理器通知参与事务的资源管理器,开启事务,执行sql(暂不提交),并进入prepare状态(该状态下可执行commit或rollback)
资源管理器收到消息开始准备阶段,执行事务但不提交,然后将是否就绪的消息推送给事务管理器
资源管理器如果判断自己的工作可以提交,则对其进行持久化,并返回给TM-OK否则返回给TM-NO。
RM发送否定发福并回滚事务后,则可以丢弃事务分支了
第二阶段:事务管理器接收各个消息之后,如果有任意其一失败,则发送回滚命令,否则发送提交命令
资源管理器接收到命令之后,执行操作,并将提交消息返回给事务管理器
两阶段提交的好处在于事务管理器统一管理,让事务在提交前尽可能完成所有能完成的工作,两阶段提交可以保证事务的一致性,不管是事务管理器还是资源管理器,每执行一步都会记录日志,为出现故障后的恢复提供依据。
MySql中的XA语法(MYSQL分布式操作基本模板)
开启XA事务:XA start <xid>
DML语句 即增删改查sql语句
终止XA事务:XA end <xid>
预提交事务:XA prepare <xid>; 此步骤有返回值
提交事务:XA commit <xid>; 或 回滚事务:XA rollback <xid>;
扩展:XA RECOVER:返回当前数据库处于PREPARE 状态的分支事务的详细信息
注意:xid(事务标识符)必须唯一,可由客户端提供或mysql服务器生成;整个XA过程中的xid必须和start中用到的一致
使用示例
XA start 'xa-first';
insert into t values(1);
XA end 'xa-first';
XA prepare 'xa-first';
XA commit 'xa-first'; 或 XA rollback 'xa-first';
XA的性能问题
XA事务和本地事务以及锁表是互斥的,因为XA事务会锁住当前表
总结
执行分支事务时,会将所有的RM锁定,等到第二阶段执行完毕时(提交或者回滚),RM的锁才会释放,因为高并发场景并不使用
参考资料:https://blog.csdn.net/m0_53157173/article/details/125275556
MySQL之分布式事务_mysql 分布式事务-CSDN博客
https://blog.csdn.net/oldshaui/article/details/88743085
5.2.11 缓存
前提:读多写少,单个主节点能支持项目数据量
1.mysql缓存方案的目的和分析
mysql有自己的缓冲层,主要用于缓存热点数据,包括索引、记录等。主要缓冲策略是lru.
mysql主要数据存储在磁盘中,适合大量重要数据的存储,磁盘数据远大于内存数据
缓存作用:缓存用户定义的热点数据,降低数据库的读写压力
缓存选择:缓存数据库可以使用redis,memcached。它们将数据存储在内存中,当然也可以将内存中数据持久化到磁盘中
场景分析:内存访问速度是磁盘访问速度的100倍,访问磁盘速度比较慢,尽量使获取数据从内存中获取
读的需求远大于写的需求,主要解决读的性能问题,如果写的问题需要解决可采用横向扩展集群方式来解决
mysql自身缓冲层不由用户来控制
mysql作为项目主要数据库,用于存储大量数据用于统计分析
缓存数据库作为辅助数据库,存储热点数据
2.提升mysql性能的几种方式
读写分离、连接池、异步连接、预处理、更换存储引擎、tidb
2.1 mysql主从复制步骤:主库数据更新事件通过io-thread写入binlog;
从库请求读取binlog,通过io-thread写入从库本地relaylog(中继日志);
从库通过sql-thread读取relay-log,并将更新事件在从库重新执行一遍。
注意:mysql主从复制是异步的,可能导致主库和从库在同一时刻数据不一致,有可能读取的数据并非是最新的
2.2 读写分离:
依据主从复制原理,设置一个主库和多个从库,从库可能位于多台不同服务器中,通过多个从库来解决读取的压力。
同时因为主从复制原理的缺陷,无法保证实时一致性。如果有强一致性要求,则需要读取主库。
2,3 连接池
定义:在服务端中创建多个与数据库建立连接的线程
解决问题:并发提升数据库访问性能;同时复用连接,避免连接建立、断开依据安全验证的开销
原理:利用mysql的网络模型创建多个连接,每个连接复用sql。
注意:如果发送一个事务(多条sql),这个事务必须在一个连接中完成
2.4 异步连接:服务端创建和mysql的连接,此连接采用的是非阻塞IO,从而节省网络传输时间
3.缓存方案
3.1 缓存和mysql的一致状态分析
引入缓存后,对数据的获取需要分别操作缓存库和mysql,此时数据可能存在以下几种状态
mysql有,缓存无;mysql无,缓存有;都有但数据不一致;都有且数据一致;都没有
3.2 执行读写策略
读策略:先读缓存,如果缓存有,直接返回;如果缓存没有,读mysql,如果mysql有,同步到缓存并返回;如果mysql没有,返回没有
写策略(从安全优先方面考虑):先删除缓存,再写入mysql,最后通过go-mysql-transfer等中间件将数据同步
写策略(从效率优先方面考虑):先写缓存,设置过期时间(200ms),再写mysql,后面数据同步交由其他中间件处理。
4.同步方案
4.1 伪装从数据库,比如阿里开源的canal方案、kafka、go-mysql-transfer等中间件等
4.2 canal:会考虑分布式问题,如果一个canal宕机,会有其他canal补充,确保服务正常提供
4.3 go-mysql-transfer:只有一个节点,未考虑分布式问题,增强高可用,可引用etcd、zk等
5.缓存方案的故障问题及解决
5.1 缓存穿透
定义:某个数据在redis和Mysql都不存在,但是一直在尝试读,可能最终导致mysql不堪重负而崩溃
简单解决方案:如果发现数据在Mysql不存在,在redis中设置为<key,null>,设置过期时间,下次再访问key的时,不再访问mysql,但是容易造成redis缓存很多无效数据
布隆过滤器解决:mysql已经存在的数据,写入布隆过滤器,不存在的直接pass掉;后续客户端发起请求,redis和mysql中都没有;判断key是否存在于布隆过滤器;存在则查询数据库,不存在则直接返回空。可能造成误判
5.2 缓存击穿
定义:某些数据redis没有,但是mysql有(一般是缓存时间到期),如果并发用户过多,同时没读取到缓存数据,又同时去数据库查询数据,引起数据库压力过大
解决方案:
分布式锁:请求数据时获取锁,如果获取成功,则操作后释放锁。若获取失败,则休眠一段时间(200ms)再去获取,获取成功,再释放锁
设置热点数据永不过期
5.3 缓存雪崩
定义:一段时间内,缓存集中失效(mysql有,redis无),导致全部请求走mysql,有可能搞垮数据库,使整个服务失效
与缓存击穿的区别:缓存击穿指并发查同一条数据;缓存雪崩是指大量数据缓存都过期了,很多数据查不到只能查数据库
注意:缓存数据库并非系统必须的,缓存数据库宕机并不会影响系统运行
解决方案:缓存数据过期时间设置随机,防止同一时间大量数据过期;设置热点数据永不过期。
5.4 缓存方案的弊端:不能处理多语句事务,不支持回滚
参考资料:https://blog.csdn.net/Long_xu/article/details/127656552
https://blog.csdn.net/kongtiao5/article/details/82771694
https://blog.csdn.net/qq_41125219/article/details/119982158