狂神视频学习笔记
https://www.bilibili.com/video/BV1S54y1R7SB?p=36
一、Nosql概述
1. 什么是Nosql
NoSQL = Not Only SQL(不仅仅是SQL)
Not Only Structured Query Language (POI jar包可以操作excel)
2. 传统的 RDBMS 和 NoSQL
传统的 RDBMS(关系型数据库) | Nosql |
---|---|
a) 结构化组织 b) SQL c) 数据和关系都存在单独的表中 row col d) 严格的一致性 e) 基础的事务 | a) 数据类型是多样型的!(不需要事先设计数据库) b) 没有固定的查询语言 c) 键值对存储,列存储,文档存储,图形数据库 d) 最终一致性 e) 高性能,高可用,高扩展 |
3. Nosql的四大分类
-
Redis KV键值对
-
MongoDB (需要掌握)
基于分布式文件存储的数据库,用于处理大量文档。MongoDB是关系型数据库和NoSQL的中间产品。Key-Value对应的键值对,Value为结构化数据。
- ConthDB列存储数据库
HBase(大数据必学),分布式文件系统
- 图关系数据库Neo4j、InfoGrid
用于广告推荐,社交网络。
4. 数据库及其应用
1. 商品信息
一般存放在关系型数据库:Mysql,阿里巴巴使用的Mysql都是经过内部改动的。
2. 商品描述、评论(文字居多)
文档型数据库:MongoDB 需要掌握
3. 图片
分布式文件系统 FastDFS:淘宝:TFS、Google: GFS、Hadoop: HDFS、阿里云: oss
4. 商品关键字 用于搜索
搜索引擎:solr,elasticsearch 阿里:Isearch 多隆
5. 商品热门的波段信息
内存数据库:Redis,Memcache
二、Redis入门
1. 概述
1.1 Redis(Remote Dictionary Server)是什么?
数据都是缓存在内存中。区别Memcache
的是redis会周期性的把更新的数据写入磁盘或者把修改操作追加到日志文件,并且在此基础上实现了master-slave(主从)复制。
1.2 Redis能该干什么
a) 内存存储数据库,效率高可用于高速缓存。
b) 内存是断电即失的,但redis支持(RDB、AOF)两种持久化方式。
c) 发布订阅系统(消息队列)
d) 地图信息分析
e) 计时器、计数器(eg:浏览量)
1.3 特性
a) 多样的数据类型
b) 支持持久化
c) 主从复制,支持集群部署
d) 支持事务
2. Docker搭建
docker run -itd --name myredis -p 6379:6379 redis 启动 (docker run -d -p 6379:6379 myredis)
docker exec -it myredis /bin/bash 进入交互模式
redis-cli 连接进入
shutdown 退出
docker start/restart xxx 启动进入
3. 基础知识与操作
redis默认有16个数据库, 16个数据库为:DB 0~DB 15,不同数据库可以存储不同的数据。
3.1 Redis是单线程的(仍然比Memecache快),
Redis是基于内存操作的。所以Redis的性能瓶颈不是CPU,而是机器内存和网络带宽。
3.2 Redis为什么单线程还这么快?
多线程操作存在CPU上下文会切换(消耗资源),多线程访问需要加锁和解锁操作(消耗资源)
对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存存储数据情况下,单线程就是最佳的方案。
3.3 数据库级别的操作:
config get databases | 查询redis的配置信息,16数据库 |
---|---|
select n | 切换到第n个DB |
dbsize | 查看当前数据库的大小(key数量相关)。 |
keys * | 查看数据库所有的key |
flushall | 清空所有的数据库 |
flushdb | 清空当前的数据库 |
set key value | / 增(改) |
del key | 删除键值对 / 删 |
move key db | 将键值对移动到指定数据库 / 改 |
exists key | 判断键是否存在 / 查 |
get key | 查询改key的值 / 查 |
keys * | 查询所有数据库的key / 查 |
type key | 查看value的数据类型 / 查 |
expire key second | 设置键值对的过期时间 |
ttl key | 查询剩下多少秒过期。-2超过设置时间;-1没设置过过期时间;其他:剩余时间 |
三、五大数据类型
1. String(字符串)
1.1 数据结构
string 类型是基本的 Key-Value 结构,Key 是某个数据在 Redis 中的唯一标识,Value 是具体的数据。
1.2 基本命令
命令 | 描述 |
---|---|
append key value | 后追加字符串,若key不存在则新建。 |
decr/incr key | 将指定key的value数值进行+1/-1(仅对于数字) |
incrby/decrby key n | 按指定的步长对数值进行加减 |
incrbyfloat key n | 为数值加上浮点型数值 |
strlen key | 获取key保存值的字符串长度 |
getrange key start end | 按起止位置获取字符串(闭区间) |
setrange key offset value | key中 offset开始的值替换为value,替换个数为value的长度 |
getset key value | 先返回key的value值,再执行set,不存在key直接set。 |
set user:1 {name:zs,age:3} | 设置一个key(user**😗*1)为json对象。1为Id。开发中-定义概念。 |
setnx key value (set not exist) | 新建的key在数据库中不存在,否则失败 |
setex key seconds value | 新建键值对并设置过期时间 |
psetex key milliseconds value | 新建键值对并设置过期时间(毫秒) |
mset key1 value1 key2 value2 | 批量set键值对 |
msetnx key1 value1 key2 value2 | Setnx的批量操作,存在原子性 |
mget key1 [key2…] | 批量获取多个key保存的值 |
1.3 String数据类型的使用场景:
(1) 存储 MySQL 中某个字段的值
set user🆔1:email 156577812@qq.com
(2) 存储对象
set user🆔1 ‘[{“id”:1,“name”:“zj”,“email”:“156577812@qq.com”},{“id”:1,“name”:“zj”,“email”:“156577812@qq.com”}]’
(3) 生成自增 id
由于 redis 所有的操作都是原子性的,所以不必担心多客户端连接时可能出现的事务问题
2. Hash(哈希)
2.1 数据结构:
key-map key-<key,value>;Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
2.2 基本命令
命令 | 描述 |
---|---|
hset key field value | 将哈希表 key 中的字段 field 的值设为 value 。重复设置同一个field会覆盖,返回0 |
hmset key field1 value1 [field2 value2…] | 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
hsetnx key field value | 字段 field 不存在时,设置哈希表字段值,存在创建失败。 |
hdel key field1 [field2…] | 删除哈希表key中一个/多个field字段 |
hget key field value | 获取存储在哈希表中指定字段的值 |
hmget key field1 [field2…] | 获取所有给定字段的值 |
hgetall key | 获取在哈希表key 的所有字段和值 |
hexists key field | 查看哈希表 key 中,指定的字段是否存在。 |
hkeys key | 获取哈希表key中所有的字段 |
hlen key | 获取哈希表中字段的数量 |
hvals key | 获取哈希表中所有值 |
hincrby key field n | 为哈希表 key 中的指定字段的整数值加上增量n,并返回增量后结果 一样只适用于整数型字段 |
hincrbyfloat key field n | 为哈希表 key 中的指定字段的浮点数值加上增量 n。 |
hscan key cursor [match pattern] [count count] | 迭代哈希表中的键值对。 |
2.3 使用场景
Hash更适合于对象的存储,Sring更加适合字符串存储!因为可以任意的删除和添加某个" 字段" 。
3. List(列表)
3.1 数据结构
Redis列表由双向链表实现,存储字符串,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
如果key不存在,则创建新的链表,如果key存在,新增内容,如果移除了所有值,空链表,list不存在。经过规则定义将其变为栈、队列、双端队列等
3.2 基本命令
Redis中List是可以进行双端操作的,所以命令也就分为了LXXX和RXXX两类,有时候L也表示List例如LLEN。
命令 | 描述 |
---|---|
lpush/rpush key value1[value2…] | 从头/尾向列表中push值(一个或者多个)。 |
lrange key start end | 获取list 起止元素(索引从左往右 递增) |
lpushx/rpushx key value | 向已存在的列名中push值(一个或者多个) |
linsert key before|after pivot value | 在指定列表中的元素(pivot)的前/后 插入value值 |
llen key | 查看列表长度 |
lindex key index | 通过索引获取列表元素 |
lset key index value | 通过索引为元素设置新值 |
lpop/rpop key | 从最左边/最右边移除值并返回 |
rpoplpush source destination | 将列表的尾部的一个值弹出,并返回,然后加到另一个列表的头部,目标没有就新建。 |
ltrim key start end | 通过下标截取指定范围内的列表,其他的删除! |
lrem key count value | 移除[key]列表中count数量的值。 |
blpop/brpop key1[key2] timout | 移出并获取列表的第一个/最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
brpoplpush source destination timeout | 和rpoplpush功能相同,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
3.3 使用场景
-
消息排队!消息队列(Lpush Rpop)
-
栈(Lpush Lpop)
-
list 结构的数据查询两端附近的数据性能非常好,所以适合一些需要获取最新数据的场景,比如新闻类应用的 “最近新闻”
4. Set(集合)
4.1 数据结构
-
Redis的Set是string类型的无序集合且集合成员是唯一的。
-
Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
-
set 类型提供了多个 set 之间的集合运算,如求交集、并集、补集。
4.2 基本命令
命令 | 描述 |
---|---|
sadd key member1[member2…] | 向集合中无序增加一个/多个成员 |
scard key | 获取集合的成员数 |
smembers key | 返回集合中所有的成员 |
sismember key member | 查询member元素是否是集合的成员,结果是无序的 |
srandmember key [count] | 随机返回集合中count(默认1)个成员 |
spop key [count] | 随机移除并返回集合中count个成员,count缺省值为1 |
smove source destination member | 将source集合的成员member移动到destination集合 |
srem key member1[member2…] | 移除集合中一个/多个成员 |
sdiff key1[key2…] | 返回所有集合的差集 key1- key2 …结果存在key1中 |
sdiffstore destination key1[key2…] | sdiff的基础上,将结果保存到destination集合中 |
sinter key1 [key2…] | 返回所有集合的交集 |
Sinterstore destination key1[key2…] | 在sinter的基础上,存储结果到集合中。覆盖 |
sunion key1 [key2…] | 返回所有集合的并集 |
sunionstore destination key1 [key2…] | 在sunion的基础上,存储结果到及和张。覆盖 |
sscan key [match pattern] [count count] | 在大量数据环境下,使用此命令遍历集合中元素,每次遍历部分 |
4.3 使用场景
集合的运算操作:“共同好友列表” 解决一般关系型数据库不方便做的工作
5. Zset(有序集合)
5.1 数据结构
不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。
5.2 基本命令
命令 | 描述 |
---|---|
zadd key score member1 [score2 member2] | 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
zcard key | 获取有序集合的成员数 |
zcount key min max | 计算在有序集合中指定区间score的成员数 |
zincrby key n member | 有序集合中对指定成员的分数加上增量 n |
zscore key member | 返回有序集中,成员的分数值 |
zrank key member | 返回有序集合中指定成员的索引 |
zrange key start end | 通过索引区间返回有序集合成指定区间内的成员 |
zrevrange key start end | 通过索引区间返回分数从高到底 (0. -1:降序排列) |
zrangebyscore key min max [withscore] | 通过分数返回有序集合指定区间内的成员-inf 和 +inf分别表示最小最大值,withscore连带显示分数。 |
zrangebylex key min(element的字符串值) max | 通过字典区间返回有序集合的成员 |
zlexcount key min max | 在有序集合中计算指定字典区间内成员数量 |
zrem key member1 [member2…] | 移除有序集合中一个/多个成员 |
zremrangebylex key min max | 移除有序集合中给定的字典区间的所有成员 |
zremrangebyrank key start stop | 移除有序集合中给定的排名区间的所有成员 |
zremrangebyscore key min max | 移除有序集合中给定的分数区间的所有成员 |
zrevrangebyscorre key max min | 返回有序集中指定分数区间内的成员,分数从高到低排序 |
zrevrangebylex key max min | 返回有序集中指定字典区间内的成员,按字典顺序倒序 |
zrevrank key member | 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
zinterstore destination numkeys key1 [key2 …] | 计算给定的一个或多个有序集的交集并将结果集存储在destination中。 numkeys:表示参与运算的集合数,将score相加作为结果的score |
zunionstore destination numkeys key1 [key2…] | 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 |
zscan key cursor [match pattern] [count count] | 迭代有序集合中的元素(包括元素成员和元素分值) |
5.3 使用场景
-
set排序 存储班级成绩表 工资表排序!
-
普通消息,1.重要消息 2.带权重进行判断
-
排行榜应用实现,取Top N测试
四、三种特殊数据类型
1. Geospatial(地理位置)
1.1 数据结构
城市经度纬度查询:http://www.jsons.cn/lngcode/
使用经纬度定位地理坐标并用一个有序集合zset保存,所以zset命令也可以使用
1.2 基本命令
命令 | 描述 |
---|---|
geoadd key long lat member […] geoadd china:city 116.40 39.90 beijing 114.05 22.52 shenzhen | 将具体经纬度的坐标存入一个有序集合。添加地理位置。 |
geopos key member [member…] | 获取集合中的一个/多个成员坐标 |
geodist key member1 member2 [unit] | 返回两个给定位置之间的距离。[unit]默认以米作为单位。 |
georadius key long lat radius m|km|mi|ft [withcoord][withdist] [withhash] [count] | 以给定的经纬度为中心,找出半径内的所有节点。withcoord:经纬度;withdist:距离;withhash: |
georadiusbymember key member radius… | 中心位置是已有的成员。 |
geohash key member1 [member2…] | 返回一个节点的经纬度转hash值。 |
指定单位的参数 unit 必须是以下单位的其中一个:
-
m 表示单位为米。
-
km 表示单位为千米。
-
mi 表示单位为英里。
-
ft 表示单位为英尺。
关于GEORADIUS的参数
-
withcoord:带上坐标
-
withdist:带上距离,单位与半径单位相同
-
COUNT n : 只显示前n个(按距离递增排序)
Geospatial的底层实现是Zset数据结构,即可以通过Zset的命令操作Geo生成的内容。
-
zrange china:city 0 -1:列出所有地图节点的名称。
-
zrem china:city beijing:删除地图节点。
1.3 使用场景
朋友的位置,附近的人,打车的直线距离计算。
2. Hyperloglog(基数统计)
2.1 数据结构
什么是基数?
数据集中不重复的元素的个数。A{1,3,5,7,8,7} 基数=5
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。
因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。其底层使用string数据类型
0.81% 错误率! 统计UV任务,可以忽略不计的!
2.2 基本命令
命令 | 描述 |
---|---|
pfadd key element1 [elememt2…] | 添加指定元素到 hyperloglog 中 |
pfcount key [key] | 返回给定 hyperloglog 的基数估算值。 |
pfmerge destkey sourcekey [sourcekey…] | 将多个 hyperloglog 合并为一个 hyperloglog |
2.3 使用场景
网页的访问量(UV):一个用户多次访问,也只能算作一个人。
如果允许容错,那么一定可以使用Hyperloglog !如果不允许容错,就使用set或者自己的数据类型即可 !
3.BitMaps(位图)
3.1 数据结构
使用位存储,信息状态只有 0 和 1
Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR,NOT以及其它位操作。
3.2 基本命令
命令 | 描述 |
---|---|
setbit key offset(星期几) value(0/1 是否打卡) | 为指定key的offset位设置值 |
getbit key offset | 获取offset位的值 |
bitcount key [start end] | 统计字符串被设置为1的bit数,也可以指定统计范围按字节 |
bitop operration destkey key[key…] | 对一个或多个保存二进制位的字符串 key 进行位操作,并将结果保存到 destkey 上。 |
BITPOS key bit [start] [end] | 返回字符串里面第一个被设置为1或者0的bit位。start和end只能按字节,不能按位 |
3.3 使用场景
签到统计、状态统计(登录/未登录 ;活跃/不活跃)
五、事务
1. 概述
Redis的单条命令是保证原子性的,但是redis事务不能保证原子性(运行时异常的处理方式)
事务中的命令在加入时都没有被执行,直到提交时才会开始执行(Exec)一次性完成。
Redis事务本质:一组命令的集合。事务中每条命令都会被序列化(队列),执行过程中按顺序执行,不允许其他命令进行干扰。一次性、顺序性、排他性
Redis事务没有隔离级别的概念。
2. Redis事务操作
2.1 事务执行过程
-
开启事务(
multi
) -
命令入队
-
执行事务(
exec
) | 取消事务(discurd)
2.2 事务错误
编译时异常:代码语法错误,所有的命令都不执行。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> error k1 # 这是一条语法错误命令
(error) ERR unknown command `error`, with args beginning with: `k1`, # 会报错但是不影响后续命令入队
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors. # **执行报错**
运行时异常:代码逻辑错误,逻辑错误代码不执行,其他代码执行。所以只保证单条命令的原子性,所以不保证事务原子性``。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> INCR k1 # 这条命令逻辑错误(对字符串进行增量)QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range # 运行时报错
4) "v2" # 其他命令正常执行
3. 监控(锁机制)
**悲观锁:**很悲观,无论做什么都会加锁(synchroniezd、读写锁)
**乐观锁(一般使用):**很乐观,不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据(CAS、版本控制)。
watch:加乐观锁
unwatch:解除乐观锁。
使用watch key
监控指定数据,相当于加乐观锁。一旦事务执行exec,无论是否成功监控都会被解除。
3.1 多线程下执行事务
我们启动另外一个客户端模拟插队线程。
线程1:
127.0.0.1:6379> watch money # money上锁
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY use 20
QUEUED
127.0.0.1:6379> # 线程2进行修改后,此时事务并没有执行
模拟线程插队,线程2:
127.0.0.1:6379> INCRBY money 500 # 修改了线程一中监视的money
(integer) 600
回到线程1,执行事务:
127.0.0.1:6379> EXEC # 执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败
(nil)
线程1如果还想控制money,需要先放弃监控 unwatch,之后再进行监控watch获取最先的乐观锁。
六、Jedis(java执行redis的jar包)
七、SpringBoot整合
1. 编写配置文件
# ``配置``redis
spring.redis.host=39.99.xxx.xx
spring.redis.port=6379
2. 使用RedisTemplate
1. @SpringBootTest
2. class Redis02SpringbootApplicationTests {
3. // 默认是jdk序列化,需要手动配置为Sting序列化
4. @Autowired
5. private RedisTemplate redisTemplate;
6. @Test
7. void contextLoads() {
8. redisTemplate.opsForValue().set("mykey","kuangshen");
9. System.out.println(redisTemplate.opsForValue().get("mykey"));
10. }
11. }
2.1 定制RedisTemplate的模板:
1.设置其他序列化方式。
2.自定义Redis工具类,重新封装API方法:
https://www.cnblogs.com/zeng1994/p/03303c805731afc9aa9c60dbbd32a323.html
https://www.cnblogs.com/zhzhlong/p/11434284.html
八、Redis.conf
1. 配置文件的unit单位对大小写不敏感。容量单位不区分大小写,G和GB有区别
2. 可以使用 include 组合多个配置文件
3. 网络配置
bind 127.0.0.1 # 绑定的ip
protected-mode yes # 保护模式 默认开启
port 6379 # 端口设置
4.日志输出级别
5. 日志输出文件
6. 持久化规则:RDB、AOF
AOF配置
默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下,rdb完全够用!
RDB(持久化)文件相关
7.主从复制
8.Security模块中进行密码设置
登录后,获得密码:config get requirepass
设置密码:config set requirepass “123456”
密码登录:auth 123456
9.客户端连接相关
maxclients 10000 最大客户端数量
maxmemory <bytes> 最大内存限制,默认字节
maxmemory-policy noeviction # 内存达到限制值的处理策略
10. redis 中的过期策略
maxmemory-policy 六种方式:默认的过期策略是 volatile-lru 。
**1、volatile-lru:**只对设置了过期时间的key进行LRU(Least recently used,最近最少使用)
2、allkeys-lru : 删除lru算法的key
**3、volatile-random:**随机删除即将过期key
**4、allkeys-random:**随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
九、持久化—RDB
1. 什么是RDB
RDB:Redis Databases(默认)
Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以 Redis 提供了持久化功能!
指定的时间间隔内将内存中的数据集写入磁盘-快照(Snapshot),它恢复时是将快照文件直接读到内存里。
2. 工作原理
Redis会单独创建(fork)一个子进程来进行持久化,子进程会先将数据写入到一个RDB临时文件,完成对新RDB文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。整个过程中,主进程是不进行任何IO操作(非阻塞),能够更好的实现写时复制,这就确保了极高的性能。
3. 触发机制
1、执行 flushall 命令,也会触发我们的rdb规则!
2、退出redis,也会产生 rdb 文件!
3、save的规则满足的情况下,会自动触发rdb规则
4、使用 save 命令,会立刻对当前内存中的数据进行持久化 ,但是会阻塞即占用Redis的主进程不接受其他操作
5、使用bgsave,bgsave
是异步进行,进行持久化的时候,redis
还可以将继续响应客户端请求 。
bgsave和save对比
命令 | save | bgsave |
---|---|---|
IO类型 | 同步 | 异步 |
阻塞? | 是 | 是(阻塞发生在fock(),通常非常快) |
复杂度 | O(n) | O(n) |
优点 | 不会消耗额外的内存 | 不阻塞客户端命令 |
缺点 | 阻塞客户端命令 | 需要fock子进程,消耗内存 |
4. 优缺点
优点:
-
适合大规模的数据恢复
-
对数据的完整性要求不高
缺点:
-
需要一定的时间间隔进行操作,如果redis意外宕机了,这个最后一次修改的数据就没有了。
-
fork进程的时候,会占用一定的内存空间。
-
生产环境下要对dump.rdb文件进行备份。
十、持久化AOF
1. 什么是AOF
Append Only File将我们所有的写命令都记录下来,恢复的时候就把这个文件全部再执行一遍。
2. 工作原理
以日志的形式来记录每个写的操作,只许追加文件但不可以改写日志文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
当.aof文件被修改了,出错,redis是启动不起来的!redis给我们提供了一个工具redis-check-aof ``–``fix`` ``appendonly``.aof``,来修复``.``aof``文件。
3. 同步机制
优点
-
appendfsync always``:
每一次修改都会同步,文件的完整性会更加好。 -
appendfsync everysec``:
每秒同步一次,可能会丢失一秒的数据,因为这一秒的修改宕机了,则该s的操作可能是错误的。 -
appendfsync no``:
从不同步,效率最高。
缺点
-
相对于数据文件来说,aof远远大于rdb,修复速度比rdb慢!
-
Aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化。
4. RDB和AOP选择
4.1 RDB 和 AOF 对比
RDB | AOF | |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
4.2 如何选择使用哪种持久化方式?
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。
十一、Redis发布与订阅
1. 概念
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。微信、微博、关注系统!Redis 客户端可以订阅任意数量的频道。订阅/发布消息图:
第一个:消息发送者, 第二个:频道 第三个:消息订阅者!
2. 命令
命令 | 描述 |
---|---|
psubscribe pattern [pattern…] | 订阅一个或多个符合给定模式的频道。 |
punsubscribe pattern [pattern…] | 退订一个或多个符合给定模式的频道。 |
pubsub subcommand [argument[argument]] | 查看订阅与发布系统状态。 |
publish channel message | 向指定频道发布消息 |
subscribe channel [channel…] | 订阅给定的一个或多个频道。 |
subscribe channel [channel…] | 退订一个或多个频道 |
------------订阅端----------------
127.0.0.1:6379> SUBSCRIBE sakura # 订阅sakura频道
Reading messages... (press Ctrl-C to quit) # 等待接收消息
1) "subscribe" # 订阅成功的消息
2) "sakura"
3) (integer) 1
1) "message" # 接收到来自sakura频道的消息 "hello world"
2) "sakura"
3) "hello world"
1) "message" # 接收到来自sakura频道的消息
"hello i am sakura"
2) "sakura"
3) "hello i am sakura"
--------------消息发布端-------------------
127.0.0.1:6379> PUBLISH sakura "hello world" # 发布消息到sakura频道
(integer) 1
127.0.0.1:6379> PUBLISH sakura "hello i am sakura" # 发布消息
(integer) 1
3. 原理
微信:
通过 SUBSCRIBE 命令订阅某频道后,redis-server 里维护了一个字典,字典的键就是一个个频道!,而字典的值则是一个链表,链表中保存了所有订阅这个 channel 的客户端。SUBSCRIBE 命令的关键,就是将客户端添加到给定 channel 的订阅链表中。
通过 PUBLISH 命令向订阅者发送消息,redis-server 会使用给定的频道作为键,在它所维护的 channel字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。
3.1 缺点
订阅方读取消息的速度却不够快的话,那么不断积压的消息会使redis输出缓冲区的体积变得越来越大,这可能使得redis本身的速度变慢,甚至直接崩溃。
订阅方断线,那么他将会丢失所有在短线期间发布者发布的消息。
4. 使用场景:
1、实时消息系统!用户注册到网站,相当于订阅了该网站,则网站可以发布消息给消息订阅者。
2、事实聊天!(频道当做聊天室,将信息回显给所有人即可!)
3、公众号订阅,微博关注
稍微复杂的场景我们就会使用 消息中间件 MQ ()
十二、Redis主从复制
1. 概念
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点 (master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。 默认情况下,每台Redis服务器都是主节点;
2. 主从复制的作用:
1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2、服务冗余:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;
3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务
4、高可用性:主从复制还是哨兵和集群能够实施的基础。
3. 使用规则
-
主机可读可写但是多用于写,从机只能读,不能写。
-
当主机断电宕机后,默认情况下从机的角色不会发生变化 ,集群中只是失去了写操作,当主机恢复以后,又会连接上从机恢复原状(如果有哨兵,则会确定新的master或手动设置
SLAVEOF no one
)。 -
当从机断电宕机后,使用配置文件配置的从机可以获取到主机的所有数据(同步原理)。
4. 同步/复制原理:
Slave 启动成功连接到 master 后会发送一个sync同步命令 ,Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。
**全量复制:**而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
**增量复制:**Master 继续将新的所有收集到的修改命令依次传给slave,完成同步
但是只要是重新连接master,一次完全同步(全量复制)将被自动执行! 我们的数据一定可以在从机中看到!
十三、哨兵模式(高频面试)
1. 概念
master宕机:自动选取master
更多信息参考博客:https://www.jianshu.com/p/06ab9daf921d
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令(心跳),等待Redis服务器响应,从而监控运行的多个Redis实例。
2. 单机单个哨兵
哨兵的作用:
-
通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
-
当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
3. 多哨兵模式
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。
切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。
4. 哨兵模式优缺点
优点:
-
主从可以切换,故障转移,系统的可用性更好
-
哨兵模式是主从模式的升级,手动到自动,更加健壮
缺点:
-
Redis不好在线扩容,集群容量一旦达到上限,在线扩容就十分麻烦
-
实现哨兵模式的配置麻烦,里面有很多配置项
十四、缓存穿透与雪崩
1. 缓存穿透(大面积查不到)
1.1 概念
在默认情况下,用户请求数据时,会先在缓存(Redis)中查找,若没找到即缓存未命中,再在数据库中进行查找,数量少可能问题不大,可是一旦大量的请求数据(例如秒杀场景)缓存都没有命中的话,就会全部转移到数据库上,造成数据库极大的压力,就有可能导致数据库崩溃。网络安全中也有人恶意使用这种手段进行攻击被称为洪水攻击。
1.2 解决方案
布隆过滤器(研究!)
对所有可能查询的参数以Hash的形式存储,以便快速确定是否存在这个值,在控制层先进行拦截校验,校验不通过直接打回,减轻了存储系统的压力。
![image-20210331103235513](https://i.loli.net/2021/03/31/RctXqN5vJnM8DSx.png)
缓存空对象
一次请求若在缓存和数据库中都没找到,就在缓存中方一个空对象用于处理后续这个请求,该空对象需要设置过期时间,否则会大量消耗存储资源。
缺点:存在缓存层和存储层的数据会有一段时间窗口的不一致。
![image-20210331103241210](https://i.loli.net/2021/03/31/WCyNAqnKRXU8bia.png)
2. 缓存击穿(同一时刻某一点的量太大且正当缓存过期)
2.1 概念
相较于缓存穿透,缓存击穿的目的性更强,一个存在的key,在缓存过期的一刻(一点),同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。
比如热搜排行上,一个热点新闻被同时大量访问就可能导致缓存击穿。
2.2 解决方案
1.设置热点数据永不过期
这样就不会出现热点数据过期的情况,但是当Redis内存空间满的时候也会清理部分数据,而且此种方案会占用空间,一旦热点数据多了起来,就会占用部分空间。
2.加互斥锁(分布式锁)
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问数据库,从而保护数据库。这样对锁的要求就十分高。
3. 缓存雪崩(某一时刻,缓存集中过期失效,Redis宕机)
3.1 概念
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
3.2 解决方案
1. redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群
2.限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
3.数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
style=“zoom:150%;” />
2. 缓存击穿(同一时刻某一点的量太大且正当缓存过期)
2.1 概念
相较于缓存穿透,缓存击穿的目的性更强,一个存在的key,在缓存过期的一刻(一点),同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。
比如热搜排行上,一个热点新闻被同时大量访问就可能导致缓存击穿。
2.2 解决方案
1.设置热点数据永不过期
这样就不会出现热点数据过期的情况,但是当Redis内存空间满的时候也会清理部分数据,而且此种方案会占用空间,一旦热点数据多了起来,就会占用部分空间。
2.加互斥锁(分布式锁)
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问数据库,从而保护数据库。这样对锁的要求就十分高。
3. 缓存雪崩(某一时刻,缓存集中过期失效,Redis宕机)
3.1 概念
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
[外链图片转存中…(img-5CQl34Te-1617158229811)]
3.2 解决方案
1. redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群
2.限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
3.数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。