Redis应用学习笔记

以我来看,这篇文章略显粗糙,毕竟所花时间不多,作为自己的一个入门吧,也可能门都没入?

Redis是一个非常强大的数据库,我曾经看过他的源码,惊叹于他底层的单调而上层却能那么变化多端,这是应了那句“道生一,一生二,二生三,三生万物”(当然还是比不上我最热爱的 操作系统的,大喊一句“Linux yyds!”)。

如标题所示,本篇博文主要是偏于应用的,当然,如果你想研究源码,我这里也有。

下面来简单介绍一下Redis。众所周知,Redis是用C语言写的一个单线程数据库,那这个单线程是怎么处理请求的呢?Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程。其中执行命令阶段,由于Redis是单线程来处理命令的,所有每一条到达服务端的命令不会立刻执行,所有的命令都会进入一个队列中,然后逐个被执行。并且多个客户端发送的命令的执行顺序是不确定的。但是可以确定的是不会有两条命令被同时执行,不会产生并发问题

为什么说Redis能够被快速执行?

  • 绝大部分请求是纯粹的内存操作(非常快速)
  • 采用单线程,避免了不必要的上下文切换和竞争条件
  • 非阻塞IO - IO多路复用,Redis采用epoll做为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接,读写,关闭都转换为了时间,不在I/O上浪费过多的时间

Redis

前置知识

Redis是什么?

Redis ( Remote Dictionary Server ),即远程字典服务!

是一个开源的使用ANSl C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。免费和开源!是当下最热门的 NoSQL技术之一!也被人们称之为结构化数据库!

redis的安装

$ wget https://download.redis.io/releases/redis-6.2.6.tar.gz
$ tar xzf redis-6.2.6.tar.gz
$ cd redis-6.2.6
$ make

打开客户端

src/redis-cli

打开服务端

src/redis-server

测试连接

ping

性能测试

src/redis-benchmark

基本命令

select 3 #切换数据库,默认使用的是第0个
dbsize	# 查看当前db大小
keys *	# 查看数据库所有的key
flushdb	# 清空数据库

为什么redis端口是6379?

意大利歌女名字对应的电话号码(粉丝效应)

明白redis是很快的,redis是基于内存操作,CPU不是redis性能瓶颈,redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程,就使用单线程了。

redis是用C语言写的,官方提供的数据为100000+的QPS,完全不比同样使用key-value的memcache差

基本命令

# 生成key-value数据
set mykey myvalue
# 根据key获得value
get mykey
# 是否存在某个key
exists mykey
# 从某个数据库移除某个键
move mykey 1
# 设置过期时间
expire mykey second
# 查看某个mykey的剩余时间
ttl mykey
# 判断当前key的数据类型
type mykey
数据类型

string

实现:字符串

# 给某个key添加字符串
append mykey "hello"
# 获得key的长度
strlen mykey
# 自增
incr mykey
# 自减
desc mykey
# 增加一定数量
incrby mykey 10
# 减少一定数量
desc mykey 10
# 获得区间值,两边均为闭区间
getrange mykey startindex endindex
# 截取字符串并重新赋值
setrange mykey startindex endindex
# 在定义数据的时候设置过期时间
setex mykey 30 "hello"
# 不存在时设置(如果已经存在该key则会报错)
setnx mykey "hello"
# 批量设置值
mset k1 v1 k2 v2
# 批量获取值
gset k1 k2
# 批量不存在时设置(如果已经存在该key则会报错),原子操作,要么同时成功,要么失败
msetnx k1 v1 k2 v2
# 先get再set,如果不存在返回nil
getset db redis

使用场景

  • 计时器
  • 统计多单位的数量

list

实现:双向链表和跳跃表

# 插入一个值,默认放在左边
lpush mylist one
lpush mylist two
lpush mylist three
# 插入一个值,放在右边
rpush mylist four
# 获取list中的范围
lrange mylist 0 -1
# 移除某个元素
lpop # 移除左边的元素
rpop # 移除右边的元素
# 通过下标获得值,默认从左边开始
lindex mylist 0
# 返回列表的长度
llen mylist
# 移除n个某个value
lrem mylist n value
# 截取某些元素并重新赋值
ltrim mylist 1 2	# 截取第一个元素到第二个元素
# 移除右边最后一个元素并将其插入到最左边
rpoplpush
# 将list中固定下标的值替换成另一个值,相当于是更新操作
lset mylist 1 hello
# 把value插入到某个元素之前
linsert mylist before "world" "value"
# 把value插入到某个元素之后
linsert mylist after "world" "value"

set

实现:整数集合和哈希表

# 添加元素
sadd myset "hello"
# 查看所有值
smembers myset
# 是否包含某元素
sismember myset "hello"
# 获取元素个数
scard myset
# 移除某个值
srem myset "hello"
# 随机筛选n个成员,默认是一个
srandmember myset n
# 随机移除元素
spop myset
# 移动值到另一个set中
smove myset1 myset2 myvalue
# 求差集
sdiff myset1 myset2
# 求交集
sinter myset1 myset2
# 求并集
sunion myset1 myset2

hashtable

实现:压缩列表和哈希表

# 添加元素
hset mymap k1 v1
# 获得元素
hset mymap k1
# 批量添加
hset mymap k1 v1 k2 v2
# 批量获取
hget mymap k1 k2
# 获得所有的key-value
hgetall mymap
# 删除元素
hdel mymap k1
# 获得元素数量
hlen mymap
# 判断是否存在
hexists mymap k1
#获取所有的key
hkeys mymap
# 获取所有的value
hvals mymap
# val = val + n
hincrby mymap k1 n
# val = val - n 
hdescby mymap k1 n
# 添加元素,如果重复则报错
hsetnx mymap k1 v1

适用常用:适合用户信息经常变动的信息存储

zset

实现:压缩列表和跳跃表

# 添加元素,其中要添加一个数值,用来排序
zadd myset 1 one
# 获取某个范围的值
zrange myset 0 -1
# 排序
zrangebyscore myset -inf +inf	# 负无穷到正无穷
zrangebyscore myset -inf 2300	# 负无穷到2500
# 移除某个元素
zrem myset k1
# 查看集合的数量
zcard myset
# 从大到小进行排序
zrevrange myset 0 -1
# 获取指定区间的成员数量
zcount myset n m
Redis事务

Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程的中,会按照顺序执行!一次性、顺序性、排他性!执行一些列的命令

  • Redis事务没有没有隔离级别的概念
  • 所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行
  • ExecRedis单条命令式保存原子性的,但是事务不保证原子性

Redis的事务

# 开启事务
multi
# 退出事务
exec
# 取消事务
discard

编译型异常

代码有问题,事务中的命令都不会被执行

运行时异常IO

如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!

监控

悲观锁:很悲观,认为什么时候都会出问题,无论做什么都会加锁

乐观锁:很乐观,认为什么时候都不会出问题,无论做什么都会加锁,更新数据的时候去判断一下,在此期间是否有人修改过这个数据,

  • 获取version
  • 更新的时候比较version
# 监视某个元素
watch money
# 开启事务
multi
incrby money 20
descby out 20
# 退出事务
exec
# 取消监视
unwatch
Redis.conf
bind 127.0.0.1 # 访问数据库的可用地址
protected-mode yes # 保护模式
port xxxx# 开启端口

通用配置

daemonize yes # 守护进程默认开启
pidfile # 如果以后台方式运行,指定pid运行文件
loglevel notice # 日志信息
logfile # 日志的文件位置名
databases # 数据库的数量
always-show-logo yes # 总是显示logo

快照

# 如果900s内,如果至少有一个1 key进行了修改,我们及进行持久化操作
save 900 1
# 如果300s内,如果至少10 key进行了修改,我们及进行持久化操作
save 300 10
# 如果60s内,如果至少10000 key进行了修改,我们及进行持久化操作
save 60 1000

stop-writes-on-bgsave-error yes # 如果持久化出错,是否继续工作

rdbcompression yes # 是否压缩rdb文件,需要消耗一些CPU资源

安全

# 获取redis的密码
config get requirepass
# 设置redis的密码
config set requirepass "xxx"
# 使用密码进行登陆
auth xx

限制

# 设置能连接上redis的最大客户端数量
maxclients xxx
# 数据删除策略
持久化

Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以Redis提供了持久化功能

RDB

redis database

Redis会单独创建 ( fork )一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

save

优点:

  • 适合大规模的数据恢复
  • 对数据的完整性要不高

缺点:

  • 需要一定的时间间隔进程操作!如果redis意外宕机了,这个最后一次修改数据就没有的了
  • fork进程的时候,会占用一定的内容空间
AOF

以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据田志文件的内容将写指令从前到后执行一次以完成数据的恢复工作

优点︰

  • 每一次修改都同步,文件的完整会更加好
  • 每秒同步一次,可能会丢失一秒的数据
  • 从不同步,效率最高的

缺点∶

  • 相对于数据文件来说,aof远远大于rdb,修复的速度也比 rdb慢!
  • aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化
  • aof默认就是文件的无限追加,文件会越来越大

如果aof文件大于64m,太大了! fork一个新的进程来将我们的文件进行重写

Redis订阅发布

Redis发布订阅(pub/sub)是一种消息通信模式︰发送者(pub)发送消息,订阅者(sub)接收消息。

订阅发布消息图:消息发送者、消息接收者、频道

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-68hpXTTs-1637155483492)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20211117190653564.png)]

# 订阅一个或多个符合给定模式的频道。
psubscribe pattern [pattern...]
# 查看订阅与发布系统状态。
pubsub subcommand [argument]
# 将信息发送到指定的频道。
publish channel message
# 退订所有给定模式的频道。
punsubscribe [pattern...]
# 订阅给定的一一个或多个频道的信息。
subscribe channel [channel...]
# 指退订给定的频道。
unsubcribe [channel...] 

使用场景

  • 实时消息系统
  • 实时聊天
  • 订阅

更复杂的时候使用消息中间件

主从复制

主从复制,读写分离! 80% 的情况下都是在进行读操作!减缓服务器的压力!架构中经常使用!一主二从!

主从复制,是指将一台Redis服务器的数据 ,复制到其他的Redis服务器。前者称为主节点(master/leader) ,后者称为从节点
(slave/follower) ;数据的复制是单向的,只能由主节点到从节点。Master以写为主, Slave以读为主。

默认情况下,每台Redis服务器都是主节点;且-一个主节点可以有多个从节点(或没有从节点),但-一个从节点只能有一个主节点。

主从复制的作用主要包括:

  • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点) , 分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  • 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
# 打印主从复制信息
info replication

复制3个配置文件,然后修改对应的信息

  • 端口
  • pid名字
  • log文件名字
  • dump.rdb 名字

默认情况下,每台redis都是master,如果要实现主从复制,则只配置从机

# 短暂性的配置从机
slaveof host port
# 永久性的配置从机

主机写,从机不能写

原理

  • Slave启动成功连接到master后会发送一个sync同步命令
  • Master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后, master将传送
  • 整个数据文件到slave ,并完成一次完全同步。
  • 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
  • 增量复制: Master继续将新的所有收集到的修改命令依次传给slave ,完成同步
  • 但是只要是重新连接master , 一次完全同步(全量复制)将被自动执行

如果主机断开了连接,我们可以使用SLAVEOF no one让自己变成主机!其他的节点就可以手动连接到最新的这个主节点(手
动) !如果这个时候老大修复了, 那就重新连接!

哨兵模式

具体配置可见sentinel.conf文件

sentinel monitor myredis host port 1 

后面的这个数字1 , 代表主机挂了, slave投票看让谁接替成为主机,票数最多的,就会成为主机!

启动哨兵

redis-sentinel sentinel.conf

优点:

  • 哨兵集群,基于主从复制模式,所有的主从配置优点,它全有
  • 主从可以切换 ,故障可以转移,系统的可用性就会更好
  • 哨兵模式就是主从模式的升级,手动到自动,更加健壮!

缺点:

  • Redis不好啊在线扩容的,集群容量一-旦到达上限,在线扩容就十分麻烦!
  • 实现哨兵模式的配置其实是很麻烦的,里面有很多选择!
缓存穿透和雪崩

Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一-些问题。 其中,最要害的
问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。

缓存穿透

缓存穿透的概念很简单,用户想要查询一一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中(秒杀! ) , 于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力这时候就相当于出现 了缓存穿透。

解决方案:

  • 布隆过滤器
  • 缓存层设为空

布隆过滤器:布隆过滤器是一种数据结构|, 对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力;

但是这种方法会存在两个问题:

  • 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
  • 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一-段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
缓存击穿

这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一一个点进行访问, 当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数
据,并且回写缓存,会导使数据库瞬间压力过大。

解决方案:

  • 设置热点数据永不过期
  • 加互斥锁

穿透和击穿的区别:穿透是查不到,击穿的量太大,缓存过期

缓存雪崩

缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis 宕机!

产生雪崩的原因之一, 比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨- -点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言 ,就会产生周期性的压力波峰。 于是所有的请求都会达到存储层,存储层的调用量会暴增 ,造成存储层也会挂掉的情况。

其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩, -定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。

解决方案:

  • redis高可用:这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之 后其他的还可以继续工作,其实就是搭建的集群。
  • 限流降级:
  • 这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  • 数据预热:数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key ,设置不同的过期时间,让缓存失效的时间点尽量均匀。

Guess you like

Origin blog.csdn.net/qq_48322523/article/details/121388305