redis 哈希,集合,有序集合,持久化方案,主从复制,高可用,集群搭建扩容缩容
哈希类型操作方法
hget key field # 获取hash key对应的field的value值
hset key field # 设置
hdel key field # 删除
测试
hset user:1001 age 23
hget user:1001 age
hset user:1001 name lxj
hgetall user:1001 # 获取userinfo的所有key value 数据量太大会导致爆内存
hdel user:1001 name
其他操作
hexists user:1001 name # 判断user:1001里是否有 name key有的话则返回1
hlen user:1001 # 返回有多少key
hmget user:1001 name age # 批量获取key对应的value
hmset user:1001 hobby blueball gender man # 批量设置
hvals user:1001 # 获取所有value
hkeys user:1001 # 获取所有keys
应用场景
eg: 我们个人主页可以存在hash里以用户为表名,里面有一个key对应着主页访问量 每次有用户访问就可以给他+1 最后取出这个key对应的value就能统计出访问量
hincrby user:1001 pageview 1 # 增加1
hincrbyfloat # 浮点
hsetnx # 过期时间
列表类型
1.插入操作
rpush key value value2 # 从右侧插入
lpush # 左侧插入
linsert key before|after value newvalue # 从value前或者后插入newvalue
2.删除操作
lpop name # 从name列表左侧弹出
rpop name # 从name列表右侧弹出
lrem key count value # 根据count值,从列表中删除所有balue相同的项
count>0 # 从左到右,删除最多count个value相等的项
count<0 # 从右到左,删除最多count个value相等的项
count=0 # 删除所有value相等的项
lrem name 2 xx # 从左到右删除列表中2个xx
lrem name -1 xx # 从右到左删除列表中1个xx
ltrim name 1 2 # 只保留name表1-2范围内的元素
3.查询操作
lrange name 0 1 # 获取包含1在内的索引位的数据值
lrange name 1 -1 # 获取第一个位置到倒数第一个位置的数据
lindex key index # 获取列表指定索引位的数据值
lindex name 1 # 获取索引位1的数据值
lindex name -1 # 获取索引位倒数第一的值
llen key # 获取列表长度
4 修改操作
lset key index newvalue # 把指定索引位的数据修改成newvalue
lset name 1 lxj # 把name列表的索引位1修改成lxj
5.其他操作
blpop name timeout # lpop的阻塞版,timeout是阻塞超时时间,timeout=0 为拥有不阻塞
brpop name timeout # rpop的阻塞版,timeout是阻塞超时时间,timeout=0 为拥有不阻塞
实现栈的功能
lpush+lpop # 左进左出 就是先进后出
实现队列功能
lpush+rpop # 左进右出 先进先出
固定大小的列表
lpush+trim # 左进加修减
消息队列
lpush+brpop # 左进右取 没有就阻塞
集合类型 操作
无序,无重复
sadd boys lxj # boys集合增加一条数据
sadd boys gtl zs # 可以增加多条
scardkey boys # 统计集合大小返回数量
srem boys zs # 从集合里删除zs
sismember boys lxj # 判断lxj在不在boys里,返回0或1
srandmember boys 2 # 从boys里随机弹出2元素不会删除,
spop boys # 从集合中随机弹出1个 会删除
smembers boys # 获取boys里所有的元素
sdiff boys1 boys2 # 计算boys1与boys2 不一样的数据展示出来 想求boys2 的把boys2写到前面去
sinter boys1 boys2 # 计算boys1与boys2 一样的数据展示出来
sunion boys1 boys2 # 计算boys1与boys2 两个表全展示去重
# 应用场景
去重场景
抽奖系统 :通过spop来弹出用户的id,活动取消,直接删除
点赞,点踩,喜欢等,用户如果点了赞,就把用户id放到该条记录的集合中
标签:给用户/文章等添加标签,sadd user:1:tags 标签1 标签2 标签3
共同好友:集合间交叉并补操作
有序集合操作
有序,无重复
# 集合与有序集合对比
集合: 无重复元素,无序 # 元素
有序集合: 无重复元素 有序 # 元素+分值
# 列表与有序集合
列表: 可以重复 有序
有序集合: 无重复元素 有序 # 元素+分值
zset girls 100 lyf # 插入一条数据 100分 lyf
zset girls 80 xh # 插入一条数据 80分 xh
zset girls 60 glnz # 插入60分 glnz
zset girls 70 glnz # 插入一样的元素会修改他的分值
zrem girls 80 xh # 删除80分小红 返回1删除成功0删除失败
zscore girls glnz # 查看glnz分数 70
zincrby girls 20 glnz # 给glnz增加20分
zcard girls # 返回元素总个数
zrank girls lyf # 返回lyf的排名 是分数从小到大排的
zrange girls 0 1 # 因为有序的 会按排名取出1和2的人
zrange girls 0 -1 # 取出全部
zrangebyscore girls 60 90 # 取出60到90之间的人
zrangebyscore girls 60 90 withscores # 连分数也返回
zcount girls 60 90 # 返回60分到90分之间人的个数
zremrangebyrank girls 0 1 # 删除 排名0到1之间的人
zremrangebyscore girls 60 90 # 删除两个分数之间的
# 其他操作
zrevrank girls lxj # 返回某个元素从大到小的排序顺序
zrevrange #从大到小排序取一定范围
zrevrangebyscore # 按从大到小排序取出分数范围内的
zinterstore # 对两个有序集合求交集
zunionstore # 求并集
慢查询
因为redis数单线程架构 命令都是一个一个执行,会有长慢命令,会造成整个redis阻塞,redis提供了一种方式,可以记录长慢命令放到慢查询队列中
,用于后续的排查修改工作
我们可以配置一个时间 如果查询时间超过了我们设置的时间,就可以认为这是一个慢查询。
客户端发送命令的时候网络问题可能也会造成慢查询清空
配置慢查询
slowlog-max-len # 慢查询的队列长度
slowlog-log-slower-than # 超过多少微秒 就算慢查询命令,就会记录到慢查询队列中
实操
"启动redis服务的时候需要带配置文件"
config set slowlog-log-slower-than 0 # 0就是全都记录 一般是10000
config set slowlog-max-len 128 #最多记录128条
# 持久化到本地配置文件
config rewrite # 写了永久生效
# 查看慢查询队列
slowlog len # 获取慢查询队列长度
slowlog reser # 清空慢查询队列
slowlog get # 获取慢查询队列里的所有命令
pipeline与事物
Redis的pipeline(管道)功能在命令行中没有,但redis是支持pipeline的,而且在各个语言版的client中都有相应的实现(Redis模块)
将一批命令,批量打包,在redis服务端批量计算(执行),然后把结果批量返回
1次pipeline(n条命令)=1次网络时间+n次命令时间
# python实现pipline
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)
#创建pipeline
pipe = r.pipeline(transaction=True)
#开启事务
pipe.multi()
pipe.set('name', 'lqz')
#其他代码,可能出异常
pipe.set('role', 'nb')
pipe.execute()
# redis原生实现事务 实现事务mutil
# 1 mutil 开启事务,放到管道中一次性执行
multi # 开启事务
set name lqz
set age 18
exec
# 2 模拟实现乐观锁 watch+multi实现乐观锁
# 在开启事务之前,先watch
watch age
multi
decr age
exec
# 另一台机器
multi
decr age
exec # 先执行,上面的执行就会失败(乐观锁,被wathc的事务不会执行成功)
发布订阅
# 发布订阅是 观察者模式 :只要订阅了某个东西,这个东西发送变化,我们就能收到
发布者发布了消息,所有的订阅者都可以收到,就是生产者消费者模型(后订阅了,无法获取历史消息)
# 一个客户端发送消息
publish lqz hello # 只要有订阅者,客户端发送消息,所有订阅者都能收到
# 另外两个客户端,订阅频道,等待接收消息
subscribe lqz
# 查看某个频道有几个订阅者
pubsub numsub lqz
# 列出活跃的频道
pubsub channels
#发布订阅和消息队列
发布订阅数全收到,消息队列有个抢的过程,只有一个抢到
Bitmap位图
Bitmap位图:是字符串类型,但是以二进制形式存储的,获取,设置某个二进制位的
# set hello big
# getbit hello 0/1/2 返回比特位是0或1
# setbit hello 7 1 把第7,也就是8个比特位设置为1
# big就变成了cit
# bitcount hello 0 1 字节数 返回8
# 独立用户统计
-假设:1亿用户,5千万活跃用户 统计今天活跃用户是多选 用户iduserid是整形,32位整型
-int32 类型 4个字节表示一个数字---》 正负 2的31次方-1 的范围
1 4个字节
1001 4个字节
-方式一:登录,把id放到集合中---》统计集合大小
-方式二:登录,操作位图,把id对应的数字设为1 ,以后统计1的个数
HyperLogLog
redis中支持这种算法,基于HyperLogLog算法:极小的空间完成独立数量统计
很类似于布隆过滤器
pfadd key element # 向hyperloglog添加元素,可以同时添加多个
pfcount key #计算hyperloglog的独立总数
pfadd uuids "uuid1" "uuid2" "uuid3" "uuid4" #向uuids中添加4个uuid
pfcount uuids #返回4
#也可以做独立用户统计
GEO地理位置信息
# GEO(地理信息定位):存储经纬度,计算两地距离,范围等
-根据经纬度---》确定具体地址的---》高德开放api---》返回具体地址
# redis 可以存储经纬度,存储后可以做运算,
比如:两个经纬度之间距离 (直线距离)
比如:统计某个经纬度范围内有哪些好友,餐馆
# 经纬度如何获取
-跟后端没关系:只需要存
-app,有定位功能
-网页,集成了高德地图,定位功能
# redis存储
geoadd key 经度 纬度 名字
# 添加
geoadd cities:locations 116.28 39.55 beijing
# 查看位置信息
geopos cities:locations beijing #获取北京地理信息
#计算两个点距离
geodist cities:locations beijing tianjin km
# 计算附近的 xx
georadiusbymember cities:locations beijing 150 km
# 5大数据类型的 : 有序集合
持久化方案
# 什么是持久化
redis的所有数据保存在内存中,把内存中的数据同步到硬盘上这个过程称之为持久化
# 持久化的实现方式
快照:某时某刻数据的一个完成备份
-mysql的Dump
-redis的RDB
写日志:任何操作记录日志,要恢复数据,只要把日志重新走一遍即可
-mysql的 Binlog
-Redis的 AOF
1.1 RDB
# rdb 持久化配置方式
-方式一:通过命令 # 同步操作
save:生成rdb持久化文件
-方式二:异步持久化,不会阻塞住其他命令的执行
bgsave
-方式三:配置文件配置,这个条件触发,就执行bgsave"我们常用的"
# 写在redis.conf配置文件
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir "/root/redis-6.2.9/data"
如果60s中改变了1w条数据,自动生成rdb
如果300s中改变了10条数据,自动生成rdb
如果900s中改变了1条数据,自动生成rdb
1.2 aof方案
RDB方案可能会出现数据丢失的情况,我们还可以选择aof方案。
aof方案就是客户端每写入一条命令,都会记录一条日志,放到日志文件中,如果出现宕机,可以将数据完全恢复。
# AOF的三种策略
日志不是直接写到硬盘上,而是先放在缓冲区,缓冲区根据一些策略,写到硬盘上
always:redis写命令刷新的缓冲区,每条命令fsync同步到硬盘
everysec(默认值):redis,写命令刷新的缓冲区,每秒把缓冲区fsync到硬盘
no:redis写命令刷新的缓冲区,操作系统决定,缓冲区fsync到硬盘
# aof持久化的配置
aof命令的逐步写入和并发量的变大,aof文件会越变越大,需要通过重写aof来解决,把过期的,无用的,重复的,可以优化的命令给优化掉。可以减少磁盘的占用,并加快恢复速度
重写aof
# 配在redis-conf
auto-aof-rewrite-min-size:500m # 重写的最小大小
auto-aof-rewrite-percentage:增长率
aof持久化的配置参数
appendonly yes #将该选项设置为yes,打开
appendfilename "appendonly.aof" #文件保存的名字
appendfsync everysec # 采用第二种策略
no-appendfsync-on-rewrite yes #在aof重写的时候,是否要做aof的append操作,因为aof重写消耗性能,磁盘消耗,正常aof写磁盘有一定的冲突,这段期间的数据,允许丢失
1.3 混合持久化
可以同时的开启aof和rdb ,他们之间互相影响,redis4.0版本后才出现,解决恢复速度的问题。
开启混合持久化,AOF重写时,不是单纯的将内存数据转换成redis命令写入aof文件,而是将这一刻之前的内容做RDB快照处理。
# 配置参数:必须先开启AOF
# 开启 aof
appendonly yes
# 开启 aof复写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 开启 混合持久化 上面可以不输 appendonly要输
aof-use-rdb-preamble yes # 真正有用的是这句话
# 关闭 rdb
save ""
# aof重写可以使用配置文件触发,也可以手动触发:bgrewriteaof
主从复制原理和方案
为什么要有主从复制,redis可能会出现机器故障,容量瓶颈,与qps瓶颈,主从可以解决qbs与机器故障的问题。主要用来做读写分离
可以做数据的副本,并提高并发量。
一个主库可以有多个从库,从库只能读不能写。
# redis主从赋值流程,原理
1. 副本(从)库通过slaveof 127.0.0.1 6379命令,连接主库,并发送SYNC给主库
2. 主库收到SYNC,会立即触发BGSAVE,后台保存RDB,发送给副本库
3. 副本库接收后会应用RDB快照,load进内存
4. 主库会陆续将中间产生的新的操作,保存并发送给副本库
5. 到此,我们主复制集就正常工作了
6. 再此以后,主库只要发生新的操作,都会以命令传播的形式自动发送给副本库.
7. 所有复制相关信息,从info信息中都可以查到.即使重启任何节点,他的主从关系依然都在.
8. 如果发生主从关系断开时,从库数据没有任何损坏,在下次重连之后,从库发送PSYNC给主库
9. 主库只会将从库缺失部分的数据同步给从库应用,达到快速恢复主从的目的
# 主从同步主库是否要开启持久化?
如果不开有可能会发生主库重启操作,造成所有主从数据丢失!
# 启动两台redis服务
主从复制步骤
# 把主库的redis复制一份给从库修改
cp redis.conf redis_6380.conf
# 内容修改
daemonize yes
port 6380
dir "/root/redis-6.2.9/data"
logfile "6380.log"
appendonly yes #将该选项设置为yes,打开
appendfilename "appendonly.aof" #文件保存的名字
appendfsync everysec # 采用第二种策略
no-appendfsync-on-rewrite yes
aof-use-rdb-preamble yes # 开启混合
# 1.命令方式建立主从关系,在从库中执行
slaveof 127.0.0.1 6379
# 2 配置文件里加入,启动带配置文件
slaveof 127.0.0.1 6379
slave-read-only yes # 从节点只读,因为可读可写,数据会乱
autpass 123456
#3. 断开主从关系
slaveof no one # 在从库执行
# 辅助配置(给主库用的)
min-slaves-to-write 1
min-slaves-max-lag 3
#那么在从服务器的数量少于1个,或者三个从服务器的延迟(lag)值都大于或等于3秒时,主服务器将拒绝执行写命令
哨兵高可用
高可用就是服务可用性高,主从服务器他本身并不是高可用,主从复制存在一些问题,如果主服务发送故障,我们需要其中的一个从服务变成主服务,我们可以使用哨兵。 redis-Sentinel
# 工作原理:
1 多个sentinel发现并确认master有问题
2 选举出一个sentinel作为领导
3 选取一个slave作为新的master
4 通知其余slave成为新的master的slave
5 通知客户端主从变化
6 等待老的master复活成为新master的slave
高可用搭建步骤
第一步:先搭建一主两从
第二步:哨兵配置文件,启动哨兵
"哨兵也是一个redis进程,也要监听端口,启动进程也有配置文件"
# 配置三个哨兵
port 26379
daemonize yes
dir /root/redis/data
bind 0.0.0.0
logfile "redis_sentinel.log"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
port 26390
daemonize yes
dir /root/redis/data1
bind 0.0.0.0
logfile "redis_sentinel.log"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
port 26381
daemonize yes
dir /root/redis/data2
bind 0.0.0.0
logfile "redis_sentinel.log"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
第三步:启动三个哨兵 # 这里是在一台机器上布置了,以后可以在三台机器上布置
./src/redis-sentinel ./sentinal_26379.conf
./src/redis-sentinel ./sentinal_26380.conf
./src/redis-sentinel ./sentinal_26381.conf
第四步:停止主库,发现80变成了主库,以后79启动,变成了从库
集群原理及搭建
使用主从做了读写分离,哨兵做高可用还存在问题
1.并发量问题 单级redis qbs为10W每秒,但是我们可能需要百万级别的并发量
2.数据量问题 机器内存最大256G如果存500G数据就不行了
可以使用集群解决这个问题
数据量特别大的情况下就需要做分区,分到若干个子集中
# 主流分区方式(数据分片方式)
1.哈希分布
原理:hash分区:节点取余,假设3台机器,hash(key)%3,分配到不同节点上
节点取余分区:扩容缩容麻烦,需要移动数据。建议翻倍扩容
一致性哈希分区: 每个节点负责一部分数据,对key进行哈希,得到的结果在节点1或节点2之间,就放到节点2中,顺时针查找
还是有数据迁移情况,会导致数据偏移,节点数据不均匀,无法保证负载均衡。翻倍扩容可以实现负载均衡
虚拟槽(redis) ***
预设虚拟槽,总共16384个槽,每个槽映射一个节点。用哈希CRC16算法。
"""redis就是用的虚拟槽,对key进行hash得到数字对16383取余,就知道这个数据应该给哪个槽"""
2.顺序分布
100个数据分到3个节点上 1--33第一个节点;34--66第二个节点;67--100第三个节点(很多关系型数据库使用此种方式)
集群搭建
名词解释
节点
某一台机器
meet
节点跟节点之间通过meet通信
指派槽
16384个槽分给几个节点
复制
主从赋值操作
高可用
主节点挂掉,从节点顶上来
# 搭建步骤
1.准备6台机器 (6个redis-server进程)
2.写6个配置文件
port 7000
daemonize yes
dir "/root/redis/data/"
logfile "7000.log"
cluster-enabled yes
cluster-node-timeout 15000
cluster-config-file nodes-7000.conf
cluster-require-full-coverage yes
3.快速复制6个配置文件,并修改配置
sed 's/7000/7001/g' redis-7000.conf > redis-7001.conf
sed 's/7000/7002/g' redis-7000.conf > redis-7002.conf
sed 's/7000/7003/g' redis-7000.conf > redis-7003.conf
sed 's/7000/7004/g' redis-7000.conf > redis-7004.conf
sed 's/7000/7005/g' redis-7000.conf > redis-7005.conf
4.启动6个redis服务
./src/redis-server ./redis-7000.conf
./src/redis-server ./redis-7001.conf
./src/redis-server ./redis-7002.conf
./src/redis-server ./redis-7003.conf
./src/redis-server ./redis-7004.conf
./src/redis-server ./redis-7005.conf
查看是否开启了6个进程
5. 快速设置主从
./src/redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 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
redis-cli -p 7000 cluster info # 查看集群信息
redis-cli -p 7000 cluster nodes #查看节点信息
redis-cli -p 7000 cluster slots # 查看槽的信息
需要使用集群模式进入客户端
./src/redis-cli -p 7000 -c
集群扩容
#1.准备两台机器
sed 's/7000/7006/g' redis-7000.conf > redis-7006.conf
sed 's/7000/7007/g' redis-7000.conf > redis-7007.conf
# 2.启动两台机器
./src/redis-server ./redis-7006.conf
./src/redis-server ./redis-7007.conf
# 3 两台机器加入到集群中去
./src/redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
./src/redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000
"需要注意必须保证没有rdb文件不然会报错,如果之前设置过有过记录也会出问题,是因为我在一台机器上操作的"
# 4 让7007复制7006
./src/redis-cli -p 7007 cluster replicate baf261f2e6cb2b0359d25420b3ddc3d1b8d3bb5a
# 5 迁移槽
./src/redis-cli --cluster reshard 127.0.0.1:7000
第一步输入:迁移4096个槽
第二部输入:7006的机器接收槽(输入7006的进程id)
第三步输入:all 全自动分配
集群缩容
# 1 下线迁槽(把7006的1366个槽迁移到7000上)
redis-cli --cluster reshard --cluster-from baf261f2e6cb2b0359d25420b3ddc3d1b8d3bb5a --cluster-to 050bfd3608514d4db5d2ce5411ef5989bbe50867 --cluster-slots 1365 127.0.0.1:7000
yes
# 7007的迁到7002上
redis-cli --cluster reshard --cluster-from baf261f2e6cb2b0359d25420b3ddc3d1b8d3bb5a --cluster-to d3aea3d0b4cf90f58252cf3bcd89530943f52d36 --cluster-slots 1366 127.0.0.1:7002
yes
#2 下线节点忘记节点,关闭节点
./src/redis-cli --cluster del-node 127.0.0.1:7006 9c2abbfaa4d1fb94b74df04ce2b481512e6edbf3 # 先下从,再下主,因为先下主会触发故障转移
./src/redis-cli --cluster del-node 127.0.0.1:7007 baf261f2e6cb2b0359d25420b3ddc3d1b8d3bb5a