学习中!!!!!
Redis基础
入门概述
- Redis之父:安特雷兹(antirez)
- Redis能干啥:
- 分布式缓存,可以挡在类似mysql这类数据库的前面,为其分担查询
- 内存存储加持久化(RDB+AOF),内存加快访问速度,持久化防止Redis突然死机时,数据丢失。
- 高可用架构搭配(单机、主从、哨兵、集群)
- 缓存穿透、击穿、雪崩
- 分布式锁
- 队列
- 进行排行榜、点赞功能等
- 总体功能概述图:
- 优势:
- 性能极高,Redis能读的速度是110000次/秒,写的速度是81000次/秒
- Redis数据类型丰富,不仅支持简单的key-value类型的数据,同时提供了list、set、zset、hash等数据结构的存储
- Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启Redis时,可以再次加载使用
- Redis支持数据的备份。master-slave模式的数据备份
- 总结图:
- 下载:
安装(Linux版)
-
检查是否安装gcc :
gcc -v
,如果没有gcc则使用命令yum -y install gcc-c++
-
因为redis的底层是使用c来编写的,gcc是一个c语言的编译工具
-
去官网下载redis,并放到Linux中
-
命令操作:::
mv redis-7.0.9.tar.gz /opt/ cd /opt tar -zxvf redis-7.0.9.tar.gz cd redis-7.0.9 //用来编译redis中的Makefile文件 make && make install //完成后默认放到/usr/local/bin路径下 cd /usr/local/bin/ //修改/opt/redis...下的redis.config,最好备份一份 daemonize yes protected-mode no #bind 127.0.0.1 -::1 //注释掉 requirepass 111111 //设置密码 //在 /usr/local/bin/目录下,启用server并进入客户端 ./redis-server /opt/redis-7.0.9/redis.conf ./redis-cli -a 111111
-
文件含义
- redis-benchmark: 性能测试工具
- redis-check-aof: 修复有问题的AOF文件
- redis-check-rdb: 修复有问题的rdb文件
- redis-cli: 客户端,操作的入口
- redis-sentinel: redis集群使用
- redis-server: redis服务器启动命令
-
redis的6379:
Redis的默认端口是6379,是由手机键盘字母MERZ的位置决定的。MERZ在Antirez的朋友圈语言中是"愚蠢和傻B"的代名词,它源于意大利广告女郎Alessia Merz在电视节目上说了一堆愚蠢的话,redis之父对她有"特殊"印象,就给她弄成端口号了
十大数据类型
-
图解(上图中右下角部分)
-
十大类型所指的都是value的类型,因为redis是key - value形式的,key一直都是string类型
-
十大类型:
- redis字符串(String)
- redis列表(List)
- redis哈希表(Hash)
- redis集合(Set)
- redis有序集合(ZSet)
- redis地理空间(GEO)
- redis基数统计(HyperLogLog)
- redis位图(bitmap)
- redis位域(bitfield)
- redis流(Stream)
-
redis命令查询地址
-
key的部分操作:
- del key 删除指定的key
- dump key 获得指定key序列化的值
- exists key 查看指定key是否存在
- expire key xxx 给指定key设置过期时间单位为秒
- expireat key xxx 给指定key设置过期时间,单位为时间戳
- pexpire key xxx 给指定key设置过期时间,单位为毫秒
- pexpireat key xxx 给指定key设置过期时间,单位为毫秒时间戳
- keys * 获取所有key
- move key x 将指定key移动到x号数据库中
- persist key 取出key的过期时间,保持为永久
- pttl key 获取key的过期时间,单位是毫秒
- ttl key 获取key的过期时间,单位是秒
- randomkey 随机获得当前数据库中的一个key
- rename key newkey 修改key的名字(如果newkey存在,会覆盖掉newkey)
- renamenx key newkey 当newkey不存在时,将key改为newkey
- type key 获取指定key的类型
- unlink key 非阻塞删除,先将key从keyspace中删除,真正的删除异步执行
- dbsize 查看当前数据库key的数量
- flushdb 清空当前的数据库key
- flushall 清空所有的数据库key
-
String类型
- set key value 设置key的值为value
- get key 获取指定的key值
- getrange key s e 获取从s到e的字串(下标从零开始)
- getset key value 获取指定key的值,并付给其一个新value
- mget key1 key2 … 获取指定的一个或多个key
- setex key xx value 将key的值设置为value并设置过期时间为xx
- setnx key value 当key不存在的时候才会设置key的值(存在key则不会成功)
- strlen key 获取key的值的长度
- mset key value key2 value2 … 设置一个或多个key , value
- msetnx key value key2 value2 … 设置一个或多个key value 若有已经存在的key则不会成功
- psetex key xx value 类似setex 设置的时间为毫秒
- incr key 将key的数字值递增1(必须是整数)
- incrby key xx 将key的数字递增 xx (必须是整数)
- incrbyfloat key xxx (可以是整数或浮点数)
- decr key (必须是整数)
- decrby key xxx (必须是整数)
- append key value 将value追加到key的末尾,如果不存在该key则创建新的key
-
List类型
- lpush key value1 value2 创建(插入)一个或多个value到指定得key中
- lpushx key value1 value2 插入 一个或多个value到指定得key中,key不存在不会插入
- rpush … rpushx … (List是一个双向链表,lpush添加在列表的头部,rpush添加在列表的尾部 / 或链表的左侧或右侧)
- lpop key 将列表头部的第一个值弹出
- blpop key1 key2 timeout 弹出并获取列表的第一个元素,列表中如果没有元素会发生阻塞,直到超时或发现可弹出的元素
- rpop … brpop … 与上述类似
- lindex key index 获取链表的第 index个元素, index从0、从列表头开始
- llen key 获取key的元素个数,即长度
- lrange key start end 获取key的从start 到end下标对应的元素
- lset key index value 修改key的下标为index的值为value
- lrem key count value 删除key中count个值为value的值
- rpoplpush key1 key2 将key1的末尾值放到key2的头部
- ltrim key index index2 将key的值截取为 index 到 index2
-
Hash类型
- hdel key field1 field2 … 删除一个或多个哈希字段
- hexists key field 查看key中的某个字段是否存在
- hget key field 获取存储在哈希表中指定字段的值
- hgetall key 获取指定哈希的所有字段和值
- hincrby key field n 为哈希表中的指定字段的值递增n
- hincrbyfloat key field n …递增的为浮点数
- hkeys key 获取哈希表中的所有字段
- hlen key 获取哈希表中的字段数量即长度
- hmget key field field2 … 获取多个哈希表中指定字段的value
- hmset key field value 将key中的指定字段设置值为value,如果没有该字段会新建,有该字段则覆盖
- hset key field value 。。这个现在好像也可也设置多个值
- hsetnx key field value 区别是如果key已经存在不会覆盖值
- hvals key 获取key中的所有值
-
Set类型
- sadd key member1 member2… 向集合中添加一个或多个成员
- scard key 统计指定集合的成员数
- sdiff key1 key2… 获取所有集合的差集,只有一个key相当于获取所有的
- sdiffstore key3 key1 key2… 将key1,key2等所有的差集存储在key3中(如果key3已存在,则会清空其原来的值,重新赋值)
- sinter key1 key2 … 同上,获取的为交集
- sinterstore key3 key1 key2 … 同上 ,获取的为交集
- sismember key1 member 查看 member是不是key1的成员
- smembers key 返回集合中的所有成员
- smove key1 key2 member 将key1的member 移动到key2中
- spop key1 移除并返回集合中的一个随机元素
- srandmember key count 返回集合中一个或多个随机数
- srem key member1 member2 … 移除集合的一个或多个成员
- sunion key1 key2 … 同上 ,获取为并集
- sunionstore key3 key1 key2 … 同上,获取为并集
-
Zset类型
Zset相当于在Set的基础上加上了一个Score来进行排序
- zadd key score1 number1 score2 number2 向有序集合添加一个或多个成员,或者更新已存在的成员分数
- zcard key 获取有序集合的成员数
- zcount key min max 计算在有序集合中指定区间分数的成员数
- zincrby key increment number 有序集合中对指定成员的分数加上增量 Increment
- zinterstore key3 numkeys k1 k2 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合key3中
- zrange key start stop 通过索引获取指定区间内的成员数量
- zrank key member 返回有序集合中指定成员的索引
- zrem key member member2… 移除一个或多个成员
- zscore key member 返回有序集中,成员的分数值
- zrevrank key member 返回有序集合中指定成员的排名
持久化
-
介绍图
-
RDB(Redis DataBase)
- RDB持久化以指定的时间间隔执行数据集的时间点快照。
- RDB可以将指定时间间隔内的内存中的数据集快照写入磁盘中,即SNAPSHOT内存快照,当Redis恢复运行时,再将快照文件直接读到内存中。因为Redis的数据都在内存中,所以保存备份时执行的是全量快照,就是把内存中的所有数据都记录到磁盘中。保存的文件默认为dump.rdb文件。
- 想要自动触发快照操作可以在Redis.conf文件中进行配置同时生成的dump.db的位置以及文件名,也可以在配置文件中进行配置。
- 触发快照保存的操作条件为在指定的时间内,至少对key进行n次操作,就会保存快照。
- 将生成的dump.db文件放到redis的安装目录下并启动服务就可以恢复备份。(最好不要将备份文件和redis放到同一台机器,要分开存放,防止redis所在的物理机损坏后备份文件也挂了)
- 同时可以手动使用命令来进行保存操作,命令为
save
和bgsave
- 在主程序中执行save命令,会阻塞当前的Redis服务器,直到持久化的工作完成。在这期间Redis不能处理其他的命令。线上是禁止使用的。
- 使用bgsave命令,Redis会在后台异步进行一个快照操作,不会阻塞,快照的同时还会响应客户端请求,该操作会fork一个子进程复制持久化过程。在这个过程中,允许主进程同时可以修改数据。
- 可以使用lastsave命令来获取最后一次成功执行快照的时间。
- 适合大规模的数据恢复、按照业务定时备份、对数据完整性和一致性要求不高、RDB文件 在内存中加载的速度比AOF快得多。
- 但是因为在一定间隔时间做一次备份,如果redis意外的down掉,就会丢失从当前至最近一次的快照期间的数据,快照之间的数据会丢失。内存数据的全量同步,如果数据量太大会导致I/O严重影响服务器的性能。RDB依赖于主进程的fork,在更大的数据集中,这可能会导致服务请求的瞬间延迟。fork的时候内存中的数据也被克隆了一份,大致2倍的膨胀,需要考虑。
-
AOF(Append Only File)
- AOF 是以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读指令不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,即当Redis重启的时候就会根据日志文件的内容将写指令从头到尾执行一次。
- 默认情况下,Redis并没有开启AOF,开启AOF需要配置文件中修改
appendonly yes
. - AOF 保存的文件名为 appendonly.aof
- 持久化写入流程:客户端Client先接收多个请求命令,命令到达RedisServer后。先将命令放入AOF缓存中进行保存,这时实际上是内存中的一片区域,存在的目的是当这些命令达到一定量的时候,统一写入磁盘,避免频繁的磁盘IO操作。AOF根据缓冲区同步文件的三种写回策略,将命令写入磁盘上的AOF文件。当AOF内容越来越多,会根据规则进行命令合并,从而压缩AOF文件。在RedisServer服务器重启的时候会从AOF文件中载入数据。
- 三种写回策略:(1)Always , 同步写回,每个写命令执行完后,立刻同步地将日志写回磁盘(可靠性高,数据基本不丢失但是性能影响较大)。 (2)everysec , 每秒写回,每个命令执行完后,先把日志写到AOF的缓存区中,每隔一秒将其从缓存区中写到磁盘(性能适中,宕机时丢失一秒内的数据)。(3) no,操作系统控制的写回,每个写命令执行完后,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘(性能好,宕机时丢失数据较多)。
- 在配置文件中开启AOF
默认写回策略为 everysec , 可在配置文件中进行修改。
AOF文件保存路径为:dir + appenddirname
Redis7.0将单个的AOF文件拆分成了多个AOF文件。分为三种类型
(1) BASE: 表示基础AOF文件,一般由子进程通过重写产生,文件最多只有一个。
(2)INCR:表示增量AOF,一般会在AOFRW(写回)
开始时执行时被创建,文件可能存在多个。
(3)HISTORY: 表示历史AOF,由BASE 和 INCR AOF变化而来,每次AOFRW成功完成时,本次AOFRW之前对应的BASE和INCR AOF 都将变为 HISTORY,HISTORY类型的AOF会被Redis自动删除。为了管理这些AOF文件,引入了一个**manifest(清单)**文件来跟踪、管理AOF。为了方便AOF备份和拷贝,将所有的AOF文件和manifest文件放入一个单独的文件目录中,目录名通过appenddirname配置。 - 优势:可以更好的保护数据不丢失、性能高、可以做紧急恢复。
- 劣势:相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb。aof的运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同。
- AOF重写机制:
(1)是什么:启动AOF文件的内容压缩,只保留可以恢复数据的最小指令(如:set k1 123, set k1 234, set k1 345 ,会压缩为 set k1 345 进行存储)。
(2)触发机制:默认:在配置文件中配置,当两者同时满足时就会触发
自动触发: 达到配置文件要求触发
手动触发: 客户端向服务器发送bgrewriteaof命令
-
RDB + AOF 混合持久化
- 同时开启两种持久化方式,在这种情况下,当redis重启时会优先加载AOF文件来恢复原始数据,在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
- 开启混合方式设置:设置aof-use-rdb-preamble的值为 yes yes表示开启,设置为no表示禁用
- 推荐方式:RDB镜像做全量持久化,AOF做增量持久化。先使用RDB进行快照存储,然后使用AOF持久化记录所有的写操作,当重写策略满足或手动触发重写的时候,将最新的数据存储为新的RDB记录。这样的话,重启服务的时候会从RDB和AOF两部分恢复数据,既保证了数据完整性,有提高了恢复数据的性能。简单来说:混合持久化方式产生的文件一部分是RDB格式,一部分是AOF格式。
事务
- 是什么:可以一次执行多个命令,本质上是一组命令的集合。一个事务中的所有命令都会序列化,按顺序的串行化执行而不会被其他命令插入,不许加塞。
- 能干什么:一个队列中,一次性、顺序性、排他性的执行一系列命令。
- Redis事务:
(1)单独的隔离操作:Redis的事务仅仅是保证事务里的操作会被连续独占的执行。redis命令执行是单线程架构,在执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的。
(2)没有隔离级别的概念: 因为事务提交前任何指令都不会被实际执行,也就不存在“事务内的查询要看到事务里的更新,在事务外查询不能看到”这种问题了。
(3)不保证原子性: Redis的事务不保证原子性,也就是不保证所有指令同时成功或同时失败,只有决定是否开始执行全部指令的能力,没有执行到一般回滚的能力。
(4)排他性:Redis会保证一个事务内的命令依次执行,而不会被其它命令插入。 - 常用命令:
(1)正常执行:MULTI、EXEC
以MULTI开始一个事务,由EXEC命令触发事务。
(2)放弃事务:MULTI、DISCARD
(3)watch监控:watch监控指定的keywatch key
,watch命令是一种乐观锁的实现,Redis在修改的时候会检测数据是否被更改,如果更改了,则执行失败。
管道
- 问题由来:
Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。客户端向服务端发送命令分为四步(发送命令-命令排队-命令执行-返回结果)。如果同时需要执行大量命令,那么就要等待上一条命令应答后在执行。这中间不仅多了RTT(Round Time Trip数据包往返于客户、服务两端的时间),而且频繁调用系统IO,发送网络请求,同时需要Redis调用多次的read() 和 write()系统方法。系统方法会从用户态转移到内核态,会对进程上下文有较大的影响,性能不太好。 - 是什么: 为了解决上述问题,引入了管道的概念。Pipeline 是为了解决RTT往返时,仅仅是将命令打包一次性发送,对整个Redis的执行不造成其他任何影响。即批量命令变种优化措施,类似于Redis的原始的批命令(mget 、 mset)
- 命令示例:
cat xxx.txt | redis-cli -pipe 将xx.txt中的命令,通过pipe进行操作
- Pipeline与原生批量命令对比:
(1) 原生的批量命令是原子性的,Pipeline是非原子性的
(2) 原生的批量命令是一次只能执行一种命令,Pipeline支持批量执行不同命令
(3) 原生命令是服务端实现,而Pipeline需要服务端与客户端共同完成 - Pipeline与事务进行对比:
(1) 事物具有原子性、管道不具有原子性
(2)管道一次性将多条命令发送到服务器,事务是一条一条的发送,事务只有接收到exec后才会执行,管道不会。
(3)执行事务时,会阻塞其他命令的执行,执行管道中的命令不会。 - Pipeline注意事项:
(1) Pipeline缓冲的指令只是会依次执行,不保证原子性,如果执行中指令发生异常,将会继续执行后续的指令
(2) 使用Pipeline组装的命令个数不能太多,不然数据量过大客户端阻塞的时间可能过久,同时服务端此时也被迫恢复一个队列答复,占用内存。
复制
-
是什么:就说主从复制,以master为主,主要写操作。slave为辅,主要读操作。当master的数据变化的时候,自动将新的数据异步同步到其他的slave中。
-
能干嘛:读写分离、容灾恢复、数据备份、水平扩容、支持高并发
-
常用三招:一主二仆、薪火相传、反客为主。
-
复制原理和工作流程:
(1)slave启动,同步初请:slave启动成功连接到master后会发送一个sync命令,slave首次全新连接master,,一次完全同步(全量复制)将被自动执行,slave自身原有数据会被master数据覆盖清楚。
(2)首次连接,全量复制:master节点收到sync命令后会开始在后台保存快照(RDB的持久化,主从复制时,触发RDB),同时收集所有接收到的用于修改数据集命令缓存起来,master节点执行完RDB持久化后,将RDB快照文件和缓存命令发送给slave完成一次完全同步。slave服务在接收到数据库文件数据后,将其存盘并加载到内存中,从而完成复制初始化。
(3)心跳持续,保持通信:repl-ping-replica-period 10
发送包的周期默认为10秒,可以在配置文件中配置
(4)进入平稳,增量复制:master继续将新的所有收集到的修改命令自动一次传给slave,完成同步。
(5)从机下线,重连续传:master会检查backlog里面的offset,master和slave都会保存一个复制的offset还有一个masterId,offset是保存在backlog中的,master只会把已经复制的offset后面的数据复制给slave,类似断点续传。 -
复制的缺点
(1)复制延时、信号衰减:由于所有的写操作都是先在Master上操作,然后同步更新到slave上,所以从master同步到slave机器有一定的延迟。当系统很繁忙的时候,延迟问题会更加严重,slave机器数增加也会使这个问题严重。
(2)如果master挂了,默认情况下,不会在slave节点中自动重选一个master。每次都要人工如操作。
哨兵
-
是什么:吹哨人巡查监控后台master主机是否故障,如果故障了根据投票数自动将某一个从库转化为新主库,继续对外服务。俗称无人值守运维。
-
能干嘛:
(1)主从控制:监控主从redis库是否正常运行。
(2)消息通知:哨兵可以将故障转移的结果发送给客户端。
(3)故障转移:如果master异常,则会进行主从切换,将其中一个slave作为新的master。
(4)配置中心:客户端通过连接哨兵来获得当前Redis服务的主节点地址。 -
架构示例图:
-
哨兵运行流程和选举原理:
当一个主从配置的master失效后,sentinel可以选举出一个新的maset.用于自动接替原master的工作,主从配置中的其他redis服务器自动指向新的master同步数据。一般建议sentinel采取奇数台,防止某一台sentinel无法连接到Master导致误切换。
(1)三个哨兵、一主二从,正常运行中。。。。。。
(2)SDown主观下线(Subjectively Down):SDown(主观不可用)是单个sentinel自己主观上检测到的关于master的状态,从sentinel的角度来看,如果发送了PING心跳后,在一定的时间内没有收到合法的回复,就达到了SDown的条件。sentinel配置文件中的down-after-milliseconds
设置了判断主观下线的时间长度。
(3)ODown客观下线(Objectively Down): ODown需要一定数量的sentinel,多个哨兵达成一致意见才能认为一个Master客观上已经宕掉。
(4)选举出领导者哨兵:当主节点被判断客观下线后,各个哨兵进行协商,选举出一个领导者哨兵节点,由该节点进行failover(故障转移)
(5)选举的算法:监视该主节点的所有哨兵都有可能被选为领导者,选举使用的算法是Raft算法,Raft算法的基本思路是先到先得。即在一轮选举中,哨兵A向哨兵B发送成为领导者的申请,如果B没有同意过其他哨兵,就会同意A成为领导者。
(6)由领导者哨兵开始推动故障切换流程,选举出一个新的master.(6-1)新主登基:某个slave被选为master,选出master的规则:在剩余的slave节点健康的前提下 redi.conf文件中的优先级slave-priority或者replica-priority最高的从节点(数字越小优先级越高), 复制偏移位置offset最大的从节点,最小RunID的从节点 (6-2) 俯首称臣:执行slaceof no noe 命令,让选出来的从节点成为新的主节点。通过slaveof命令让其他节点成为其从节点。 哨兵领导者会对选举出的新master执行slaveof no noe 命令。将其升为主节点,之后向其他slave发送命令,让剩余的slave成为新的master节点的slave. (6-3)旧主拜服:之前下线的master设置为新选出来的master的从节点,当老的master上线后,会变为从节点。 哨兵领导者会让老master降级为slave并恢复正常工作。
-
使用建议:
(1)哨兵节点的数量应为多个,哨兵本身应该集群,保证高可用
(2)哨兵节点的数量应该是奇数
(3)各个哨兵节点的配置应该一致
(4)如果哨兵节点部署在Docker等容器里面,尤其要注意端口的正确映射
(5)哨兵集群 + 主从复制, 并不能保证数据零丢失。