网上收集整理,仅供笔记参考学习
数据库通关整理1-索引、聚簇索引、非聚簇索引、MyISAM和InnoDB存储引擎、范式
数据库通关整理2-事务、并发问题、事务隔离级别、锁(S锁、X锁`)、乐观锁、悲观锁、sql注入、内连接、外连接
数据库通关整理3-Redis基础(数据库、缓存、消息中间件)、RDB、AOF、发布订阅、主从复制、哨兵模式、缓存穿透、缓存击穿、缓存雪崩
基础
为什么要用Nosql
发展历程:
1.单机MySQL
2.Memcached(缓存) + MySQL + 垂直拆分(读写分离)
3.分库拆表 + 水平拆分 + MySQL集群
4.关系型数据库不够用!数据很多,变化很快
为什么要使用NoSQL!
用户的个人信息、社交网络、地理位置。用户自己产生的数据,用户日志等等爆发式增长!
这时候我们就就需要使用NoSQL数据库,NoSQL可以很好的处理以上的情况
Redis
Redis(Remote Dictionary Server),即远程字典服务
Redis 是速度非常快的非关系型(NoSQL)数据库,可基于内存亦可持久化的日志型、Key-Value数据库。可以用作数据库
、缓存
和消息中间件MQ
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
Redis使用场景
- (数据库、缓存、消息中间件)
1、内存存储、持久化、内存中是断电即失、所以说初九话很重要(RDB、AOF)
2、效率高、可以用于高速缓存
3、发布订阅系统
4、地图信息分析
5、计时器、计数器(浏览量)
6、…
特性:
1、多样的数据类型
2、持久化
3、集群
4、事务
…
Redis基础知识
1、默认是16个数据库
2、Redis是单线程的
3.为什么是单线程还这么快?
误区1:高性能的服务器一定是多线程的
误区2:多线程(CPU上下文切换)一定比单线程效率高
核心:redis是将所有数据放在内存中的,所以说使用单线程去操作效率最高。多线程(CPU上下文切换:耗时)
五种数据类型
string (字符串)
string 是 redis 最基本的类型,一个 key 对应一个 value。
string 类型的一个键最大能存储 512MB。
redis 127.0.0.1:6379> SET runoob “Helloworld”
OK
redis 127.0.0.1:6379> GET runoob
“Helloworld”
场景:计数器、统计多单位的数量
Hash(哈希)
Map集合,key-value中的value 是一个Map
hash 是一个value键值(key=>value)对集合。
hash 是一个 string 类型的 field 和 value 的映射表.
redis 127.0.0.1:6379> HMSET runoob field1 “Hello” field2 “World”
- hash变更的数据 user name age,尤其是用户信息之类,经常变动的值
- hash更加适合对象的存储,string更适合字符串的存储
List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
redis 127.0.0.1:6379> lpush runoob redis
- 实际是一个双向链表,before Node after , left, right都可插入
- 如果key不存在,新增链表
- 如果key存在,新增内容
- 如果移除所有值,空链表,也表示不存在
- 在连边插入或者改动之,效率最高!中间元素,相对来说效率会低一点
- 消息排队!消息队列(Lpush Rpop),栈(Lpush Lpop)
Set(集合)
Redis 的 Set 是 string 类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
redis 127.0.0.1:6379> sadd runoob redis
- 微博,A用户将所有关注的人放在一个set集合中!将它的粉丝也放在一个集合中!
- 共同关注,共同爱好,二度好友,推荐好友!(六度分隔理论)
Zset(有序集合)
在 set 的基础上,增加一个值(分数),然后可以进行排序
关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
redis 127.0.0.1:6379> zadd runoob 0 redis
- 案例思路:set 排序 存储班级成绩表,工资表排序!
- 普通消息、重要消息,带权重判断
- 排行榜实现,取top N测试
三种特殊数据类型
范围查询
Geospatial(地理位置)
朋友的定位,附近的人,打车距离计算
这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人
只有6个命令
- 附近的人?(获得所有附近的人的地址,定位!),通过半径来查询
- 获得指定数量的人
Hyperloglog(不重复数)
什么是基数?A {1,3,3,5,7}
不重复的元素个数 = 4,可以接受误差
Hyperloglog数据结构,Hyperloglog 基数统计的算法
网页的UV(一个人访问一个网站多次,但是还是算作一个人!)
传统的方式,set保存id,统计set中数量作为标准判断。如果保存大量的id,就比较麻烦!我们的目的是计数,不是保存数据id
优点:占用内存是固定的,2^64不同的元素的技术,只需要废12kb内存,如果要从内存角度上来说,Hyperloglog是首选
0.81%错误率!统计UV任务,可以忽略不记
Bitmap(位图)
位存储
统计用户信息,活跃,不活跃!登录,未登录!打卡【0 1 代替】
Bitmap位图,数据结构!操作二进制位来进行记录,只有0/1两个状态
事务
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺寻执行!
- Redis 单条命令是保证原子性,但是Redis的事务是不保证原子性的
- Redis的事务没有隔离级别的概念!
所有的命令在事务中,并没有直接执行!只有发起执行命令才会执行 Exec
redis事务:
- 开启事务(multi)
- 命令入队()
- 执行事务(exec)
放弃事务:
-discard
异常:
- 编译时异常。代码有误,事务所有的命令都不会被执行!
- 运行时异常(1/0)。错误命令抛出异常,其他命令正常执行!
监控-悲观锁、乐观锁《面试常问》
悲观锁
- 很悲观,认为什么时候都会出现问题,无论做什么都会加锁!
redis可以实现乐观锁----watch,类似对比version版本号
解锁unwatch
乐观锁
- 很乐观,认为什么时候都不会出问题,所以不会上锁!在提交数据的时候,需要判断一下,在此期间,数据是否被其他改动。
- 获取version
- 更新的时候比较version
- watch命令可以当成redis的乐观锁操作!
Jedis
import redis.clients.jedis.Jedis;
public class RedisStringJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
//如果有密码,输入
// jedis.auth("123456");
System.out.println("连接成功");
//设置 redis 字符串数据
jedis.set("runoobkey", "www.runoob.com");
// 获取存储的数据并输出
System.out.println("redis 存储的字符串为: "+ jedis.get("runoobkey"));
}
}
SpringBoot整合
SpringBoot操作数据:spring-data jpa mongodb redis
SpringData也是和SpringBoot齐名的项目!
- 说明在SpringBoot2.x之后,原来使用的jedis被替换为了
lettuce
- jedis:底层采用的是直连,多个线程操作的话,是不安全的,如果想要避免不安全,使用jedis pool连接池!更像BIO模式!
- lettuce:底层采用的是netty,实力可以在多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据,更像NIO模式!
1、导入依赖
<!-- 操作redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置连接
# 配置redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
3、测试
- 在企业开发中,80%的情况下,都不会使用这个原生的方式来编写代码
- 在我们真实的开发中,一般可以看到一个公司自己封装的
RedisUtils
工具类
进阶
Redis.conf
快照
- 持久化,在规定的时间内,执行了多少次操作,则会持久化到 .rdb .aof
- redis是内存数据库,如果没有持久化,那么数据断电即失
.rdb文件
300秒内,如果至少有 10 个 key 的值变化,则进行持久化操作
- save 300 10
安全
- 可以设置redis的密码,默认没有密码
aof配置 -APPEND ONLY 模式
- appendonly no
默认是不开启aof模式的,默认是使用rdb方式持久化,在大多数情况在,rdb而完全够用- appendonly.aof 持久化的文件名字
三种配置方式:appendfsync everysec 相对常用
appendfsync always
每次修改都会sync。消耗性能appendfsync everysec
每秒执行一次sync。可能会丢失这一秒的数据!appendfsync no
不执行sync。这时候操作系统自己同步数据,速度最快
具体的配置,在持久化中
…
Redis持久化
RDB
流程
(1)redis根据配置自己尝试去生成rdb快照文件
(2)fork一个子进程出来
(3)子进程尝试将数据dump到临时的rdb快照文件中
(4)完成rdb快照文件的生成之后,就替换之前的旧的快照文件
dump.rdb,每次生成一个新的快照,都会覆盖之前的老快照
rdb保存的文件是dump.rdb,有时候在生产环境下,我们需要备份这个文件
-
触发机制
1、save的规则满足的情况下,会自动出发rdb规则
2、执行flushall命令,也会出发我们的rdb规则
3、退出redis,也会产生rdb文件
备份就自动生成一个dump.rdb -
如何恢复rdb文件
1、只需要将rdb文件放在我们redis启动目录就可以了。redis启动的时候救护i自动检查dump.rdb恢复其中的数据!
2、查看需要存放的位置
config get dir
“dir”
“/usr/local/bin” 如果在这个目录下的dump.rdb文件没启动就会自动恢复其中的数据
- 优点
1、适合大规模的数据恢复!dump.rdb
2、对数据要求不高! - 缺点
1、需要一定的时间间隔进程操作,如果redis意外宕机,最后一个修改的数据就没有了。
2、fork进程的时候,需要占用一定的空间
AOF
流程
(1)redis fork一个子进程
(2)子进程基于当前内存中的数据,构建日志,开始往一个新的临时的AOF文件中写入日志
(3)redis主进程,接收到client新的写操作之后,在内存中写入日志,同时新的日志也继续写入旧的AOF文件
(4)子进程写完新的日志文件之后,redis主进程将内存中的新日志再次追加到新的AOF文件中
(5)用新的日志文件替换掉旧的日志文件
将我们的所有命令都记录下来,恢复的时候就把这个文件全部执行一遍
- aof保存的是appendonly.aof文件。记录我们的所有写操作
- 默认不开启,需要手动开启配置
appendonly
改为yes就可以了
如果aof文件有错误,redis重启时就会拒绝恢复数据
- redis提供
redis-check-aof --fix
命令可以修复aof文件
如果文件正常,直接重启数据就恢复了
- 优点
1、每一次修改都同步,文件的完整性会更好
2、每秒同步一次,最多只会丢失一秒的数据
3、从不同步,效率最高 - 缺点
1、相对数据文件来说,aof远远大于rdb,修复的数据速度也比rdb慢
2、aof运行的效率比rdb慢,所以redis里面默认的
AOF重写机制
redis中的数据会不断淘汰掉旧的,就一部分常用的数据会被自动保留在redis内存中,所以可能很多之前的已经被清理掉的数据,对应的写日志还停留在AOF中,AOF日志文件就一个,会不断的膨胀,到很大很大,所以AOF会自动在后台每隔一定时间做rewrite操作,比如日志里已经存放了针对100w数据的写日志了; redis内存只剩下10万; 基于内存中当前的10万数据构建一套最新的日志,到AOF中; 覆盖之前的老日志; 确保AOF日志文件不会过大,
- redis2.4之后,就会自动进行rewrite操作
上一次AOF rewrite之后,是128mb。然后就会接着128mb继续写AOF的日志,如果发现增长的比例,超过了之前的100%,256mb,就可能会去触发一次rewrite但是此时还要去跟min-size,64mb去比较,256mb > 64mb,才会去触发rewrite
Redis发布订阅
Redis发布订阅是一种消息通信模式:发送者发布信息,订阅者接收信息。微信、微博、关注系统!
Redis客户端可以定语任意数量的频道
订阅/发布图:
第一个:消息发送者;第二个:频道;第三个消息订阅者
- 命令被广泛用到构建即时通信应用,比如网络聊天室和实时广播、实时提醒
- 使用场景
1、实时消息系统!
2、实时聊天()频道当作聊天室,将消息回显给所有人即可
3、订阅,关注系统都是可以 - 稍微复杂的场景我们就会使用消息中间件MQ()
Redis主从复制
数据的复制是单向的,只能由主节点到从节点。
主从复制的作用主要包括:
1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2、故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
4、高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
环境搭建
复制三个redis配置文件,然后修改对应的信息
1、端口
2、pid名字
3、log
4、dump.rdb文件名
修改完毕后,启动三个redis服务器,可以通过进程信息查看
一主二从
默认情况下,每台Redis服务器嗾使主节点,一般情况下配从机就可以了。
认老大!
命令配置
- 从机配置:SLAVEOF 127.0.0.1 6379
真实的从主配置应该在配置文件中配置,这样就是永久的,命令配置是暂时的。
细节
- 主机可以写,从机不能写只能读。主机中的信息和数据,都会被从机保存。
测试:主机断开连接,从机依旧连接到主机的,但是没有写操作,此时,如果主机回来了,从机一九可以直接获取到主机写的信息!
如果是使用命令行配置的主从,如果从机重启,则默认变回主机!只要重新设置为从机,立马就可以从主机中获取到值。
复制原理
Slave启动成功连接到Master后,会发送一个sync(同步)命令
- 全量复制
- 增量复制
只要重新连接master,一次完全同步(全量复制)会被自动执行,数据一定可以在从机中看到!
层层链路
一个接一个
如果主机宕机,或者没有主机,可手动选择老大!
谋财篡位
- 如果主机断开连接,我们可以使用
SLAVEOF no one
,手动让自己变为主机,其他节点就可以连接到最新的这个主节点(手动命令) - 如果这时候老大回来了,需要重新配置
哨兵模式(Sentinel)-重点
主从切换技术方法:当主服务器宕机之后,需要手动把其中的一台从服务器切换成主服务器,需要人工命令干预设置,这样费时费力,还会造成一段时间内服务不可用。 这不是一种推荐的方式,更多的时候,优先考虑哨兵模式。
基础模式:
- 这里哨兵的两个作用
1、通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
2、当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
谋财篡位的自动版,可以后台监控主机是否故障,若故障就会根据票数自动将从机转换为主机。
多哨兵模式:
一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
- 优点
1、哨兵集群,基于主从复制模式,所有的主从配置优点,它都有
2、主从可以切换,故障可以转移,系统的可用性就会更好
3、哨兵模式就是主从复制的升级,手动到自动回滚,更加健壮 - 缺点
- 1、Redis不好在线扩容,集群容量一旦达到上限,在线扩容九十分麻烦!
- 2、实现
哨兵模式的配置很麻烦,里面的选择有很多
故障切换(failover)的过程描述。假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。
Redis缓存穿透和雪崩(面试高频,工作常用)
作为一个内存数据库,redis也总是免不了有各种各样的问题
缓存穿透(查不到)
概念
用户想要查询一个数据时,发现redis内存数据库中没有,也就是缓存没有命中,于是向持久化数据库查询。发现也没有,语事本次查询失败。当用户很多时,缓存都没有命中(),语事都去请求持久数据库。 这会给持久数据库造成很大的压力,是时候就相当于缓存穿透。
解决方案
布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,当用户想要查询的时候,使用布隆过滤器发现不在集合中,就直接丢弃,不再对持久层查询。
将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
缓存空对象
当首次持久存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后在访问这个数据就可以在缓存智官获取,保护了后端数据库
- 问题/缺点
1、需要更多的空间存储更多空值的键
2、即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口不一致,这对保持一致性的业务有影响
缓存击穿(量不大,缓存过期)
微博服务器宕机
集中访问缓存中的一个key
概述
key对应的数据存在,但在redis中过期失效,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
缓存击穿,是指一个key(或者某些key)非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
解决方案
设置热点数据不过期
加互斥锁
加锁只保证只有一个进程到服务器查询,其余等待,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。
缓存雪崩
概念
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
缓存雪崩是指,缓存层出现了错误,不能正常工作了。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。
在一定时间内,缓存几种过期失效,redis宕机。
比如说双十一前一个小时的抢购,快到1点时,缓存集体过期
- 更致命的时服务器某个节点宕机或者断网
双十一:停掉退款服务
- 解决方案
1、redis高可用
思想含义:既然redis有可能挂掉,那就多增设几台redis,这样一台挂掉之后,其他的还可以继续工作。其实就是搭建集群。(异地多活!)
2、限流降级
思想:在缓存失效后,通过加索或者队列来空值读数据库写缓存的线程数量。比如对某个key只允许一个线程进行查询数据和写缓存,其他线程等待。
3、数据预热
数据加热的含义就是在正式部署之前,先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到内存。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀
高可用:
通过设计减少系统不能提供服务的时间。增加容错性。如:主从复制、集群