redis 哈希,集合,有序集合,持久化方案,主从复制,高可用,集群搭建扩容缩容

发布时间 2023-04-21 21:36:00作者: 李阿鸡

哈希类型操作方法

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