- 数据库三大范式
- 确保每列原子性,所有字段值都是不可分解的原子值,每个列都不可以再拆分
- 非码属性必须完全依赖于候选码,在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
- 每一个表不能包含其他表的分主键信息,在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。
- MySQL基本机构:
- Server层
- 链接器:身份认证
- 查询缓存:执行查询语句的时候,会先查缓存(MySQL 8.0 版本后移除,表的更新会使缓存清空,且有更好的,例如redis)
- 分析器:对SQL语句进行分析,分析语句的方式,查询的表和字段,查询条件等,检查SQL语句是否正确
- 优化器:按照MYSQL认为最优的方案执行(例如多个索引,多表查询如何关联顺序)
- 执行器:执行语句(效验用户权限),然后从存储引擎返回数据
- 存储引擎:主要负责数据的存储和读取,采用可以替换的插件式架构, InnoDB、MyISAM、Memory,默认InnoDB。
- InnoDB在主键上建聚簇索引,将主键组织到一棵B+树中,而
行数据就储存在叶子节点上
。在非主键上建辅助索引,查找的时候,首先在辅助索引B+树上检索,找到叶子节点获取对应主键,再在主键索引执行一次B+树搜索操作,叶子节点即为数据。 - InnoDB内存结构:
- Buffer Pool缓冲池:基于LRU算法(最近最少使用页面置换算法),80%占80%以上的物理内存。Page,InnoDB存储的最基本结构,磁盘管理的最小单元,最小16K。分为数据页,索引页,日志页等。
- Insert Buffer更改缓冲区(实现插入缓冲),数据页的存放还是按照主键进行顺序存放,但对于非聚簇索引叶节点则是随机的,此时就需要使用到插入缓冲技术。插入时,首先判断插入的非聚集索引页面是否在缓冲区里面,是就直接插入。不是就先存放到Insert Buffer,达到一定数量进行排序,然后再将排序的结果插入叶子节点。使用Insert Buffer条件,辅助索引,索引非唯一。
- 为什么索引非唯一?:因为如果索引唯一,则他对应B+树的索引只会包含唯一一个对应的主键值;只有当索引非唯一的时候,所对应的B+树叶子节点能对应多个主键值,多次插入才能合并。
- Mysql5.5以后改名为Change Buffer,不仅仅支持插入操作,还支持更新,删除操作
- 自适应哈希索引:InnoDB会根据热点索引页(经常访问的二级索引数据会自动被生成到hash索引里面去),计算效率,建立哈希索引,具体搜索过程中,首先尝试hash搜索,如果失败再尝试二进制搜索。
- Log Buffer日志缓冲区:保存要写入磁盘上日志文件的数据的内存区域,默认大小16M,有三种刷到磁盘的方式:
- 每次事务都会写入内核缓冲区,并将数据刷到磁盘
- 每秒写事务到内核缓冲区,并将数据刷到磁盘
- 每次事务都会写入内核缓冲区,每秒将数据刷到磁盘
- InnoDB磁盘结构:表、索引、表空间、数据字典信息、双写缓冲区,重做日志,撤销日志
- 表空间:
- 系统表空间:数据字典、双写缓冲区、更改缓冲区和撤消日志的存储区域 。
- 通用表空间:存储表数据,共享空间
- 撤销表空间:存储回滚段(并发,回滚消息)
- 临时表空间:存储临时表,正常关闭或中止初始化时被删除,并在每次服务器启动时重新创建。
- 数据字典信息:数据库中数据、库对象、表对象、索引等元信息的集合
- doublewrite buffer
- 若在将脏页(内存数据页跟磁盘数据页内容不一致)写到数据库的过程中,写到一半,突然断电。
- innodb的数据页一般大小是16KB,有可能在一个页中写入一半时(比如8K)数据库宕机,部分写失效,而磁盘上的数据页损坏,就需要用到二次写
- 写数据页之前,先把这个数据页写到一块独立的物理文件位置,再写到数据页。这样在宕机重启时,先用副本恢复数据页,然后再使用rado log重做
- 重做日志(rado log):物理日志,当记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log里面,并更新内存,这个时候更新就算完成了。当系统比较闲或者redo log满了的时候更新刷新到磁盘。
- 撤销日志(undo log):包含撤销日志
- 表空间:
- InnoDB特性:
-
插入缓冲(insert buffer):Change Buffer
-
二次写(double write)
-
自适应哈希索引(ahi)
-
预读(read ahead),rado log
-
- InnoDB优点:
- 支持事务
- 灾难恢复性好:
- 使用行级锁:
- 实现缓冲处理:InnoDB 提供了专门的缓存池,实现了缓冲管理,不仅能缓冲索引也能缓冲数据,常用的数据可以直接从内存中处理,比从磁盘获取数据处理速度要快。
- 支持外键:InnoDB 支持外键约束,检查外键、插入、更新和删除,以确保数据的完整性。
- 适合大数据:InnoDB 是为处理巨大数据量时的最大性能设计
- InnoDB在主键上建聚簇索引,将主键组织到一棵B+树中,而
- mysql语句执行流程;
- 查询语句:权限效验->查询缓存->分析器->优化器->权限效验->执行器->引擎
- 更新语句:权限效验->查询缓存->分析器->优化器->权限效验->执行器->引擎 redo log prepare(InnoDB重做日志 预提交状态)->binlog(归档日志)->redo log commit(提交状态)
- 需更新的数据首先放在BufferPool缓冲池
- 若prepare完毕,binlog出错,会根据redo log回滚
- 若prepare完毕,binlog完毕,commit出错,会执行提交
- Server层
- 事务:
- 事务ACID特性:
- 原子性:事务包含的所有操作要么全部成功,要么全部失败回滚。事务执行的时候,innodb会制作撤销日志(undo log),当事务执行失败或调用 rollback回调函数,数据库会根据undo log的记录反向操作。
- 持久性:事务一旦提交,他对数据库的改变就应该是永久的。其他影响,即使发生故障,也不会对这个改变发生影响。基于重做日志(rado log)实现,如果 MySQL 宕机,重启时可以读取 redo log 中的数据,对数据库进行恢复。
- 隔离性:事务执行的内部操作与其他事务是隔离的,并发执行时各个事务是隔离的。基于锁机制实现。
- 一致性:事务执行后,数据库的完整性不会被破坏,事务执行前后都是合法数据状态。其他三种特性的实现,才能保证数据库一致性的实现,且数据库本身会提供保障(例如:不允许向整形列插入字符串值,字符串值长度限制等),还需用户层面来保障一致性(例如:转账操作只扣钱不加钱)。
- 并发情况下,数据操作所存在的问题:
- 丢失更新:A,B两个事务获得同一个数据并将其修改,则A,B事务执行结束后,数据会丢失某个事务的修改。
- 脏读:事务A读取到事务B修改但是未提交的事务,然后事务B发生了回滚。
- 不可重复读:事务A两次读取的数据不一致,在事务两次读取数据之间,事务B将数据进行修改,并提交了事务。
- 幻读:事务A两次读取结果不一致,事务两次读取数据之间,事务B新增,并提交了事务。
- 事务的隔离级别:
- 读未提交:最低的隔离级别,运行读取未提交的数据,读取数据不加锁,修改数据增加行级共享锁。
- 读已提交:允许读取事务已经提交的数据,读取数据增加行级共享锁,读取结束释放,修改数据时增加行级排它锁,事务结束释放
- 可重复读:对同一字段的多次读取结果都是一致的,读取数据增加行级共享锁,事务结束释放,修改数据时增加行级排它锁,事务结束释放(mysql默认级别)
- 可串行化:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行
- InnoDB,MVCC(多版本并发控制):
- 查询一些正在被另一个事务更新的行,但可以看到它们被更新之前的值,这样查询就不用等待另一个事务释放锁。
- InnoDB数据行包含两个隐藏列:(最近修改该行数据的事务的ID),(该行回滚段的指针,指向undo中该行的历史记录链表)。MVCC并没有简单的使用数据库的行锁,而是使用了行级锁。
- 为什么叫多版本?版本号并不是实际的时间值,而是系统版本号。每开始新的事务,系统版本号(事务版本)都会自动递增。
- 更新操作(删除再插入)
- 两个隐藏列也可看作:创建时的版本号和删除时的版本号
- 增删改查:
- 增加,创建一行,并将当前版本号设置为此事务的id
- 删除,将删除行的删除版本号的事务id设为当前事务的id
- 更新:(更新 = 删除 + 增加),将删除行的删除版本号的事务id设为当前事务的id,创建一行,并将当前版本号设置为此事务的id。
- 查询:只查找小于或等于当前事务版本的数据行。数据行的删除版本号一定大于当前事务id或者为未定义。
- 事务ACID特性:
- 索引:
- 索引是对数据库表中一个或多个列的值排序的结构
- 索引类型:
- 聚簇索引:叶子节点为行数据的全部信息。此列或列集特点:非空唯一。如果没有定义,则会自动选择合适的唯一索引作为聚簇索引,并将其设为非空;如果没有合适,会自动创建隐藏列作为聚簇索引。主键索引是聚簇索引的一种具体展现。
- 二级索引:非聚簇索引
- 索引分类:
- 主键索引:列值唯一,非空,表中唯一
- 唯一索引:列值唯一,可以有null
- 普通索引
- 联合索引:多列值组成一个索引,专门用于组合搜索,查询的时候会基于最左前缀匹配原则。
- 最左前缀:例如表对于(a,b,c)创建了联合索引,则匹配的时候可以通过(a)或者(a,b)或者(a,b,c)进行索引查找。而其他组合均不能利用到索引
- 覆盖索引:查询的字段已经全部被索引包含了,不需要回表查询了
- 排序索引的构建(创建或重建索引时执行),采用自下而上的构建方式,不需要页面分裂合并,不需要多次搜索查找插入位置。顺序创建节点,然后子节点满了,创建父节点。
- 全文索引:把包含一定的字符串的的行记录挑选出来,以一定字符串创建索引。简单来说,就是记录某些词会在哪些行中出现。
- 索引实现:
- Hash索引:基于hash表实现,适合精确查找不适合范围查找
- B树索引:
- 根节点非叶子节点至少有两个子树
- 非根节点有M/2 - 1 ~ M-1个关键字
- 叶子节点均在同一层
- 插入节点,逐层往上,插入,分离
- 删除节点,父节点下移,兄弟节点上移
- B+树索引
- 与B大致相同,子树与关键字数相等,父节点指也在子节点中
- 叶子节点包含全部数据
- 所有查询的时间一致
- 索引失效:
- 违反最左前缀
- 在索引列上做一些操作,例:计算、函数、类型转换
- 多个单列索引,只会生效最左边的
- 使用不等于(!=、<>)
- 条件中有or
- like查询以通配符%开头
- 字符串不加单引号
- mysql判断全表查询更快,就会使用全表查询
- 锁
- 锁分类:
- 共享锁和排他锁
- 共享锁(读锁):其他事务可以读,但不能写。
- 排他锁(写锁):其他事务不可以读,但也不能写。
- 锁粒度:表级锁,页级锁,行级锁(Innodb支持行级锁和表级锁)
- 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
- 行级锁(innodb特有):开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
- 页级锁(BDB支持):表级锁和行级锁之间的锁。
- 悲观锁和乐观锁:
- 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。CAS,假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。
- 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。
- 共享锁和排他锁
- innodb锁机制:
- innodb的行级锁是对索引加锁,而不是对数据行加锁。当SQL语句通过索引锁定数据行,则对索引加的是行级锁;若全表扫描找到数据行,则加的是表锁。
- 具体加行锁还是表锁,由mysql优化器决定。因为有可能mysql会认为表很小,不需索引。就会加表锁。
- 因为是行锁是对索引加锁,不是对数据行加锁,所以有可能不同的行,使用了相同索引,还是会出现冲突。例如表包含a,b两个字段,a建立了索引,b没有。则如果以索引查询,查询条件 a = 1and b = 1 就会和 a = 1and b = 4冲突
- 当表有多个事务的时候,不同的事务可以通过不同的索引加锁,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。
- 行级锁:共享锁 和 排他锁
- 表级锁:意向共享锁 和 意向排它锁 ;共享锁 和 排他锁
- 意向共享锁: 事务想要获得一张表中的某几行的共享锁
- 意向排它锁: 事务想要获得一张表中的某几行的排它锁
- 一致性非锁定读(快照读):普通读写,普通的select语句
- 一致性锁定读(当前读):显示加锁:
- select ... for update 使用临键锁,相当于加排它锁(行)
- 间隙锁:封锁索引记录中的间隔,开区间()。
- 临键锁:间隙锁 + 记录锁(行锁)。(】左开右闭区间,防止幻读。
- 产生情况:
- 可重复读 级别下
- 普通索引
- 唯一索引范围查找
- 注:唯一索引会降级为行锁
- select ... lock in share mode 加共享锁(行)
- select ... for update 使用临键锁,相当于加排它锁(行)
- innodb的行级锁是对索引加锁,而不是对数据行加锁。当SQL语句通过索引锁定数据行,则对索引加的是行级锁;若全表扫描找到数据行,则加的是表锁。
- 锁分类:
- 数据库优化
- SQL语句优化
- 尽量避免全表扫描,
- 字段尽量不要出现NULL
- 避免在where子句中使用!=、or、in、not in
- 避免不带任何限制范围的条件查询语句
- 索引优化:在where和order by涉及的列上创建索引,避免索引失效
- SQL结构优化
- 分库分表:垂直分表和水平分表
- 垂直拆分:针对表,字段太多
- 垂直分库:关联度低的不同表存储在不同的数据库
- 垂直分表:将某些不常用的,但是长度又很大的字段拎出来放到另外一张表。
- 水平拆分:针对数据量太大
- 库内分表:同一个db上,将表按照某种条件拆分为多张表。
- 分库分表:将表拆分到不同的机器上
- 常用分库分表策略:
- HASH取摸:用户表user,将其分成3个表user0,user1,user2.路由规则是对3取模,当uid=1时,对应到的是user1,uid=2时,对应的是user2.
- 范围分片:从1-10000一个表,10001-20000一个表
- 地理位置分片
- 垂直拆分:针对表,字段太多
- 缓存:使用MySQL缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存,例如redis。
- 读/写分离:经典数据库拆分方式,利用主从复制实现,主库负责写,从库负责读
- 主从复制流程:
- 主库更新事件写入binlog
- 主库创建线程,将binlog内容发送到从库
- 从库启动,并发起连接到主库
- 从库创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log
- 从库创建一个SQL线程,从relay log里面读取内容,将更新内容写入从库
- 主从复制方式:
- 异步复制:主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,早期MySQL仅仅支持异步复制
- 半同步复制:主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。
- 全同步复制
- 主从复制流程:
- 分库分表:垂直分表和水平分表
- SQL语句优化
- Raids:
- 基本概念:用C语言编写的开源高性能非关系型键值对数据库
- 支持的数据类型:
- 字符串
- 列表
- 集合
- 散列表
- 有序集合
- 优点:
- 读写数独很快
- 支持数据持久化,AOF和RDB
- 支持事务
- 数据结构丰富
- 支持主从复制
- 高性能高并发
- 缺点:
- 受内存限制,不能存储海量数据
- 不具备容错和恢复功能,主机从机宕机,前端请求会出错,需要重启或切换IP
- 主机宕机,还有数据没有备份到从机,切换IP到从机会导致数据不一致问题
- 很难在线扩容
- 快的原因:
- 完全基于内存
- 数据结构简单
- 采用单线程
- 使用I/O多路复用,非阻塞I/O
- 底层模型不同,Redis 直接自己构建了 VM 机制
- 应用场景:
- 计数器
- 缓存
- 消息队列
- 分布式锁
- 底层实现
- 压缩列表:将数据按照一定规则编码在一块连续的内存区域
- 整数集合:编码方式(int16,int32,int64),长度,整数数组。可以升级编码方式,不能降级
- 跳跃表(有序数据结构):
- 多层结构
- 每一层都是有序链表
- 最底层包含所有数据,最上层节点数最少
- 如果一个节点出现在某一层,其下所有层都包含这个节点。
- 莫一层节点有两个指针,一个指针指向同一层下一个节点,一个指针指向下一层节点。
- 搜索,插入,删除:
- 搜索:从左往右,从上往下
- 插入:随机确定插入层数,从下往上添加
- 删除:删除这一层的节点。
- 字典:使用哈希表作为底层实现,哈希冲突使用链表,使用重新散列进行扩容和收缩。
- 重新散列:底层有两个哈希表,一个空白,一个存数据,扩容和收缩时,为空白的哈希表分配空间,将数据刷新到这个哈希表上,重新计算hash值,并将之前存数据的哈希表置空,交换两个哈希表。
- 扩容和收缩都是非一致性的,都是渐进。查找数据的时候,在一个哈希表没找到就会去另外一个哈希表查找。
- 链表:双向链表,节点数据可以是不同类型的值。记录头结点,尾节点和节点数
- Redis的字符串:简单动态字符串(SDS)
- 结构:
- free记录buf中未使用的字节数量
- len保存字符串长度
- buf[] 保存字符串每个元素
- 优点:
- 读取长度的时间复制度为O(1)
- 杜绝缓冲区溢出:C语言使用函数将两个字符串进行拼接,可能因为内存不够而造成缓冲区溢出,SDS拼接字符串会检查len属性,不够会进行相应的空间扩展。
- 减少修改字符串所造成的内存重新分配:C语言修改字符串需要重新分配内存。SDS修改字符串使用了空间预分配和惰性空间释放两种策略。
- 空间预分配:当需要对 SDS 进行空间扩展的时候, 程序不仅会为 SDS 分配修改所必须要的空间, 还会为 SDS 分配额外的未使用空间。当SDS长度小于1M时,会分配与实际字符串相同的长度,此时free = len;当SDS长度大于1M时,会分配1M的额外空间。
- 惰性空间释放:当SDS需要缩短的时候,不是直接内存重新分配,而是使用free记录多余的字节。
- 二级制安全:字符串以空字符串表示结束,而一些二进制文件(图片)可能包含空字符,不能保存。SDS以len元素判断结束,且都以二进制方式处理buf中的字符。
- 兼容C语言:字符串最后包含空字符,可以使用C语言的一些函数
- 结构:
- 持久化
- RDB(默认):按照一定时间,将内存中的数据保存到硬盘中去。使用单独子进程进行持久化,主进程不会进行IO操作。
- 只有一个文件,方便
- 内容可以保存到安全磁盘,安全。
- 单独进程,性能好
- 间隔一段时间才持久化,若持久化之间 redis 发生故障,会照成数据丢失,数据安全低
- AOF持久化:将redis执行的每次写命令到写到单独的日志文件中,当重启redis会重新基于持久化文件进行数据恢复。
- 数据安全,速度慢,需手动启动
- RDB与AOF同事配备,优先加载AOF
- RDB(默认):按照一定时间,将内存中的数据保存到硬盘中去。使用单独子进程进行持久化,主进程不会进行IO操作。
- 扩容:
- Radis作为缓存,使用一致性哈希(用hash环存值)实现动态扩容缩容。
- 一致性哈希:构建一个0到2^32-1的hash环,根据hash值将服务器节点放到环中,存储key值,key值顺时针执行最近的节点。
- 如果radis当做持久化存储,则必须使用节点-键映射关系,节点数量一旦确定不能变化。因为运行时需要数据平衡。
- Radis作为缓存,使用一致性哈希(用hash环存值)实现动态扩容缩容。
- 过期策略:
- 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。
- 惰性过期:当访问key时才会判断这个key是否过期
- 定期过期:每隔一段时间扫描key(非全部),过期则清除。
- 内存淘汰策略:
- 内存不足,新写入会报错
- 内存不足,会移除最近最少使用的key(最常用)
- 内存不足,随机移除某个key
- 内存不足,在设置了过期时间的键空间中,移除最近最少使用的key。
- 内存不足,在设置了过期时间的键空间中,随机移除某个key
- 内存不足,在设置了过期时间的键空间中,移除最早过期时间的key
- radis内存优化:
- 利用好hash,set,list等集合,尽可能将数据(例如对象)抽象到散列表中
- 事务:一次性、顺序性、排他性的执行一个队列中的一系列命令
- 集群方案
- 哨兵模式:客户端和radis集群之间使用哨兵集群(至少三个实例)监管控制,不保证数据零丢失,保证高可用性。判断一个主radi是否宕机,需要大多数哨兵同意,且一个哨兵挂掉,哨兵集群还是能够正常工作。哨兵的具体作用如下:
- 集群监控:监控主radis和从radis进程是否能正常使用
- 消息通知:某个radis发生故障,会通知管理员
- 故障转移:主radis故障,会自动转移到从radis上
- 配置中心:主radis故障,通知客户端新的主radis地址
- 主从结构:一主多从,主负责写,从负责读。先写主再写从。
- 异步复制
- 主radis链接到多个从服务器,从服务器之间也可以互相链接
- 主从复制,使用单独的进程,不会影响主服务器
- 主从复制期间,从服务器正常工作,从服务器更新的时候,暂停服务
- 过程:
- 从服务器链接到主服务器,发送同步(SYNC)命令
- 主服务器收到命令,生成RDB快照文件,并使用缓存区记录此后的写操作
- 快照文件完成后,会将文件和缓冲区所有缓存的写命令发送给从服务器(先发文件,再发缓存)
- 从服务器收到后,会载入快照文件,并执行收到的缓存
- 工作原理:radis集群由多个节点组成,一个节点包含一个主radis和多个从radis。radis集群通过分片来保存数据库中的键值对,集群数据库包含2^14个槽,数据库的键值对都放在这些槽中。并将这些槽分给radis集群多个节点。每个节点可以包含0~2^14个槽。具体流程如下:
- 通过hash将数据分片
- 将数据片存储到互为主从的多个节点
- 先写入主节点,再写入从节点
- 读取数据时,若key值没有在此节点上,会放回转向指令,指向正确的节点
- 哨兵模式:客户端和radis集群之间使用哨兵集群(至少三个实例)监管控制,不保证数据零丢失,保证高可用性。判断一个主radi是否宕机,需要大多数哨兵同意,且一个哨兵挂掉,哨兵集群还是能够正常工作。哨兵的具体作用如下:
- 分区:
- 提高多核cpu利用率:radis是单线程的,在一个服务器上部署多个radis实例能更好的利用内存
- 好处:可以使用更大的内存
- 分区方案:
- 客户端分区:客户端知道键值对在哪个集群节点
- 代理分区:客户端将请求发送给代理,代理决定在哪个节点进行数据读写
- 查询路由:客户端随机向节点发送请求,然后由此节点将请求转发给正确的节点
- 缺点:
- 数据处理麻烦(例如:数据备份)
- 扩容缩荣麻烦
- 不能操作多个key(例:不能将两个key进行合并,因为他们可能分布在不同的节点)
- 分布式锁:
- 使用setnx实现分布式锁。若key不存在则将key的值设为value,若key存在则不进行操作。
- RedLock:使用redis实现的分布式锁(多个服务间保证同时同用户只能有一个请求)
- 流程:假设有5台实例
- 客户端设置锁的过期时间TTL,使用相同key-value去各个节点获取锁
- 在各个radis实例获取锁的时间的总和要小于TTL。例:5台实例,TTL=5s,则每个radis获取锁的时间最多为1s,超过就是获取锁失败
- 只有获得半数以上radis实例的锁,才算获取锁成功
- 获取锁成功,则锁的真正有效时间 = TTL - 获取锁所用时间
- 获取锁失败,必须解锁所有节点的锁
- 特性:
- 互斥:只有一个 client 能拿到锁
- 避免死锁:即使锁定资源的服务崩溃或者分区,仍然能释放锁
- 容错性:只要大部分 Redis 节点存活就可以正常提供服务
- 流程:假设有5台实例
- 缓存异常:
- 缓存雪崩:缓存同一时间大面积失效,就会有大量请求同时落在数据库上,造成数据库崩溃
- 缓存过期时间随机
- 并发量不高,加锁
- 给每个缓存添加缓存标记,标记失效更新缓存
- 缓存穿透:缓存,数据库都没有数据,大量请求就会落在数据库上,造成数据库崩溃
- 增加校验,不合理请求直接拦截,例:id<0
- 查找数据库发现没有数据,缓存设置为key-null,将其过期时间设置短一些
- 采用布隆过滤器,将所有可能存在的数据,hash存到哈希表中,一个一定不存在就直接过滤
- 缓存击穿:缓存失效了(过期),高并发查数据库
- 热点数据永不过期
- 加锁
- 缓存预热:系统上线,直接将相关缓存加到系统
- 直接写一个缓存页,上线手动刷新一下
- 数据量不大,系统启动直接加载
- 定时刷新
- 缓存降级:高并发,服务出现问题、非核心服务影响到核心服务,自动降级或手动减级,例:radis出现问题,不去访问数据库,直接返回默认值给用户
- 缓存雪崩:缓存同一时间大面积失效,就会有大量请求同时落在数据库上,造成数据库崩溃
- Redisson:Redis官方推荐的Java版的Redis客户端
数据库学习
最新推荐文章于 2021-11-03 10:40:09 发布