NoSQL篇-Redis数据库
1.NoSQL的概念:
NoSQL(Not Only SQL)即不仅仅是SQL,泛指非关系型的数据库,它可以作为关系型数据库的良好补充。随着互联网web2.0网站的兴起,非关系型的数据库现在成了一个极其热门的新领域,非关系数据库产品的发展非常迅速。
为什么会出现NoSQL技术:传统的关系型数据库在应付web2.0网站暴露了很多难以克服的问题:
- 数据的高并发读写
- 海量数据的高效率存储和访问
- 数据库的高扩展和高可用
NoSQL的类别:
- 键值(Key-Value)存储数据库:这一类数据库主要会使用到一个哈希表
- 应用:内容缓存,主要用于处理大量数据的高访问负载。
- 优势:快速查询
- 劣势:存储的数据缺少结构化
- 列存储数据库:这部分数据库通常是用来应对分布式存储的海量数据
- 应用:分布式文件系统
- 优势:查找速度快,可扩展性强,更容易进行分布式扩展
- 劣势:功能相对局限
- 文档型数据库:该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储
- 应用:Web应用
- 优势:数据结构要求不严格
- 劣势:查询性能不高,且缺乏统一的查询语法
- 图形(Graph)数据库:图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上
- 应用:社交网络
- 优势:利用图结构相关算法
- 劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案
- 键值(Key-Value)存储数据库:这一类数据库主要会使用到一个哈希表
NoSQL适应场景:
- 数据模型比较简单
- 需要灵活性更强的IT系统
- 对数据库性能要求较高
- 不需要高度的数据一致性
- 对于给定key,比较容易映射复杂的环境
- 取最新的N个数据(如排行榜)
- 数据缓存
CAP:
- Consistency(强一致性)
- Availability(可用性)
- Partition tolerance(分区容错性)
什么是BASE:
- BASE就是为了解决关系数据库强一致性引起的问题而导致可用性降低而提出的解决方案。BASE其实是下面三个术语的缩写:
- 基本可用(==B==asically ==A==vailable)
- 软状态(==S==oft state)
- 最终一致(==E==ventually consistent)
- BASE就是为了解决关系数据库强一致性引起的问题而导致可用性降低而提出的解决方案。BASE其实是下面三个术语的缩写:
2.Redis概念:
Redis诞生于2009年全称是Remote Dictionary Server,远程词典服务器
redis是一款高性能的NOSQL的系列的基于内存的键值型的非关系型数据库,redis是单线程的
特征:
- 键值(key-value)型,value支持多种不同数据结构,功能丰富
- 单线程,每个命令具备原子性
- 低延迟,速度快(基于内存、IO多路复用、良好的编码)
- 支持数据持久化
- 支持主从集群、分片集群
- 支持多语言客户端
关系型数据库与非关系型数据库:
关系型数据库 非关系型数据库 数据结构 结构化(表和表的约束) 非结构化(键值类型Redis,文档类型MongoDB,列类型HBase,Graph类型Neo4j) 数据关联 关联的(表的外键) 无关联的 查询方式 SQL查询 非SQL查询 事务特性 ACID(事务的四大特征) BASE 存储方式 磁盘 大多存储在内存 扩展性 垂直 水平 - 使用场景:
- SQL数据库:1) 数据结构固定,2) 相关业务对数据安全性一致性要求较高
- NOSQL数据库:1) 数据结构不固定,2) 对一致性,安全性要求不高,3) 对性能要求较高
- 使用场景:
3.下载安装:
yum安装:
安装:
1
yum install -y redis
重启服务:
1
systemctl restart redis
redis的配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18vim /etc/redis.conf
修改内容如下
[Unit]
服务描述
Description=Redis Server Manager
服务类别
After=network.target
[Service]
后台运行的形式
Type=forking
服务命令
ExecStart=/usr/local/redis-6.2.7/bin/redis-server /etc/redis.conf
给服务分配独立的临时空间
PrivateTmp=true
[Install]
运行级别下服务安装的相关设置,可设置为多用户,即系统运行级别为3
WantedBy=multi-user.target使用redis客户端进行连接:
1
redis-cli -p 6379 -a 123456
docker安装:
上传redis.conf配置文件
1
https://blog.csdn.net/qq_56776641/article/details/129057235?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22129057235%22%2C%22source%22%3A%22qq_56776641%22%7D
修改配置文件中的密码:
1
2
3vim ~/redis/redis.conf
内容
requirepass 123456创建容器,设置端口映射
1
2
3
4
5
6docker run -id --name=c_redis -p 6379:6379 \
-v $PWD/data:/data -v $PWD/redis.conf:/etc/redis/redis.conf \
redis:6.2.4
--restart=always:总是开机启动
--appendonly yes:开启redis 持久化
–requirepass:设置密码进入redis容器
1
docker exec -it c_redis /bin/bash
启动redis客户端:
1
redis-cli -p 6379 -a 123456
4.redis的数据结构:
redis存储的是:key,value格式的数据,其中key都是字符串,value常用的有8种不同的数据结构
value的数据结构:
基本数据结构 说明 字符串类型 String String 哈希类型 Hash map格式 列表类型 List linkedlist格式。支持重复元素 集合类型 Set 不允许重复元素 有序集合类型 SortedSet 不允许重复元素,且元素有顺序 特殊数据结构 说明 Geo 存储的地理位置的经纬度 BitMap 本身不是一种数据类型, 实际上它就是字符串,可以实现对位的操作 HyperLog Redis HyperLogLog 是用来做基数统计的算法
5.命令操作:
2.通用命令:
| 格式 | 说明 |
|---|---|
| select [数据库号] | 切换数据库(redis的数据库有16个) |
| help @[数据类型] | 查看相关数据类型的命令 |
| help [commend] | 查询相关命令的用法 |
| keys [条件] | 查看符合模板的所有key,不建议在生产环境设备上使用 |
| expire [key] [time] | 给一个key设置有效期,到期时会被自动删除;创建key时没有设置时间key将永久有效,表示为-1; |
| ttl [key] | 查看一个KEY的剩余有效期 |
| exists [key] | 判断key是否存在 |
| set [key] [value] | 添加或者修改已经存在的一个String类型的键值对 |
| set [key] [value] ex [time] | 组合命令,添加一个的键值对,并且指定有效期(单位秒) |
| get [key] | 获取指定 key 的值 |
| mset [key1] [value1] … | 批量添加多个键值对 |
| mget [key1] [key2] ….. | 根据多个key获取多个String类型的value |
| setnx [key] [value] | 添加一个String类型的键值对,前提是这个key不存在,否则不执行 |
| setex [key] [time] [value] | 添加一个String类型的键值对,并且指定有效期(单位秒) |
| del [key1] [key2]….. | 删除一个指定的key,或删除多个key |
| flushdb | 当前数据库中的所有Key |
| flushall | 所有数据库中的key |
| incr [key] | 让一个整型的key自增1,返回增加1后的值 |
| incrby [key] [增长数] | 让一个整型的key自增并指定步长,返回增加1个步长后的值 |
| decr [key] | 将 key 中储存的数字值减一 |
| decr key [增长数] | key 所储存的值减去给定的减量值(decrement) |
| append [key] [value] | 如果 key 已经存在并且是一个字符串,将指定的value 追加到该 key 原来值 value 的末尾 |
查看相关数据类型的命令
1
help @hash
查询相关命令的用法
1
help keys
查看符合模板的所有key,不建议在生产环境设备上使用
1
2keys * # 查询所有的键
keys a* # 查询以a开头的键删除一个指定的key,或删除多个key
1
del k1 k2 k3
判断key是否存在
1
exists name
给一个key设置有效期,有效期到期时该key会被自动删除;如果创建key时没有设置时间key将永久有效,表示为-1;
1
expire name 20 # (单位秒)
查看一个KEY的剩余有效期
1
ttl name
3.String类型:
String类型,也就是字符串类型,时Redis中最简单的存储类型。
其中value是字符串,不过根据字符串的格式不同,又可以分为3类:
分类 说明 string 普通字符串 int 整形类型,可以做自增,自减操作 float 浮点类型,可以做自增,自减操作 不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过512m.
常见命令:
1
2# 添加或者修改一个String类型的键值对
set name itcast1
2# 添加或者修改一个String类型的键值对(多key结构)
set heima:user:1 itcast1
2# 批量添加多个String类型的键值对
mset k1 v1 k2 v2 k3 v31
2# 根据多个key获取多个String类型的value
mget k1 k2 k31
2# 让一个整型的key自增1
incr age1
2# 让一个整型的key自增并指定步长
incrby age 21
2# 添加一个String类型的键值对,前提是这个key不存在;如果不存在不执行
setnx lock 1
4.Hash类型:
Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构
String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便

Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD:

常用命令:
常用命令 说明 hset [key] [field] [value] 添加或者修改hash类型key的field的值 hget [key] [field] 获取一个hash类型key的field的值 hmset [key1] [field1] [value1] [field2] [value2]… 批量添加多个hash类型key的field的值 hmget [key1] [field1] [field2]….. 批量获取多个hash类型key的field的值 hgetall [key] 获取一个hash类型的key中的所有的field和value hkeys [key] 获取一个hash类型的key中的所有的field hvals [key] 获取一个hash类型的key中的所有的value hincrby [key] [field] [time] 让一个hash类型key的字段值自增并指定步长,返回增长后的值 hsetnx [key] [field] [value] 添加一个hash类型的key的field值,前提是这个field不存在,否则不执行 1
2
3# 添加或者修改hash类型key的field的值
hset heima:user:1 name zhangsan
hset heima:user:1 age 181
2# 获取一个hash类型key的field的值
hget heima:user:3 age1
2# 获取一个hash类型的key中的所有的field和value
hgetall heima:user:31
2# 获取一个hash类型的key中的所有的field
hkeys heima:user:31
2# 让一个hash类型key的字段值自增并指定步长,返回增长后的值
hincrby heima:user:3 age 2
5.List类型:
Redis中的List类型与Java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。
特征也与LinkedList类似:
- 有序 2.元素可以重复 3.插入和删除快 4.查询速度一般
常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等。
常用命令:
常见命令 说明 lpush [key] [value2] [value2]…. 向列表左侧插入一个或多个元素,返回链表长度 lpop [key] 移除并返回列表左侧的第一个元素,没有则返回null rpush [key] [value2] [value2]…. 向列表右侧插入一个或多个元素,返回链表长度 rpop 移除并返回列表右侧的第一个元素 lrange [key] [start] [end] 返回一段角标范围内的所有元素,从零开始[ start end ) blpop [key] [time] 移除并返回列表左侧的第一个元素,只不过在没有元素时等待指定时间(单位秒) brpop [key] [time] 移除并返回列表右侧的第一个元素,只不过在没有元素时等待指定时间(单位秒) 1
2# 向列表左侧插入一个或多个元素(顺序为3-2-1)
lpush users 1 2 31
2# 向列表右侧插入一个或多个元素(顺序为1-2-3)
rpush users 1 2 31
2# 移除并返回列表左侧的第一个元素
lpop users1
2# 返回一段角标范围内的所有元素,[ start end )
lrange users 0 21
2# 移除并返回列表左侧的第一个元素,只不过在没有元素时等待指定时间(单位秒)
blpop users 10
6.集合类型 set:
Redis的Set结构与Java中的HashSet类似,可以看做是一个value为null的HashMap。因为也是一个hash表,因此具备与HashSet类似的特征:
特点:
- 无序 2.元素不可重复 3.查找快 4.支持交集、并集、差集等功能
常见命令:
常见命令 说明 sadd [key] [value1] [value2]…. 向set中添加一个或多个元素 srem [key] [value] 移除set中的指定元素 scard [key] 返回set中元素的个数 sismember [key] [value] 判断一个元素是否存在于set中,如果存在返回1 smembers [key] 获取set中的所有元素 sinter [key1] [key2] 求key1与key2的交集,返回相同的元素 sdiff [key1] [key2] 求key1与key2的差集,返回key1中不相同的元素 sunion [key1] [key2] 求key1和key2的并集,返回所有的元素(不重复) 1
2# 向set中添加一个或多个元素
sadd users a b c d1
2# 移除set中的指定元素
srem users a1
2# 返回set中元素的个数
scard users1
2# 判断一个元素是否存在于set中
sismember users a1
2# 获取set中的所有元素
smembers users1
2# 求key1与key2的交集,返回相同的元素
sinter users products
7.有序集合类型 sortedset:
Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表
SortedSet具备下列特性:
- 可排序 2.元素不重复 3.查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可
注意:score为相同数据类型
常用命令:
常用命令 说明 zadd [key] [score1] [value1] [score2] [value2]…. 添加一个或多个元素到sorted set ,如果已经存在则更新其score值 zrem [key] [value] 删除sorted set中的一个指定元素 zscore [key] [value] 获取sorted set中的指定元素的score值 zrank [key] [value] 获取sorted set 中的指定元素的排名,从零开始 zcard [key] 获取sorted set中的元素个数 zcount [key] [min] [max] 统计score值在给定范围内的所有元素的个数,包括min和max zrange [key] [start] [stop] 按照score排序后,获取指定排名范围内的元素,包括start和stop zincrby [key] [increment] [value] 让sorted set中的指定元素自增,步长为指定的increment值 zrangebyscore [key] [min] [max] 按照score排序后,获取指定score范围内的元素,包括min和max zdiff,zinter,zunion 求差集、交集、并集 1
2# 添加一个或多个元素到sorted set ,如果已经存在则更新其score值
zadd stus 85 jack 45 lucy 98 rose 68 wangsan1
2# 删除sorted set中的一个指定元素
zrem stus jack1
2# 获取sorted set中的指定元素的score值
zscore stus jack1
2
3# 获取sorted set 中的指定元素的排名,从零开始
zrank stus lucy # 升序
zrevrank stus jack # 降序1
2# 获取sorted set中的元素个数
zcard stus1
2# 统计score值在给定范围内的所有元素的个数
zcount stus 45 851
2
3# 按照score排序后,获取指定排名范围内的元素
zrange stus 0 1 # 升序
zrevrange stus 0 1 # 降序1
2# 让sorted set中的指定元素自增,步长为指定的increment值
zincrby stus 2 jack1
2# 按照score排序后,获取指定score范围内的元素
zrangebyscore stus 45 87
8.位操作Bitmaps:
Bitmaps 本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作
Bitmaps 单独提供了一套命令, 所以在 Redis 中使用 Bitmaps 和使用字符串的方法不太相同。 可以把 Bitmaps 想象成一个以位为单位的数组, 数组的每个单元只能存储 0 和 1, 数组的下标在 Bitmaps 中叫做偏移量
实例:存储网站每天访问次数
语法:
bitmaps命令 说明 setbit [key] [offset] [value] 设置Bitmaps中某个偏移量的值(只能是0或1),offset默认从0开始偏移 getbit [key] [offset] 获取Bitmaps中某个偏移量的值 bitcount [key] [start end] 返回字符串被设置为1的个数,例:100001中有2个1 1
2
3setbit users:20210101 1 1 -- 1
setbit users:20210101 6 1 -- 100000
setbit users:20210101 11 1 -- 100000000001
getbit users:20210101 6 -- 获取第6位上的1
9.基数统计HyperLog:
Redis HyperLogLog 是用来做基数统计的算法
基数:一个集合中,不重复的数
HyperLogLog 的优点是:在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素
语法:
HyperLog命令 说明 pfadd [key] [value…..] 向key集合中加入value,添加成功返回1,否则返回0 pfcount [key…..] 统计所选集合们中不重复的个数(所有集合值加在一起不重复) pfmerge [destkey] [sourcekey…..] 将sourcekey集合们的值合并到destkey集合 1
2pfadd program "java"
pfadd program "java" "C++"1
pfcount program
1
pfmerge hll3 hll1 hll2
10.经纬度GEO:
Redis 3.2 中增加了对 GEO 类型的支持。GEO,Geographic,地理信息的缩写。该类型,就是元素的 2 维坐标,在地图上就是经纬度。
redis 基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度 Hash 等常见操作
语法:
GEO命令 说明 geoadd [key] [longitude] [latitude] [name]… 添加地理位置信息到key集合,longitude经度,latitude维度,name名称 geopos [key] [name] 获取key集合中name的经纬度 geodist [key] [name1] [name2] 获取两个位置之间的之直线距离 georadius [key] [longitude] [latitude] [num] [unit] 获取以给定经纬度为中心,num长度,unit单位的半径内的元素 1
2geoadd china:city 121.47 31.23 shanghai
geoadd china:city 121.47 31.23 shanghai china:city 121.47 31.23 shanghai1
geopos china:city shanghai
1
geopos china:city shanghai beijing
1
geoadd china:city 121.47 31.23 1000 km
5.Redis的配置文件:
1.Units:
1 | 配置大小单位,开头定义了一些基本的度量单位,只支持 bytes,不支持 bit。大小写不敏感。 |
2.network:
bind:默认情况下 bind=127.0.0.1 只能接受本机的访问请求。在不写的情况下,无限制接受任何 IP 地址的访问。
1
bind 127.0.0.1
protected-mode:如果开启了protected-mode,那么在没有设定bind ip且没有设密码的情况下,Redis只允许接受本机的响应。
1
protected-mode no
tcp-backlog:设置 tcp 的 backlog,backlog 其实是一个连接队列,backlog队列总和 = 未完成三次握手队列 + 已经完成三次握手队列。在高并发环境下你需要一个高 backlog 值来避免慢客户端连接问题。
1
tcp-backlog 511
timeout:一个空闲的客户端维持多少秒会关闭,0表示关闭该功能。即永不关闭。
1
timeout 0
tcp-keepalive:对访问客户端的一种心跳检测,每 n 秒检测一次。单位为秒,如果设置为0,则不会进行 Keepalive 检测,建议设置成 60。
1
tcp-keepalive 0
3.general:
daemonize:是否为后台进程,即守护进程,用于后台启动,设置为yes。
1
daemonize yes
pidfile:存放pid文件的位置,每个实例会产生一个不同的pid文件。
1
pidfile /var/run/redis_6379.pid
loglevel:指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为notice。四个级别根据使用阶段来选择,生产环境选择 notice 或者warning。
1
loglevel notice
logfile:日志文件名称
1 | logfile "" |
databases:设定库的数量,默认16,默认数据库为0,可以使用 SELECT
命令在连接上指定数据库id。 1
databases 16
4.security:
1 | # 设置密码 |
1 | 127.0.0.1:6379> config get requirepass # 查看密码 |
5.clients:
maxclients:设置redis同时可以与多少个客户端进行连接。默认情况下为10000个客户端。如果达到了此限制,redis则会拒绝新的连接请求,并且向这些连接请求方发出“max number of clients reached”以作回应。
1
maxclients 10000
maxmemory:建议必须设置,否则可能导致将内存占满,造成服务器宕机。设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以通过maxmemory-policy来指定。
1
maxmemory [字节数]
maxmemory-policy:设置内存达到使用上限后的移除规则
参数 说明 volatile-lru 使用LRU算法移除key,只对设置了过期时间的键;(最近最少使用) allkeys-lru 在所有集合key中,使用LRU算法移除key volatile-random 在过期集合中移除随机的key,只对设置了过期时间的键 allkeys-random 在所有集合key中,移除随机的key volatile-ttl 移除那些TTL值最小的key,即那些最近要过期的key noeviction 不进行移除。针对写操作,只是返回错误信息 1
maxmemory-policy noeviction
maxmemory-samples:用于设置样本数量,LRU算法和最小TTL算法都并非是精确的算法一般设置3到7的数字,数值越小样本越不准确,但性能消耗越小
1
maxmemory-samples 5
6.Redis发布与订阅:
概念:Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息
命令行实现:
打开一个客户端订阅channell:
1
subscribe [订阅名]
打开另一个客户端,给channell发布消息hello
1
publish [订阅名] hello -- 返回当前订阅者数量
7.事务管理:
1.事务定义:
- Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- Redis 事务的主要作用就是串联多个命令防止别的命令插队。
2.Multi、Exec、discard:
Multi:从输入 Multi 命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入 Exec 后,Redis会将之前的命令队列中的命令依次执行组队的过程中可以通过 discard 来放弃组队。
使用:
1
2
3
4
5
6
7127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> exec
3.事务的错误处理:
- 组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消。
- 如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
4.事务的冲突问题:
悲观锁:
- 悲观锁(Pessimistic Lock)顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁
乐观锁:
- 乐观锁(Optimistic Lock)顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁
- 但是在更新的时候会判断一下在此期间别人有没有去更新这个数据
- 乐观锁适用于多读的应用类型,这样可以提高吞吐量
- Redis就是利用这种check-and-set机制实现事务的
watch:
在执行 multi 之前,先执行 watch key1 [key2] 可以监视一个(或多个) key ,如果在事务执行之前这个/些 key 被其他命令所改动,那么事务将被打断。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18客户端A:
127.0.0.1:6379> watch balance
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby balance 8000
QUEUED
127.0.0.1:6379> exec
1) (integer) 2000
客户端B:
127.0.0.1:6379> watch balance
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby balance 2000
QUEUED
127.0.0.1:6379> exec
1) (nil) # 客户端B没有修改成功
unwatch:
- 用于取消 WATCH 命令对所有 key 的监视。
- 如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。
事务三特性:
- 单独的隔离操作:
- 事务中的所有命令都会序列化、按顺序地执行
- 事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行。
- 不保证原子性:事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
- 单独的隔离操作:
8.数据持久化:
1.分类:
- Redis提供了主要提供了 2 种不同形式的持久化方式:
- RDB(Redis数据库):RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。
- AOF(Append Only File):AOF 持久化记录服务器接收到的每个写操作,在服务器启动时再次播放,重建原始数据集。 命令使用与 Redis 协议本身相同的格式以仅附加方式记录。 当日志变得太大时,Redis 能够在后台重写日志。
2.RDB持久化:
Redis会单独创建(fork)一个子进程来进行持久化化,会先将数据写入到 一个临时文件中,待持久化过程都结束后,再用这个临时文件替换上次持久化好的文件。
整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能
Redis默认支持,不许需要进行配置,默认就使用这种机制
快照文件称为.rdb文件,默认是保存在当前运行目录。
如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
RDB的缺点是最后一次持久化后的数据可能丢失。
Fork:
- 作用:复制一个与当前进程一样的进程。
- 在 Linux 程序中,fork() 会产生一个和父进程完全相同的子进程,但子进程在此后多会 exec 系统调用,出于效率考虑,Linux 中引入了“写时复制技术”。
当主进程执行读操作时,访问共享内存;
当主进程执行写操作时,则会拷贝一份数据,执行写操作。
RDB方式bgsave的基本流程:
- fork主进程得到一个子进程,共享内存空间
- 子进程读取内存数据并写入新的RDB文件
- 用新RDB文件替换旧的RDB文件
RDB的优缺点:
- 优点:
- 适合大规模的备份
- 节省磁盘空间
- 恢复速度快
- 缺点:
- RDB执行间隔时间长
- 两次RDB之间写入数据有丢失的风险
- fork子进程、压缩、写出RDB文件都比较耗时
- 优点:
RDB持久化的配置:
1
2
3
4
5dbfilename dump.rdb # 快照将被写入dbfilename指定的文件中(默认是dump.rdb文件)
dir "./" # 配置文件路径
stop-writes-on-bgsave-error yes # 当 Redis 无法写入磁盘的话,直接关掉 Redis 的写操作,推荐配置为 yes。
rdbcompression yes # 是否进行压缩存储,如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能
rdbchecksum yes #RDB持久化在四种情况下会执行:
执行save命令:
1
save -- 由Redis主线程来执行RDB,会阻塞所有命令
执行bgsave命令(推荐使用):
1
bgsave -- 开启子进程执行RDB,避免主进程受到影响
Redis停机时:Redis停机时会执行一次save命令,实现RDB持久化
触发RDB条件时
Redis内部有触发RDB的机制,可以在redis.conf文件中找到,格式如下:
1
2
3
4# 900秒内,如果至少有1个key被修改,则执行bgsave , 如果是save "" 则表示禁用RDB
save 900 1
save 300 10
save 60 10000RDB的其它配置也可以在redis.conf文件中设置:
1
2
3
4
5
6
7
8# 是否压缩 ,建议不开启,压缩也会消耗cpu,磁盘的话不值钱
rdbcompression yes
# RDB文件名称
dbfilename dump.rdb
# 文件保存的路径目录
dir ./
3.AOF持久化:
AOF全称为Append Only File(追加文件)。
Redis处理的每一个写命令(写操作)都会记录在AOF文件, 只许追加文件但不可以改写文件,可以看做是命令日志文件。
redis 启动之初会读取该文件重新构建数据
持久化流程:
图示:

- 客户端的请求写命令会被 append 追加到 AOF 缓冲区内。
- AOF 缓冲区根据 AOF 持久化策略 [always,everysec,no] 将操作sync同步到磁盘的 AOF 文件中。
- AOF 文件大小超过重写策略或手动重写时,会对 AOF 文件 rewrite 重写,压缩 AOF 文件容量。
- Redis 服务重启时,会重新 load 加载 AOF 文件中的写操作达到数据恢复的目的。
AOF配置:
需要修改redis.conf配置文件来禁用RDB
1
2
3save 900 1
.......
save "" //禁用RBDAOF默认是关闭的,需要修改redis.conf配置文件来开启AOF:
1
2
3
4# 是否开启AOF功能,默认是no
appendonly yes
# AOF文件的名称
appendfilename "appendonly.aof"AOF的命令记录的频率也可以通过redis.conf文件来配:
1
2
3
4
5
6# 方案一:表示每执行一次写命令,立即记录到AOF文件
appendfsync always
# 方案二:写命令执行完先放入AOF缓冲区,然后表示每隔1秒将缓冲区数据写到AOF文件,是默认方案
appendfsync everysec
# 方案三:写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘
appendfsync no配置项 刷盘时机 优点 缺点 Always 同步刷盘 可靠性高,几乎不丢数据 性能影响大 everysec 每秒刷盘 性能适中 最多丢失1秒数据 no 操作系统控制 性能最好 可靠性较差,可能丢失大量数据
AOF文件重写(Rewrite压缩):
因为是记录命令,AOF文件会比RDB文件大的多。
而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义。
重写流程:
- 图示:
- bgrewriteaof触发重写,判断是否当前有bgsave或bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行。
- 主进程fork出子进程执行重写操作,保证主进程不会阻塞。
- 子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失。
- 子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。主进程把aof_rewrite_buf中的数据写入到新的AOF文件。
- 使用新的AOF文件覆盖旧的AOF文件,完成AOF重写。
通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同效果。
1
bgrewriteaof
Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置:
1
2
3
4# AOF文件比上次文件 增长超过多少百分比则触发重写
auto-aof-rewrite-percentage 100
# AOF文件体积最小多大以上才触发重写
auto-aof-rewrite-min-size 64mb
AOF的优缺点:
- 优点:
- 备份机制更稳健,丢失数据概率更低。
- 可读的日志文本,通过操作AOF稳健,可以处理误操作。
- 缺点:
- 比起RDB占用更多的磁盘空间。
- 恢复备份速度要慢。
- 每次读写都同步的话,有一定的性能压力。
- 存在个别Bug,造成恢复不能。
- 优点:
4.RDB与AOF的对比:
| RDB | AOF | |
|---|---|---|
| 持久化 | 定时对整个内存做快照 | 记录每一次执行的命令 |
| 数据完整性 | 不完整,两次备份之间会丢失 | 相对完整,取决于刷盘策略 |
| 文件大小 | 会有压缩,文件体积小 | 记录命令,文件体积很大 |
| 宕机恢复速度 | 很快 | 慢 |
| 数据恢复优先级 | 低,因为数据完整性不如AOF | 高,因为数据完整性更高 |
| 系统资源占用 | 高,大量CPU和内存消耗 | 低,主要是磁盘IO资源,AOF重写占用大量CPU和内存资源 |
| 使用场景 | 可以容忍数分钟的数据丢失,追求更快的启动速度 | 对数据安全性要求较高 |
9.Redish的主从复制:
1.概念:
Redis主从复制,Master 节点写操作,Slave 节点读操作,Master 和 Slave分别代表了一个个不同的 Redis Server 实例
另外定期的数据备份操作也是单独选择一个 Slave 去完成,这样可以最大程度发挥 Redis 的性能,为的是保证数据的弱一致性和最终一致性
另外,Master 和 Slave 的数据不是一定要即时同步的,但是在一段时间后 Master 和 Slave 的数据是趋于同步的,这就是最终一致性
Replication Id:简称replid,是数据集的标记
- id一致则说明是同一数据集。
- 每一个master都有唯一的replid,slave则会继承master节点的replid(master如何得知salve是第一次来连接)
offset:偏移量
- 随着记录在repl_baklog中的数据增多而逐渐增大。
- slave完成同步时也会记录当前同步的offset。
- 如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。
repl_baklog文件:
- 这个文件是一个固定大小的数组,只不过数组是环形
- 角标到达数组末尾后,会再次从0开始读写,这样数组头部的数据就会被覆盖。
- repl_baklog大小有上限,写满后会覆盖最早的数据。如果slave断开时间过久,导致数据被覆盖,则无法实现增量同步,只能再次全量同步。
全量同步:主从第一次建立连接时,会执行全量同步,将master节点的所有数据都拷贝给slave节点
- slave节点请求增量同步
- master节点判断replid,发现不一致,拒绝增量同步
- master将完整内存数据生成RDB,发送RDB到slave
- slave清空本地数据,加载master的RDB
- master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave
- slave执行接收到的命令,保持与master之间的同步
增量同步:除了第一次做全量同步,其它大多数时候slave与master都是做增量同步。
- Master 接收到用户的操作指令,判断是否需要传播到 Slave。
- 将操作记录追加到 AOF 文件。
- 将操作传播到其他 Slave:对齐主从库;往响应缓存写入指令。
- 将缓存中的数据发送给 Slave。
2.搭建主从:
复制redis的配置文件为redis-6380.conf
1
cp /etc/redis.conf /root/rediscluster/redis-6380.conf
修改文件内容:
1
2
3
4
5
6
7
8
9
10
11
12vim /root/rediscluster/redis-6380.conf
修改内容
修改端口号
port 6380
后台运行
daemonize yes
修改pid文件名
pidfile "/var/run/redis_6379.pid"
修改持久化文件名
dbfilename "dump_6379.rdb"
dir "/rediscluster"复制redis-6380.conf成两份文件:
1
2cp redis-6380.conf redis-6381.conf
cp redis-6380.conf redis-6382.conf通过sed修改内容:
1
2sed -i 's/6380/6381/g' ./redis-6381.conf
sed -i 's/6380/6382/g' ./redis-6382.conf启动服务:
1
2
3redis-server redis-6380.conf
redis-server redis-6381.conf
redis-server redis-6382.conf查看redis的进程:
1
ps -ef | grep redis
连接服务:
1
2
3redis-cli -p 6380
redis-cli -p 6381
redis-cli -p 6382选择两种方案中的一种进行配置,在5.0以后新增命令replicaof,与salveof效果一致。
修改配置文件(永久生效)
1
2方式一:vim
replicaof 192.168.73.141 7001 -- 告诉redis1(Master)节点自己是slave1
2方式二:sed
sed -i 's/^# replicaof <masterip> <masterport>/replicaof 127.0.0.1 6380/' redis-6381.conf使用redis-cli客户端连接到redis服务,执行slaveof命令(重启后失效):
1
slaveof 192.168.73.141 7001 -- 告诉redis1(Master)节点自己是slave
查看从节点状态
1
2
3redis-cli -p 6380 info replication
redis-cli -p 6381 info replication
redis-cli -p 6382 info replication
3.主从切换:
当一个 master 宕机后,后面的 slave 可以立刻升为 master,其后面的 slave 不用做任何修改。
用 slaveof no one 将从机变为主机。
假设主机 6380 宕机了,我们可以在 6381 上执行如下命令来切换为主机:
1
redis-cli -p 6381 slaveof no one
查看从节点状态
1
redis-cli -p 6381 info replication
10.哨兵模式:
1.概念:
哨兵也叫 sentinel,Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。
哨兵的作用如下:
- 监控:Sentinel 会不断检查您的master和slave是否按预期工作
- 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
- 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端
集群监控原理:
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令
主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。
图示:

2.搭建哨兵集群:
在 /root/rediscluster 目录下新建 sentinel.conf 文件,文件名称不能写错,必须叫这个名称
1
2
3vim sentinel.conf
修改内容
sentinel monitor redismaster 127.0.0.1 6379 11
2
3
4参数说明:
monitor:监控
redismaster:为监控对象起的服务名称
1:为至少有多个个哨兵同意迁移的数量启动哨兵:
1
redis-sentinel sentinel.conf
验证哨兵是否可用:关闭主redis服务
1
redis-cli -p 6380 shutdown
查看从节点状态
1
redis-cli -p 6381 info replication
3.复制延时:
- 由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
4.选举策略:
- 选择优先级靠前的服务器。优先级的配置在 redis.conf 文件中的 replica-priority 配置,默认为 100,值越小优先级越高。
- 选择偏移量最大的。偏移量是指获得原主机数据最全的。
- 选择 runid 最小的从服务器。每个redis实例启动后都会随机生成一个40位的runid
11.集群环境:
1.集群的介绍:
- Redis 集群是一个提供在多个 Redis 间节点间共享数据的程序集。
- Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误
- Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令。Redis 集群的优势:
- 自动分割数据到不同的节点上。
- 整个集群的部分节点失败或者不可达的情况下能够继续处理命令。
2.数据分片:
- Redis 集群没有使用一致性hash,而是引入了哈希槽(slots)的概念。
- Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个节点负责一部分hash槽,比如当前集群有3个节点,那么:
- 节点 A 包含 0 到 5500号哈希槽。
- 节点 B 包含5501 到 11000 号哈希槽。
- 节点 C 包含11001 到 16384号哈希槽。
- 这种结构很容易添加或者删除节点。比如想新添加个节点D,只需要从节点 A、B、C中得部分槽并分配到D上即可。
- 如果想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可。
- 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。
3.集群搭建:
开启两个主机,一台上开启3个redis服务:
IP PORT 角色 192.168.178.101 7001 master 192.168.178.101 7002 master 192.168.178.101 7003 master 192.168.178.151 8001 slave 192.168.178.151 8002 slave 192.168.178.151 8003 slave 把之前主从复制中所产生的所有持久文件先清除。
1
rm -f dump*
复制redis的配置文件到指定目录下
1
mkdir /root/cluster
修改配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28vim redis-7001.conf
修改内容
include /root/cluster/redis.conf
修改端口号
port 7001
修改pid文件名
pidfile "/var/run/redis_7001.pid"
持久化文件存放目录
dir "/cluster"
修改持久化文件名
dbfilename "dump_7001.rdb"
绑定地址
bind 0.0.0.0
让redis后台运行
daemonize yes
注册的实例ip
replica-announce-ip 192.168.72.101
保护模式,如果开启了protected-mode,那么在没有设定bind ip且没有设密码的情况下,Redis只允许接受本机的响应。
protected-mode no
日志
logfile /cluster/log/run-7001.log
开启集群功能
cluster-enabled yes
设定节点配置文件名,不需要我们创建,由redis自己维护
cluster-config-file ./nodes-7001.conf
节点心跳失败的超时时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 10000如果希望开启 aof 持久化功能,则还需添加如下配置:
1
2
3
4
5
6
7
8
9开启aof持久化
appendonly yes
配置aof持久化文件名
appendfilename aof-6380.aof
配置同步方式
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewirte-min-size 64mb修改其他多个服务,通过以下命令
1
sed 's/6379/6380/g' redis-7002.conf
启动所有服务:
1
2
3
4
5
6redis-server redis-7001.conf
redis-server redis-7002.conf
redis-server redis-7003.conf
redis-server redis-8001.conf
redis-server redis-8002.conf
redis-server redis-8003.conf查看 Redis 服务进程
1
ps -ef | grep redis
创建集群:虽然服务启动了,但是目前每个服务之间都是独立的,没有任何关联。
1
redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006
参数 说明 redis-cli –cluster 代表集群操作命令 create 代表是创建集群 –replicas 1 或者–cluster-replicas 1指定集群中每个master的从节点个数为1 通过命令可以查看集群状态:
1
redis-cli -c -p 7001 cluster nodes
4.测试集群:
对集群操作时,需要给 redis-cli 加上 -c 参数
尝试连接 6379 节点(连接任何一个节点都可以),存储一个数据:
1
2
3
4
5
6
7
8# 连接
redis-cli -h 192.168.72.101 -c -p 7001
# 存储数据
set num 123
# 读取数据
get num
# 再次存储
set a 1向集群批量添加值:
在集群环境下,由于 redis-cli 每次录入、查询键值时,Redis 都会计算出该 key 对应的插槽值,并交给对应插槽所在的节点进行处理。如果不是该客户端对应服务器的插槽 Redis 会报错
当我们在集群下使用 mset 或 mget 来添加多个值或查询多个值时:
1
2127.0.0.1:7001> mset name lisi age 20
(error) CROSSSLOT keys in request don't hash to the same slot解决方法:我们可以使用自定义组来完成,通过一对大括号 {} 来定义一个相同的组名,从而使 key 中的 {} 内相同内容的键值对放到同一个 slot 中
1
2127.0.0.1:7002> mset name{user} lisi age{user} 20
-> Redirected to slot [4867] located at 192.168.72.101:6379
向集群查询值:
查看组开始的插槽值
1
2127.0.0.1:7001> cluster keyslot user
(integer) 4867统计自己插槽所在范围的值(在自己创建的主从上查看)
1
2127.0.0.1:7001> cluster countkeysinslot 4867 #只能统计自己插槽所在范围的值
(integer) 2查看具体键值:
1
2
3127.0.0.1:7001> cluster getkeysinslot 4867 10
1) "name{user}"
2) "age{user}"
5.故障转移:
假设我们让 7001 所以服务器宕机
1
redis-cli -c -p 7001 shutdown
然后执行 cluster nodes 命令查看集群状态:
1
redis-cli -c -p 7002 cluster nodes
这时发现,7005从slave节点变成master节点;7001节点上的数据保存到7002和从节点中
1
2
3
4
5[root@zhanglin slicecluster]# redis-cli -c -p 7002
127.0.0.1:7002> keys *
1) "age{user}"
2) "set{user}"
3) "name"
6.集群扩容:
我们向现有集群中添加两个节点,这两个节点做了一主一从。主节点的端口号为 7007,从节点的端口号为 7008。
创建配置文件:
1
mv ./redis-7001.conf ./redis-7007.conf
启动服务:
1
2redis-server redis-7007.conf
redis-server redis-7008.conf添加主节点:add-node命令用于添加节点到集群中
1
2
3
4
5
6
7
8
9add-node new_host:new_port existing_host:existing_port --cluster-slave --cluster-master-id <arg>
例:redis-cli --cluster add-node 192.168.178.101:7007 192.168.178.101:7001
参数:
new_host:被添加节点的主机地址
new_port:被添加节点的端口号
existing_host:目前集群中已经存在的任一主机地址
existing_port:目前集群中已经存在的任一端口地址
--cluster-slave:用于添加从(Slave)节点
--cluster-master-id:指定主(Master)节点的ID(唯一标识)字符串添加从节点:
1
2
3
4先查看主节点的ID值
redis-cli -c -p 7008 cluster nodes
使用主节点的ID值添加从节点到集群中
redis-cli --cluster add-node 192.168.72.101:6386 192.168.178.101:7008 --cluster-slave --cluster-master-id 1982a39401caaf04d283545f2b8cf292eb64f211查看主节点和从节点是否添加成功:
1
redis-cli -c -p 7008 cluster nodes
分配插槽:将新主节点的插槽分配100个槽给新从节点
1
2
3
4
5
6
7
8
9
10
11
12
13reshard host:port --cluster-from [分配主节点ID值] --cluster-to [被分配从节点ID值] --cluster-slots [num] --cluster-yes --cluster-timeout [num] --cluster-pipeline [num] --cluster-replace
例:# redis-cli --cluster reshard 192.168.178.101:7007 --cluster-from [7007redis的ID值] --cluster-to
[7008redis的ID值] --cluster-slots 100 --cluster-yes --cluster-timeout 5000 --cluster-pipeline 10 --cluster-replace
参数:
host:集群中已经存在的任意主机地址
port:集群中已经存在的任意主机对应的端口号
--cluster-from:表示slot目前所在的节点node ID,多个ID用逗号分隔
--cluster-to:表示需要分配节点的node ID
--cluster-slot:分配的slot数量
--cluster-yes:指定迁移时的确认输入
--cluster-timeout:设置migrate命令的超时时间
cluster-pipeline:定义cluster getkeysinslot命令一次取出的key数量,不传的话使用默认值为10
--cluster-replace:是否直接replace到目标节点查看是否分槽成功:
1
redis-cli -c -p 7008 cluster nodes
7.集群缩容:
删除从节点:
收回分片:
1
2redis-cli --cluster reshard 192.168.178.101:7007 --cluster-from [7007redis的ID值] --cluster-to
[7003redis的ID值] --cluster-slots 100 --cluster-yes --cluster-timeout 5000 --cluster-pipeline 10 --cluster-replace查看集群状态:
1
127.0.0.1:6379> redis-cli -c -p 6379 cluster nodes
删除从节点:
1
redis-cli --cluster del-node 192.168.178.101:7006 [要删除的节点ID值]
查看是否安装成功:
1
redis-cli -c -p 7006 cluster nodes
删除主节点:
删除:
1
redis-cli --cluster del-node 192.168.178.101:7006 [要删除的节点ID值]
查看是否安装成功:
1
redis-cli -c -p 7006 cluster nodes
8.集群的优缺点:
- 优点:
- 扩容方便
- 分摊压力
- 无中心配置相对简单
- 缺点:
- 多键操作是不被支持的
- 多键的Redis事务是不被支持的。lua脚本不被支持