10 Redis
1 Redis介绍
1.1 Redis是什么?
● Redis(Remote Dictionary Server),即远程字典服务。key—value存储系统,是跨平台的非关系型数据库。
● Redis是一个开源的使用ANSI C语言编写、遵循BSD协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key—Value)存储数据库,并提供多种语言的API。
● Redis通常称为数据结构服务器,因为值(value)可以是如下八种数据类型:
- 字符串(String)、哈希(Hash)、列表(list)、集合(set)、有序集合(sorted set)、位图(Bitmaps)、HyperLogLog、地理空间(Geospatial)。
1.2 Redis的特点
① 速度快
② 基于键值对数据模型
③ 功能丰富
④ 简单稳定
⑤ 客户端语言多
⑥ 持久化
⑦ 高可用
1.3 Redis的使用场景
● 当作高性能的缓存
● 分布式锁
● 计数器
● 最新列表
● 排行榜
2 Redis的安装部署及简单的使用
2.1 安装部署
2.2 Redis的简单使用
2.2.1 Redis服务启动
说明 | 指令 |
启动redis服务(挂在前台) | bin/redis-server ./redis.conf |
改redis.conf文件(挂在后台) | daemonize yes |
用端口,查看redis服务 | netstat -nltp | grep 6379 |
ps命令,查看redis程序 | ps -ef | grep redis |
2.2.2 Redis服务停止
说明 | 指令 |
查看redis端口号 | nerstat -nltp | grep 6379 |
杀掉redis进程 | kill -9 端口号 |
客户端关闭 | shutdown |
2.2.3 Redis的客户端连接
说明 | 指令 |
连接redis服务 | bin/redis-cli -p 6379 |
-p参数可以指定连接端口,不带-p默认连接6379 | |
该bind参数(绑定到hadoop102映射地址) | bin hadoop102 |
连接redis服务 | bin/redis-cli -h hadoop102 -p 6379 |
2.2.4 Redis的路径配置
● 在redis根目录下,创建一个文件夹名为dataDir,并在配置文件中配置
[atguigu@hadoop102 redis]$ vim redis.conf
……
dir /opt/module/redis/dataDir
2.2.5 Redis的基本操作
在配置文件redis.conf中:databases 16
● 参数是16,当前Redis示例有16个数据库,
● dbid范围 [0,database-1]
1) 数据库相关操作
命令 | 说明 | 示例 |
select <dbid> | 切换数据库 | select 7 |
dbsize | 查看数据库中数据个数 | dbsize |
flushdb | 清空当前库 | flushdb |
flushall | 通杀全部库 | flushall |
注意:
不同的命令采用Integers类型返回值含义不同
● dbsize命令,属于Integers类型返回值
● INCR命令采用整型回复作为返回值,并没有特殊意义,仅仅是INCR的UNIX时间的增加数值
● EXISTS命令,将通过返回值为1表示true,0表示false
2) key相关操作
● Redis中的数据存储方式:键值对(Key—Value)
● key—字符串类型 value—支持多种数据类型
创建键值对
hadoop102:6379> set zs 18
OK
hadoop102:6379> set ls 19
OK
hadoop102:6379> set ww 20
OK
相关操作
表达式 | 描述 | 示例 |
key pattern | 查询key,*是全部,?是任意一个字符 | keys * / key w* |
type key | 查看key对应值的类型 | type zs |
exists key | 指定的key是否存在,0不存在,1存在 | exists ls |
del key | 删除指定的key,成功返回1,不成功返回01 | del ww |
randomkey | 随机返回一个key | randomkey |
expire key second | 为键值设置过期时间,单位:秒 | expire zs 20 |
ttl key | 查看key还有多久过期,-1永不过期,-2已过期 | ttl zs |
rename key newkey | 重命名key,若newkey已存在,值将会被覆盖 | rename ww lw |
renamenx key newkey | newkey存在,才能执行成功 | renamenx zz xx |
3 Redis 的数据类型
3.1 字符串(String)
介绍
● String是Redis最基本的类型,一个key对应一个value。
● String类型是二进制安全的。Redis的String可以包含任何数据。比如jpg图片或者序列化的对象。
● String类型的值最大能存储512MB。
基本操作
表达式 | 描述 | 示例 |
set key value | 将字符串value关联到key | set k1 v1 |
EX | 设置键值对,过期时间20秒 | set k1 v1 EX 20 |
PX | 设置键值对,过期时间20000毫秒 | set k2 v2 PX 20 |
ptt | 查看毫秒级,过期剩余时间 | pttl k2 |
NX | 键不存在时,设置键值 | set k1 v1 NX |
XX | 键存在时,设置键值 | set k2 v2 XX |
setex key seconds value | 设置带有过期时间的键值对 | setex k4 20 v4 |
setnx key value | key不存在时,可设置value | setnx k1 v11 |
setrange key offset value | 用新值复写,用偏移量开始 | setrange k1 3 abcde |
注意:原字符长度不够时“填零占位”——>"\x00" | ||
mset [key value ... ] | 添加多个键值对 | mset k2 v2 k3 v3 |
mget key1 key2 ... | 同时获取多个键的值 | mget k1 k2 |
msetnx [key value ... ] | k1不存在,设置多对键值对,k1存在则失败 | msetnx k1 v1 k2 v1 k3 v3 |
get key | 返回key关联的字符串值 | get k1 |
getset key value | 对给定的key赋值,同时获取旧值 | getset k2 v22 |
getrange key start end | 返回子字符串,-1最后一个字符,-2倒数第2个 | getrange k4 0 -1 |
用””号将多个单词作为一个值存储 | ||
当指定的获取值域大于实际字符串的值域,超出的部分会自动忽略 | ||
incr key | 将key中存储的数字值+1 | incr k1 |
incrby key increment | 对key的值增加指定增量 | inctby k1 10 |
decr key | 将key中存储的数字值-1 | decr k2 |
decrby key decrement | 对key的值增加指定减量 | decrby k2 10 |
3.2 列表(list)
介绍
● Redis List是实现是基于:Linked lists。
● 双链表,通过push、pop操作从链表的头部或尾部添加删除元素。
● 一个列表最多可以包含232-1个元素(4294967295,每个表超过40亿个元素)。
● 链表头尾元素操作,效率高。 链表中间元素操作,效率低。
基本操作
表达式 | 描述 | 示例 |
lpush key value [value ... ] | 向表头插入元素 | lpush llist01 l1 l2 l3 |
lpushx key value | 当且仅当key存在时,向表头插入元素 | lpushx list01 l1 l2 |
rpush key value [value ... ] | 向表尾插入元素 | rpush list01 r1 r2 r3 |
rpushx key value [value ... ] | 当且仅当key存在时,向表尾插入元素 | rpushx list01 r1 r2 |
lpop key | 移除,并返回表头元素 | lpush list01 |
rpop key | 移除,并返回表尾元素 | rpop list01 |
lrange key start stop | 返回指定区间内的元素(0第一个,-1最后一个) | lrange list01 0 -1 |
ltrim key start stop | 对列表进行修剪(保留1到-1的value) | ltrim list01 1 -1 |
llen key | 返回列表的长度 | llen list01 |
lindex key index | 返回列表下标为index的元素 | lindex list01 5 |
insert key before|after pivot element | 将值插入指定位置 | LINSERT list01 before r1 0 |
rpoplpush source destination | 将尾部元素拿出,放头部 | rpoplpush list03 list03 | rpoplpush list02 list01 |
lrem key n x | 删除n个于x相等的元素 | lrem lisrt 2 r1 |
3.3 集合(set)
介绍
● Redis的的Set是String类型的无序集合。
● 集合成员是唯一的
● Redis对象的编码可以是inset或hashtable
● Redis中集合是通过哈希表实现的,所哟添加、删除、查找的时间复杂度都是O(1)。
● 集合中最大的成员数为232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
表达式 | 描述 | 示例 |
sadd key member [member ... ] | 将元素添加到集合key中 | sadd set01 s1 s2 s3 |
scard key | 返回集合key的基数(成员个数) | scard set01 |
smembers key | 返回集合key中所有成员 | smembers set01 |
sismember key member | 判断元素是否是集合中的元素 | sismember set s111 |
spop key [count] | 随机移除,并返回集合中一个元素 | spop set01 |
srandmember key [count] | 随机返回集合中一个元素,count可指定返回多个 | srandmember set01 3 |
srem key member [member ... ] | 移除集合中的元素 | srem set01 s1 s2 |
sinter key [key ... ] | 返回多个集合的交集 | sinter set01 set02 |
sinterstore destionation key [key ... ] | 将交集结果存储到新的集合 | sinterstore set_01_inter_02 set01 set02 |
sdiff key [key ... ] | 返回多个集合的差集 | sdiff set02 |
sdiffstore destionation key [key ... ] | 将差集结果存储到新的集合 | sdiffstore set_01_diff_02 set01 set02 |
sunion key [key ... ] | 返回多个集合的并集 | sunion set01 set02 |
sunionstore destionation key [key ... ] | 将并集结果存储到新的集合 | sunionstore set_01_union_02 set01 set02 |
3.4 有序集合(Sort set)
介绍
● Redis有序集合,String类型,且不允许重复的成员。
● 每个元素都会关联一个double类型的分数,通过分数进行排序。
● 有序集合的成员是唯一的,但分数(score)可以重复。
● 集合是通过哈希表实现的,所以添加、删除、查找的时间复杂度是O(1),集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
表达式 | 描述 | 示例 |
zadd key [nx|xx] [ch] [incr] scores member ... | 将元素+score值添加到有序集合 | zadd zset01 1 zhangsan 2 lisi |
zrange key start stop [withscores] | 依照下标,返回集合中,指定区域成员 | zrange zset01 1 3 withscores |
zrangebysocre key min max [withscores] | 依照分数score,返回结合中,指定区域成员 | zrangebyscore zset01 1 3 withscores |
zrevrange key start stop [withsocres] | 依照下标,递减返回集合成员 | zrange zset01 0 -1 withscores |
zrevrangebysocre key max min [withscores] | 依照分数socre,递减返回集合成员(小于3,大于0) | zrevrangebyscore zset01 (3 0 inf withscores |
zrank key member | 先按分数排序,返回成员排名(下标值) | zrank zset01 zhangsan |
zrevrank key member | 先按分数逆序排序,返回成员排名(下标值) | zrevrank key member |
zrem key member ... | 移除集合中成员 | zrem zset01 zhangsan lisi |
zremrangebyscore key start stop | 依照分数,返回区域成员 | zremrangebyscore zset01 1 3 |
zremrangebyrank key min max | 依照分数,返回区域成员 | zremrangebyscore zset01 1 3 |
zcard key | 返回集合基数(成员个数) | zcard zset01 |
zcount key min max | 依照分数score,返回指定范围内成员个数 | zcount zset01 1 3 |
zincrby key increment member | 将分数score增加increment | zincrby zset01 5 zhangsan |
zscore key member | 返回成员的分数score | zscore zset01 zhangsan |
3.5 哈希(Hash)
● Redis hash是一个string类型的field(字段)和value(值)的映射表,hash特别适合用于存储对象。
● Redis中每个hash可以存储 232 - 1 键值对(40多亿)。
表达式 | 描述 | 说明 |
hset key field value | 添加键值对 | hset has01 h1 1 |
hmset key field value [field value ... ] | 添加多个键值对 | hmset hash01 h1 v1 h2 v2 |
hsetnx key field value | 域field不存在时,添加键值对 | hsetnx hash01 h3 v3 |
hget key field | 返回指定域field的值 | hget hash01 h1 |
hgetall key | 返回所有域和值 | hgetall hash01 |
hmget key field [field ... ] | 返回一个或多个域的值 | hmget hadh01 h1 h2 |
hkeys key | 返回所有的域 | hkeys hash01 |
hvals key | 返回所有的值 | hvals hash01 |
hexists key field | 查看域是否存在 | hexists hash01 h1 |
hdel key fied [field ... ] | 删除指定的域 | hdel hash01 h1 h2 |
hlen key | 返回域的数量 | hlen hash01 |
hincrby key field increment | 将域field的值加上增量 | hincrby hash01 h1 5 |
5 Redis的事物与锁机制
5.1 Redis的事物
5.1.1 介绍
● 事物机制,将多个命令打包,按顺序一次性执行多个命令的机制。
● 事物执行期间,不会被服务器终端,等事务执行完毕,才会去处理其它客户端的命令请求。
5.1.2 命令
● Redis通过MULTI、EXEC等命令实现事物。
● MULTI(开始)—— 将命令放入队列 —— EXEC(将事物提交给服务器执行)。
● 三个阶段:1.事物开始 2.命令入队 3.事物执行
● 放弃组队命令:DISCARD
【案例】事务正常执行
// 事务正常执行
hadoop102:6379> MULTI // 开启事务
OK
hadoop102:6379(TX)> set k1 10 // 将第一个命令入队,设置键值对,键k1,值10
QUEUED
hadoop102:6379(TX)> INCR k1 // 将第二个命令入队,将键k1的值增加1
QUEUED
hadoop102:6379(TX)> INCR k1 // 将第三个命令入队,将键k1的值增加1
QUEUED
hadoop102:6379(TX)> EXEC // 将事务提交到服务器执行
1) OK
2) (integer) 11
3) (integer) 12
【案例】事务中途放弃
hadoop102:6379> MULTI // 开启事务
OK
hadoop102:6379(TX)> set k2 v2 // 将第一个命令入队,设置键值对,键k2,值v2
QUEUED
hadoop102:6379(TX)> set k3 v3 // 将第二个命令入队,设置键值对,键k3,值v3
QUEUED
hadoop102:6379(TX)> DISCARD // 取消事务
OK
hadoop102:6379> keys * // 查看所有的键,键k2,k3并没有创建
1) "k1"
2) "hash01"
3) "zset01"
5.1.3 事物的错处理
1) 语法错误
【案例】组队时就会报错,事物提交后,报错不会执行任何命令
hadoop102:6379> MULTI // 开启事务
OK
hadoop102:6379(TX)> sets k1 v2 // 执行一个错误命令,报错
(error) ERR unknown command `sets`, with args beginning with: `k1`, `v2`,
hadoop102:6379(TX)> set k1 10 // 正确命令入队
QUEUED
hadoop102:6379(TX)> INCR k1 // 正确命令入队
QUEUED
hadoop102:6379(TX)> exec // 提交事务执行报错
(error) EXECABORT Transaction discarded because of previous errors.
2) 数据类型错误
【案例】组队时不会报错,发现不了,事物提交后,正常执行,类型错误的命令报错不执行,事物不会回滚。
hadoop102:6379> get k1
"11"
hadoop102:6379> MULTI // 开启事务
OK
hadoop102:6379(TX)> set k1 v1 // 命令入队
QUEUED
hadoop102:6379(TX)> INCR k1 // 命令入队,对键k1的值增加1,但是k1的值类型不对
QUEUED
hadoop102:6379(TX)> exec // 提交事务,只有错误的命令不会执行。
1) OK
2) (error) ERR value is not an integer or out of range
hadoop102:6379> get k1 // 查看键k1的值,修改成功了
"v1"
5.1.4 事物冲突问题
【案例】连个客户端都对同一个key操作时(man和woman共有100,慢刚打算用100时,woman却提前消费99,但man还是继续执行操作,结果出现负数)
如何解决呢?下面就需要使用到锁了。
5.2 Redis的锁
5.2.1 悲观锁和乐观锁
1) 悲观锁
● 定义:每次拿到数据,就会立即上锁。(每次向拿到数据,就会block,直到上一个事物结束,才能使用数据)
● 使用:关系型数据库里面就用到了很多这种锁机制:行锁、表锁、读锁、写锁等。都是在操作前先上锁。
2) 乐观锁
● 定义:每次拿到数据,都不会上锁,但会更新时会看别人更新了没有(数据还是原来的版本吗?,不是,就放弃),这里使用了版本号机制。
● 使用:乐观锁多用于多读的应用类型,这样可以提高吞吐量。
5.2.2 锁机制
1) 使用锁的命令
Watch —— 监控一个或多个key
● 在执行multi之前,使用watch限制key(方法:watch key ),如果事物执行前这个key被其它命令修改,那么此事物将被打断。
UNWATCH -- 取消对key的监视
● 在执行watch之后,执行了exec或discard命令,那么就会自动取消对key的监控,无需再执行unwatch命令
2) 事物及锁的使用
【案例】两个客户端同时对一个key操作
6 Redis的持久化机制
6.1 问题
1) Redis是基于内存的数据库,重启数据会丢失,如何避免呢?
● 数据持久化到磁盘
2) 如何基本保证Redis效率又能将数据持久化呢?
● RBD(快照)、AOF(日志)
6.2 快照(RDB)
6.2.1 什么是RDB内存快照?
1) 内存快照解释
● RDB(Redis DataBase)
● 在Redis执行“写”指令过程中,内存数据会一直变化。
● 内存快照:指Redis内存中数据在指定的时间间隔内将数据中的数据集快照写入到磁盘中。
2) Redis内存快照图
● Redis通过定期执行RDB内存快照,我们就不用担心忘记了,只需要在执行内存快照时写入磁盘就行。
● 保证了Redis的高效读写性能,实现了持久化、宕机快速恢复。
6.2.2 RDB快照生成策略
1) RDB手动触发
save
● 出来的进程独享资源
● 此时主进程没有资源可用,会阻塞客户端读/写数据。
bgsave
● 调用glibc函数,进而fork出一个子进程来执行快照操作
● 子进程——将RDB文件写入磁盘,用来快照持久化操作
● 父进程——处理客户端的请求
flushall
● 清空所有数据库后,创建一个空的RDB文件。
shutdown
● 关闭Redis数据库时,可以传入save参数,触发RDB快照
2) RDB自动触发
● 配置文件参数:save 900 1
● RDB持久化完全交给子进程负责,主进程继续处理客户端的请求,保证Redis的高效读写。
6.2.3 RDB方式的优缺点
优点
● 适合大规模数据恢复。
● 适合数据完整性和一致性要求不高的场景。
缺点
● 在一定间隔时间做一次备份,如果Redis意外宕机,就会丢失最后一个时间间隔的所有修改。
● 需要考虑fork时,内存中的数据膨胀问题,需提前规划好内存。
6.2.4 RDB持久化的启用于禁用
● Redis默认开启EDB持久化
● 禁用RDB持久化 —— save " " 。
● 动态停止RDB持久化 —— redis-cli config set save " " 。(无需重启Redis服务)
RDB持久化存在丢失数据的风险,那么还有更好的方式吗?
6.3 日志存储(AOF)
6.3.1 AOF持久化方式介绍
● AOF(AppendOnly File)日志存储的是Redis服务器顺序指令序列。
● AOF日志只记录:对内存进行修改的指令。
● AOF持久化的方式:以追加写的方式,将数据写入磁盘。Redis重启后首先读取该文件来重新构建数据。
● 构建数据:其实就是将文件中记录的写操作指令按顺序执行一遍。
● 默认情况下AOF机制是关闭的,使用AOF持久化机制时需要进行配置。
6.3.2 AOF持久化方式体验
1) 开启AOF持久化
配置文件中修改APPEND ONLY MODE部分中的appendonly参数:
appendonly yes
2) 查看目录中,新增的 appendonly.aof 文件。
3) 执行命令后,查看appendonly.aof文件内容
AOF文件格式
● * 数字:表示当前指令分为几个部分。
● $ 数字:表示这条命令各部分占用字节大小。( $3表示占用3个字节,也就是set )
AOF持久化方式问题
● 同样存在数据丢失风险:Redis刚执行完命令,但还没来得及追加到AOF文件,就发生了宕机,丢失本条数据。
● AOF持久化操作是主线程执行,虽避免了当前命令的阻塞,但是可能会给下一个命令带来阻塞。
AOF持久化带来的阻塞风险是和磁盘写回策略有关,如何合理的控制写指令执行后,指令落盘时机?
6.3.3 AOF文件写入策略
● 在AOF持久化时,用户调用write函数,将指令数组写入文件时, 操作系统会将数据暂存到内存缓冲区,等达到指定时间或大小,再将缓存数据写入磁盘。
● AOF配置项appendfsync的写回策略
策略 | 描述 | 特点 |
always | 同步写回,写指令执行完毕—将aof_buf缓冲区内容—写入AOF文件 | 同步持久化、性能差,但数据完整 |
everysec | 每秒写回,写指令执行完毕—写入AOF文件缓冲区—每隔1s再把缓冲区内容写入磁盘 | 默认配置,异步操作,每秒记录,最多丢失1s内的数据 |
no | os控制,写执行完毕—将日志写到AOF文件内存缓冲区—由os决定何时写入磁盘 | 性能最好,但时可能丢失更多的数据 |
6.3.4 AOF文件重写机制
背景:
● AOF采用文件追加,会越来越大,为了避免此情况,出现了文件重写机制。
流程:
● 当AOF文件大小超出所设置的阈值。
● Redis就会启动AOF文件内容压缩(AOF文件重写)。
● 只保留可恢复数据的最小指令集。
● 补充 — 主动触发命令:bgrewriteaof
原理:
① 开辟一个子进程对内存进行遍历转换(一系列Redis的操作指令)
② 序列化到一个新的AOF日志文件中,完毕后。
③ 再将操作期间发生的增量AOF日志追加到新的AOF日志文件,完毕后
④ 新AOF日志文件立即代替旧AOF日志文件,至此“瘦身”完成。
6.3.5 AOF重写触发
1) 配置参数(同时满足两个条件)
参数 | 描述 |
auto-aof-rewrite-percentage | 默认100,当AOF文件大小超过上一次重启后大小的100%时,进行重写。 |
auto-aof-rewrite-min-size | 默认64,当文件>64M时,进行重写 |
2) 命令触发
主动触发AOF重写的命令——bgrewriteaof
3) AOF重写体验
描述 | 视图 |
① 重复执行多条incr命令,查看AOF文件 | |
② 使用bgrewriteaof命令触发AOF重写,再次查看AOF文件 | |
③ 观察文件大小 |
6.3.6 AOF持久化优缺点
AOF优点
● 执行成功才会记录,避免语法检查带来的开销,并且并且不会阻塞当前写指令
● 可根据具体需求选择不同写回策略,兼顾了性能和可靠性
AOF缺点
● 相同数据集的数据,恢复数据的速度AOF<RDB,并且AOF文件>RDB文件。
● 受到文件系统对文件大小的闲置,不能保存过大的文件,文件变大,追加写的效率下降。
● 运行效率AOF<RDB,每秒同步策略同时兼顾了效率和可靠性,no策略的效率和RDB相同。
6.4 持久化方式选择
7 Redis主从复制
7.1 Redis主从复制概述
1)主从复制
● 将数据冗余一份复制到其他的Redis实例中。 前者—主节点(master) ,后者—从节点(slave)
● 数据的复制是单向的,主—>从
● 每个Redis实例启动,都是主节点(默认)
● 建立主从关系方法:配置文件、命令
2) 如何保证主从节点之间的数据一致性问题呢?
主从架构采用的方式:读写分离 (避免主从库都可以修改数据,导致数据不一致)
● 读操作:主、从都可以,生产环境多仅在从库,减轻主库压力
● 写操作:主库先执行,之后将操作命令同步到从库中执行
7.2 主从复制实战
8 Redis的哨兵模式(sentinel)
8.1 哨兵模式概述
8.1.1 定义
● 哨兵模式,在主从复制模式下,单独运行一个哨兵进程,哨兵通过发送命令,等待Redis服务器响应。从而监控多个运行的Redis实例。
8.1.2 作用
1) 监控Redis服务状态
● 通过发送命令,从而让Redis服务器响应,监控其运行状态,包括主从服务器。
2) 主从切换
● 哨兵检测到master宕机后,会自动将从机slaver切换为主机master。
● 然后通知其他从服务器,是哪个从机变成了主机。
● 再修改其他服务器配置信息,完成其它从机的主机切换。
总结:master宕机——>从机B变主机——>通知其它从机——>修改其它从机