索引是用来快速寻找特定的记录;把无序的数据变成有序的查询
把创建索引的列数据进行排序
对排序结果生成倒排表
在倒排表的内容上拼接上地址链
在查询时,先拿到倒排表内容,再取出地址链,最后拿到数据
聚簇索引:进数据与索引放在一起,找到索引也找到了数据
非聚簇索引:将数据与索引分开存放,索引结构的叶子节点指向了数据的位置,通过位置找到数据
区别:
查询聚簇索引可以直接获取数据,非聚簇索引需要二次查询
聚簇索引适合范围查询 ,非聚簇索引适合排序
索引的数据结构与具体的存储引擎有关。比较常用的是Hash索引和B+树索引;
Hash索引: 采用一定的hash算法,把键值换算成新的hash;等值查询,那么hash索引有明显优势,前提是健值都是唯一的,如果健值不唯一,那就需要先找到健的位置,然后再扫描链表,找到对应的值;范围查询,hash索引就不好用了
B+树索引:关键字检索效率比较平均;常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高
查询更快,占用空间更少
适合出现在where子句中的列,或者连接子句中指定的列
基数较小的列,索引效果差,不需建
不要过度索引。索引需要磁盘空间
定义为外键的列一定要建立索引
频繁更新的字段不适合建立索引
对于查询涉及少,重复值较多的列不建立索引
定义为text、image、bit之类的数据类型不建立索引
基本特性(ACID):
原子性:事务的最小执行单位,不允许分隔。确保动作要么全部完成,要么不完成
一致性:执行事务前后数据保持一致,多个事务对同一个数据的读取结果是相同的
隔离性:一个事务的修改在最终提交前,对其他事务是不可见的
持久性:一个事务被提交后,所做的修改永久保存在数据库中
隔离原则:
读未提交:可能会读取到其他事务未提交的数据,也叫脏读
读已提交(oracle):只会读取已提交的事务,两次读取的结果不一致,叫做不可重复读
可重复读(mysql):每次读取的数据都是一致的,但有可能会出现幻读
可串行化:一般不使用,会给每行加锁,会导致大量超时和锁竞争的问题
什么是分库分表:当数据量过大时,查询速度降低。为提升效率,将一个表中的数据分散到多个数据库的多个表中。
常用分库分表工具:MyCat、ShardingSphere
数据分片方式:
垂直分片:从业务角度将不同的表拆分到不同的库中,能解决数据库数据文件过大的问题,但不能从根本上解决查询问题。
水平分片:从数据角度将一个表中的数据拆分到不同的库或者表中,这样可以从根本上解决数据量过大造成的查询效率低的问题。
分片策略:
取余:均匀存放数据,但扩容非常麻烦
按照范围:比较好扩容,但数据分布不够均匀
按照时间:比较容易将热点数据区分
按照枚举值:例如按地区分片
按照目标字段前缀指定分区:自定义业务规则分片
水平分片从理论上突破了单机数据量处理的瓶颈,并且拓展自由,是分库分表的标准解决方案
阿里开发手册建议:一个表的数据超过500万或者数据文件达到2G(业务开始前,提前预估3年业务量)
分库分表后执行流程(ShardingSphere):
sql解析->查询优化->sql路由->sql改写->sql执行->结果归并
分库分表问题:
跨库查询、跨库排序、分布式事务、公共表、主键重复......
主从同步:当主库的数据发生改变时,变化会实时同步到从库;
主从同步好处:
水平拓展数据库的能力
容错、高可用
数据备份
实现:在主库机器上,主从同步事件会被写到特殊的log文件中;在从库机器上,从库读取主从同步事件,并根据读取的事件变化,在从库上做对应的更改
Innodb索引是聚簇索引,Myisam索引是非聚簇索引
Innodb的主键索引叶子节点存储着行数据,因此主键索引很高效;Myisam的主键索引叶子节点存储着行数据地址
Innodb非主键索引的叶子节点存储的是主键和其他带索引的列数据
主键:特殊的唯一索引,一张表中只有一个
唯一索引:保证数据的唯一性
普通索引:允许被索引的数据列包含重复的值
索引可以极大地提高数据的查询速度,提高系统的性能;但是删除、新增、修改的速度会降低;每一个索引都要占用物理空间。
id:语句中每出现一次select,就会分配一个唯一id,某些子查询会被优化成join,id就会一致
select_type:select关键字对应的查询类型
table:表名
partitions:匹配的分区信息
type:针对单表的查询方式(全表扫描、索引)
possible_keys:可能用到的索引
key:实际用到的索引
key_len:实际用到的索引长度
ref:当使用索引查询时,与索引列进行值匹配的对象信息
rows:读取的记录条数
filtered:表过滤后的剩余记录百分比
Extra:额外信息
执行sql时,当前sql所需要查询出来的字段数据在索引对应的B+树中都包含了,不需要再去找,直接返回结果。
最左优先,在创建索引时,最频繁的一列放在最左侧
Innodb收到语句,根据条件查询数据所在页,并缓存在Buffer Pool中
执行修改语句,修改Buffer Pool数据
针对修改语句生成一个
B树:节点排序、一个节点可以存多个元素,多个元素也排序了
B+树:拥有B树的特点、叶子节点之间有指针、非叶子节点在叶子节点上有冗余,并且排好序
Mysql索引使用B+树,索引为了加快查询,而B+树在叶子节点上存储所有元素并且进行排序可以提高查询速度
行锁:指锁住表的某一行或多行,其他事务访问时,被锁住的行不能访问,其他正常
表锁:指锁住整个表,其他请求只能读,不能写;直到读锁释放,才能写入
死锁:多个进程在执行过程中,争夺资源造成相互等待,无法继续执行
乐观锁:假设数据不会冲突,所以在数据提交更新时才会检测,如果冲突则返回错误信息
悲观锁:当对数据库中一条数据修改时,为了避免被其他人修改,直接加锁,防止并发
共享锁:当数据加上锁后,其他事务只能读锁,而不能加写锁;直到所有读锁释放完毕,才能加写锁
排它锁:当一个事务为数据加上写锁时,其他请求不能再加任何锁,直到该锁释放
检查是否走了索引,如果没有则优化sql利用索引
检查是否用了最优索引
检查字段是否都是必须的,是否查询了过多字段,查出多于数据
检查是否需要分库、分表
检查数据库配置,是否需要增加资源
主键索引:数据列不允许重复,不允许为null,一个表只能有一个
唯一索引:数据列不允许重复,允许为null,一个表允许多个列创建唯一索引
普通索引:基本的索引类型,没有唯一性的限制,允许为NULL值
非最左匹配(以最左的为起点字段查询可以使用联合索引,否则不能使用联合索引)
错误模糊查询(只有右模糊查询才能触发索引)
列运算(索引列使用了运算)
使用函数(索引列使用函数)
类型转换(字段为字符串类型,但是传入int类型,索引失效)
使用is not null
使用(!=或<>)
索引列使用了or
主键索引不允许为null,且唯一;唯一索引允许为null,允许多个列创建唯一索引
主键一定会创建一个唯一索引,有唯一索引的列不一定为主键
主键索引只能有一个,但可以有多个唯一索引
主键可以被其他表引为外键,唯一索引不行
主键的执行顺序要高于唯一索引
主键是约束,但唯一索引是索引
InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引
InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效
MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
d.InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会
非常高效
内存
纯内存操作
核心是基于非阻塞的IO多路复用机制
避免了多线程的频繁上下文切换带来的性能问题
一组按顺序执行的命令集合;事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
实现:
事务开始
命令入队
事务队列
执行事务
数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器;
好处:
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
故障恢复:如果master宕掉了,使用哨兵模式,可以提升一个 slave 作为新的 master,进而实现故障转移,实现高可用
负载均衡:可以轻易地实现横向扩展,实现读写分离
流程:
slave服务器连接到master服务器,便开始进行数据同步
master服务器收到psync命令之后,开始执行bgsave命令生成RDB快照文件并使用缓存区记录此后执行的所有写命令
master服务器bgsave执行完之后,就会向所有Slava服务器发送快照文件,并在发送期间继续在缓冲区内记录被执行的写命令。
slave服务器收到RDB快照文件后,会将接收到的数据写入磁盘,然后清空所有旧数据,在从本地磁盘载入收到的快照到内存中,同时基于旧的数据版本对外提供服务。
master服务器发送完RDB快照文件之后,便开始向slave服务器发送缓冲区中的写命令。
slave服务器完成对快照的载入,开始接受命令请求,并执行来自主服务器缓冲区的写命令;
如果slave node开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF。
String:字符串;计数器、分布式id
hash:哈希;存储对象
List:列表;消息流数据
Set:集合;共同关注、点赞
ZSet:有序集合;排行榜
分布式锁,即分布式系统中的锁,解决了分布式系统中控制共享资源访问的问题;
基于set命令的分布式锁;
基于setnx、get、getset的分布式锁
基于RedLock的分布式锁
基于Redisson看门狗的分布式锁
Redis 的并发竞争 Key 的问题也就是多个系统同时对一个 key 进行操作
解决方案:
乐观锁:适用于大家一起抢着改同一个key,对修改顺序没有要求的场景,使用watch命令来实现
分布式锁:在业务层进行控制,操作 redis 之前,先去申请一个分布式锁,拿到锁的才能操作
时间戳:写入时保存一个时间戳,写入前先比较自己的时间戳是不是早于现有记录的时间戳,如果早于,就不写入
消息队列:在并发量很大的情况下,可以通过消息队列进行串行化处理
主从模式:使用一个redis实例作为主机,其余的实例作为备份机,主机和从机的数据完全一致,主机支持数据的写入和读取等各项操作,而从机则只支持与主机数据的同步和读取
优缺点:
一个主节点可以同步多个从节点
主节点自动将数据同步到从节点,可以进行读写分离
主从之间的同步是以非阻塞的方式进行的,同步期间,客户端仍然可以提交查询或更新请求
不具备自动容错与恢复功能
难以支持在线扩容
哨兵模式:Redis集群站哨的,一旦发现问题能做出相应的应对处理;监控主节点、从节点是否正常运行当主节点出现故障时,能自动将一个从节点转换为主节点(大哥挂了,选一个小弟上位)多个哨兵可以监控同一个Redis,哨兵之间也会自动监控
Redis Cluster模式:Cluster采用无中心结构,客户端与redis节点直连,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
缓存雪崩:缓存同一时间大面积失效,所有请求落在数据库上
解决:
缓存数据过期时间设置随机,防止同一时间大面积过期
给每个缓存增加标记,记录缓存是否失效,如果失效,则更新数缓存
缓存预热
互斥锁
缓存穿透:缓存和数据库中都没有数据,所有请求都落在数据库上
解决:
接口层增加校验,如用户鉴权校验,id做基础校验
从缓存取不到的数据,在数据库中也没有取到,可以将value设为null
布隆过滤器,将所有数据存到bitmap中,不存在数据会被拦截掉
缓存击穿:同一条数据,缓存中没有,数据库中有,但由于访问并发量大,造成数据库压力大
解决:
设置热点数据永远不过期
加互斥锁,互斥锁缓存预热
延迟双删:先删除redis数据,再更新mysql,延迟几百毫秒,再删除redis数据;
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行
持久化机制:redis数据都是存储在内存中,为了避免数据丢失,需要将数据以某种形式从内存中保存到硬盘中,重启时利用持久化数据恢复。
RDB(快照):指定时间间隔将数据进行快照存储,重启时读取文件来恢复数据
触发方式:
配置文件
执行save命令(同步操作命令)
执行bgsave命令(对save命令的优化,异步操作)
执行shutdown命令(客户端执行命令)
执行filshall命令(这个命令就相当于删库跑路)
AOF(日志文件):记录每次对服务器的操作,当服务器重启重新执行这些命令恢复原始数据
aof持久化方式redis是默认不开启的,我们可以通过配置文件开启aof持久化方式(appendonly的值默认为no,改为yes)
redis作为缓存存储的时候,一般都要设置过期时间,否则垃圾数据会占用大量的内存
分开设置:先设置值,再设置时间(非原子操作,极端情况下会出现问题);
合并设置:一条命令设置过期时间和值(推荐)
跟redis的内存回收策略有关,默认是当内存用完写入数据报错
回收策略:
使用LRU(最近最少使用)策略移除key,只针对过期key
使用LRU(最近最少使用)策略移除key,针对所有的key
使用LRU(最近最不常使用)策略移除key,只针对过期key
使用LRU(最近最不常使用)策略移除key,针对所有的key
随机移除一个key
随机移除一个过期key
移除过期时间最少的key,即将要过期的key
不移除key,写入时返回error
惰性过期:当访问key时,检查是否过期,过期则删除;最大化节约cpu,但对内存不友好;极端情况下出现大量过期key未被访问,会占用大量内存
定期过期:每隔一段时间扫描一定数量的key,并清除已过期的key;通过调整扫描时间间隔和扫描耗时,可以让cpu和内存达到最优的平衡效果
Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作
Redis集群之间是采用异步复制的
Redis集群最大节点个数是16384个
Redis集群目前无法做数据库选择,默认在0数据库
主节点不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化
如果数据比较关键,某个从节点开启AOF备份数据,策略为每秒同步一次
为了主从复制的速度和连接的稳定性,主从节点最好在同一个局域网内
尽量避免在压力较大的主库上增加从库
为了主节点的稳定性,主从复制不要用图状结构,用单向链表结构更稳定,主节点挂了,可以立马启用Slave1做主节点,其他不变
发生条件:
互斥,共享资源只能被一个线程占用
占有线程,等待其他被占有线程
其他线程不能强行抢占线程资源
循环等待
解决:
使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务
设置死锁超时参数为合理范围,超过时间,自动放弃本次操作,避免进程悬挂
优化程序,检查并避免死锁现象出现
对所有的脚本和程序都要仔细测试
所有的程序都要有错误处理