Redis

Redis

Redis简介及使用场景

本文仅做学习记录

简介

Redis是一个开源的使用C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的APl。
本质是客户端服务端应用软件程序。
特点是使用简单、性能强悍、功能应用场景丰富。
官网地址:Redis

使用场景

  1. 微博数,粉丝数(常规计数)
  2. 微博的关注列表,粉丝列表
  3. 实现如共同关注、共同喜好、二度好友
  4. 排行榜、按照用户投票和时间排序
  5. 存储部分变更数据,如用户信息
  6. LBS应用开发
  7. 消费者生产者场景(类似MQ)

注:在工作中,redis用于流式计算中,用来存储计算数据加快程序处理效率;记录实时数据等。

操作环境

系统DockerRedis版本
MAC2.1.0.05.0.5

Redis基本数据结构及常用命令

通用命令

命令描述
DEL KEY该命令用于在key存在时删除key
EXISTS key检查给定key是否存在
EXPIRE key seconds为给定key设置过期时间,以秒计
TTL key以秒为单位,返回给定key的剩余生存时间(TTL, time to live)
TYPE key返回key所储存的值的类型
127.0.0.1:6379> set test 123
OK
127.0.0.1:6379> EXISTS test
(integer) 1
127.0.0.1:6379> EXPIRE test 20
(integer) 1
127.0.0.1:6379> TTL test
(integer) 15
127.0.0.1:6379> type test
string

String:数据结构是简单的key-value类型,value其实不仅是String,也可以是数字。
使用场景:微博数,粉丝数(常规计数)

命令描述
Get获取指定key的值
Set设置指定key的值
Incr将key中储存的数字值增一
Decr将key中储存的数字值减一
Mget获取所有(一个或多个)给定key的值
127.0.0.1:6379> set test 0
OK
127.0.0.1:6379> get test
"0"
127.0.0.1:6379> INCR test
(integer) 1
127.0.0.1:6379> get test
"1"
127.0.0.1:6379> DECR test
(integer) 0
127.0.0.1:6379> set test1 5
OK
127.0.0.1:6379> MGET test test1
1) "0"
2) "5"

List:就是链表,相信略有数据结构知识的人都应该能理解其结构。
使用场景:微博的关注列表,粉丝列表

命令描述
Lpush将一个或多个值插入到列表头部
Rpush在列表中添加一个或多个值
Lpop移出并获取列表的第一个元素
Rpop移除列表的最后一个元素,返回值为移除的元素
Lrange获取所有(一个或多个)给定key的值
127.0.0.1:6379> LPUSH test aaa
(integer) 1
127.0.0.1:6379> RPUSH test bbb
(integer) 2
127.0.0.1:6379> LRANGE test 0 -1
1) "aaa"
2) "bbb"
127.0.0.1:6379> RPOP test
"bbb"
127.0.0.1:6379> LPOP test
"aaa"
127.0.0.1:6379> LRANGE test 0 -1
(empty list or set)

Set:就是一一个集合,集合的概念就是一堆不重复值的组合。利用Redis提供的Set数据结构,可以存储一些集合性的数据。
使用场景:实现如共同关注、共同喜好、二度好友

命令描述
Sadd向集合中添加一个或多个成员
Spop移除并返回集合中的一个随机元素
Smembers返回集合中的所有成员
Sunion返回所有给定集合的并集
127.0.0.1:6379> SADD list1 1111
(integer) 1
127.0.0.1:6379> SADD list1 2222
(integer) 1
127.0.0.1:6379> SADD list1 3333
(integer) 1
127.0.0.1:6379> SMEMBERS list1
1) "1111"
2) "2222"
3) "3333"
127.0.0.1:6379> SPOP list1 2
1) "3333"
2) "1111"
127.0.0.1:6379> SMEMBERS list1
1) "2222"
127.0.0.1:6379> SADD list2 1111
(integer) 1
127.0.0.1:6379> SADD list2 2222
(integer) 1
127.0.0.1:6379> SUNION list1 list2
1) "1111"
2) "2222"

Sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。
使用场景:排行榜、按照用户投票和时间排序

命令描述
Zadd向有序集合添加一个或多个成员,或者更新已存在成员的分数
Zrange通过索引区间返回有序集合中指定区间内的成员
Zrem移除有序集合中的一个或多个成员
Zcard获取有序集合的成员数
127.0.0.1:6379> ZADD test 10 s1
(integer) 1
127.0.0.1:6379> ZADD test 50 s2
(integer) 1
127.0.0.1:6379> ZADD test 90 s3
(integer) 1
127.0.0.1:6379> ZCARD test
(integer) 3
127.0.0.1:6379> ZRANGE test 0 -1
1) "s1"
2) "s2"
3) "s3"
127.0.0.1:6379> ZRANGE test 0 1
1) "s1"
2) "s2"
127.0.0.1:6379> ZREM test s2
(integer) 1
127.0.0.1:6379> ZRANGE test 0 -1
1) "s1"
2) "s3"

Hash
Hash是一个string类 型的field和value的映射表
使用场景:存储部分变更数据,如用户信息

命令描述
Hget获取存储在哈希表中指定字段的值
Hset将哈希表key中的字段field的值设为value
Hgetall获取在哈希表中指定key的所有字段和值
127.0.0.1:6379> HSET test field "aaaa"
(integer) 1
127.0.0.1:6379> HGET test field
"aaaa"
127.0.0.1:6379> HSET test field1 "bbbb"
(integer) 1
127.0.0.1:6379> HSET test field2 "cccc"
(integer) 1
127.0.0.1:6379> HGETALL test
1) "field"
2) "aaaa"
3) "field1"
4) "bbbb"
5) "field2"
6) "cccc"
127.0.0.1:6379> HDEL test field
(integer) 1
127.0.0.1:6379> HGETALL test
1) "field1"
2) "bbbb"
3) "field2"
4) "cccc"

GEO
GEO 3.2版本开始对GEO(地理位置)的支持
使用场景: LBS应用开发

命令描述
GEOADD增加地理位置的坐标,可以批量添加地理位置
GEODIST获取两个地理位置的距离
GEOHASH获取某个地理位置的geohash值
GEOPOS获取指定位置的坐标,可以批量获取多个地理位置的坐标
GEORADIUS根据给定地理位置坐标获取指定范围内的地理位置集合
GEORADIUSBYMEMBER根据给定成员的位置获取指定范围内的位置信息集合

(注意:该命令的中心点是由给定的位置元素决定)

127.0.0.1:6379> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
127.0.0.1:6379> GEODIST Sicily Palermo Catania
"166274.1516"
127.0.0.1:6379> GEORADIUS Sicily 15 37 100 km
1) "Catania"
127.0.0.1:6379> GEORADIUS Sicily 15 37 200 km
1) "Palermo"
2) "Catania"
127.0.0.1:6379> GEORADIUSBYMEMBER Sicily Palermo 2000 km
1) "Palermo"
2) "Catania"

Stream
Stream 5.0版本开始的新结构“流”
使用场景:消费者生产者场景(类似MQ)

命令描述
XADD往指定的流中添加消息
XLENstream流中的消息数量
XDEL删除流中的消息
XRANGE返回流中满足给定ID范围的消息
XREAD从一个或者多个流中读取消息
XINFO检索关于流和关联的消费者组的不同的信息

这种类型还没有实际使用过,没有去研究,有兴趣的可以去官网看看。
注:比较常用的几个:Hash、String、Set、List、Sorted set。

持久化

RBD持久化

RDB持久化方式能够在指定的时间间隔对你的数据进行快照存储。
客户端直接通过命令BGSAVE或者SAVE来创建一个内存快照

  • BGSAVE调用fork来创建一个子进程,子进程负责将快照写入磁盘,而父进程任然继续处理命令。
  • SAVE执行SAVE命令过程汇总,不再相应其他命令。

在redis.conf中调整save配置选项,当在规定的时间内,Redis发生了写操作的个数满足条件会触发发生BGSAVE命令。

#900 秒之内至少一次写操作
save 900 1
#300 秒之内至少发生10次写操作
save 300 10
#60 秒之内发生至少10000次
save 60 10000

RDB文件进行数据恢复比使用AOF要快很多

AOF(append only file)持久化

AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来回复原始数据。

开启AOF持久化
appendonly yes
AOF策略调整
#每次有数据修改发生时都会写入AOF文件
appendfsync always
#每秒钟同步一次,该策略为AOF的缺省策略
appendfsync everysec
#从不同步。高效但是数据不会被持久化
appendfsync no

内存分配

不同数据类型的大小限制

  • Strings类型: 一个String类型的value最大可以存储512M。
  • Lists类型: list的元素个数最多为2^32-1个,也就是4294967295个。
  • Sets类型:元素个数最多为2^32-1个,也就是4294967295个。
  • Hashes类型:键值对个数最多为2^32-1个,也就是4294967295个

最大内存控制

maxmemory最大内存阈值
maxmemory-policy到达阈值的执行策略

内存压缩

#配置字段最多512个
hash-max-zipmap-entries 512
#配置value最大为64字节
hash-max-zipmap-value 64
#配置元素个数最多512个
list-max-ziplist-entries 512
#配置value最大为64字节
list-max-ziplist-value 64
#配置元素个数最多512个
set-max-intset-entries 512
#配置元素个数最多128个
zset-max-ziplist-entries 128
#配置value最大为64字节
zset-max-ziplist-value 64
注:大小超出压缩范围,溢出后Redis将自动将其转换为正常大小

过期数据的处理策略

主动处理( redis主动触发检测key是否过期)每秒执行10次。 过程如下:
1.从具有相关过期的密钥集中测试20个随机密钥
2.删除找到的所有密钥已过期
3.如果超过25%的密钥已过期,请从步骤1重新开始
被动处理:
1.每次访问key的时候,发现超时后被动过期,清理掉
数据恢复阶段过期数据的处理策略
RDB方式:

  • 过期的可以不被持久化到文件中。
  • 载入时过期的key,会通过redis的主动和被动方式清理掉。
    AOF方式:
  • 当redis使用AOF方式持久化时,每次遇到过期的key redis会追加一条DEL命令到AOF文件,也就是说只要我们顺序载入执行AOF命令文件就会删除过期的键。
    注:过期数据的计算和计算机本身的时间是有直接联系的!

Redis内存回收策略
配置文件中设置: maxmemory-policy noeviction
动态调整: config set maxmemory-policy noeviction

回收策略说明
noeviction客户端尝试执行会让更多内存被使用的命令直接报错
allkeys-lru在所有key里执行LRU算法
volatile-lru在所有已经过期的key里执行LRU算法
volatile-lfu使用过期集在密钥中使用近似LFU进行驱逐
allkeys-lfu使用近似LFU逐出任何键
allkeys-random在所有key里随机回收
volatile-random在已经过期的key里随机回收
volatile-ttl回收已经过期的key,并且优先回收存活时间(TTL) 较短的键

LRU算法

LRU (Least recently used,最近最少使用) :根据数据的历史访问记录来进行淘汰数据

  • 核心思想:如果数据最近被访问过,那么将来被访问的几率也更高。
  • 注意: Redis的LRU算法并非完整的实现,完整的LRU实现是因为这需要太多的内存。
  • 方法:通过对少量keys进行取样(50%),然后回收其中一个最好的key。
  • 配置方式:maxmemory-samples 5

LFU算法

LFU (Least Frequently Used)根据数据的历史访问频率来淘汰数据

  • 核心思想:如果数据过去被访问多次,那么将来被访问的频率也更高。
  • Redis实现的是近似的实现,每次对key进行访问时,用基于概率的对数计数器来记录访问次数,同时这个计数器会随着时间推移而减小。
  • Morris counter算法依据:https://en.wikipedia.org/wiki/Approximate_counting_algorithm
  • 启用LFU算法后,可以使用热点数据分析功能。 ( redis-cli --hotkeys )

主从复制

为什么要使用主从复制?
  • redis-server单点故障
  • 单节点QPS有限

主从复制应用场景分析

  • 读写分离场景,规避redis单机瓶颈
  • 故障切换,master出现问题后还有slave节点可以使用

搭建主从复制

第一种方式:命令行
#连接需要实现从节点的redis,执行下面的命令
slaveof [ip] [port]
第二种方式:redis.conf配置文件
#配置文件中增加
slaveof [ip] [port]   新版本:replicationof
#从服务器是否只读(默认yes)
slave-read-only yes
退出主从集群的方式
Slaveof no one
主从复制流程
  1. 从服务器通过psync命令发送服务器已有的同步进度(同步源ID、同步进度offset)
  2. master收到请求,同步源为当前master,则根据偏移量增量同步
  3. 同步源非当前master,则进入全量同步: master生 成rdb,传输到slave,加载到slave内存
    在这里插入图片描述
主从复制核心知识
  • Redis默认使用异步复制,slave和master之间异步地确认处理的数据量。
  • 一个master可以拥有多个slave。
  • slave可以接受其他slave的连接。slave可以有sub slave。
  • 主从同步过程在master侧是非阻塞的。
  • slave初次同步需要删除旧数据,加载新数据,会阻塞到来的连接请求。
主从复制应用场景
  • 主从复制可以用来支持读写分离。
  • slave服务器设定为只读,可以用在数据安全的场景下。
  • 可以使用主从复制来避免master持久化造成的开销。master关闭持久化,slave配置为不定期保存或是启用AOF。(注意:重新启动的master程序将从一个空数据集开始,如果一个slave试图与它同步,那么这个slave也会被清空。)
主从复制的注意事项

读写分离场景:

  • 数据复制延时导致读到过期数据或者读不到数据(网络原因、slave阻塞)
  • 从节点故障(多个client如何迁移)

全量复制情况下:

  • 第一次建立主从关系或者runid不匹配会导致全量复制
  • 故障转移的时候也会出现全量复制

复制风暴:

  • master故障重启,如果slave节点较多,所有slave都要复制,对服务器的性能,网络的压力都会有很大的影响。
  • 如果一个机器部署了多个master

写能力有限:

  • 主从复制还是只有一台master,提供的写服务能力有限。

master故障情况下:

  • 如果是master无持久化,slave开启持久化来保留数据的场景,建议不要配置redis自动重启。
  • 启动redis自动重启,master启动后,无备份数据,可能导致集群数据丢失的情况。

带有效期的key:

  • slave不会让key过期,而是等待master让key过期。
  • 在Lua脚本执行期间,不执行任何key过期操作。

Redis集群分片存储

1、客户端发送数据到redis集群中的时候,redis服务器端会利用crc16算法计算hash数据,如果请求的redis服务器不是目标服务器,那么会重定向到目标服务器,找到相应的slot(槽、位置)。——存在问题:如果说发送错误的次数过多,那么性能会下降。
解决办法:可以在发送命令之前,客户端进行slot计算,根据slot分配信息定位到目标服务器,如果说redis服务器返回重定向消息的时候,那么就说明slot分配信息改变,需要重新刷新缓存信息。
注:slot并不是存储数据的,而是作为片区的划分
在这里插入图片描述

集群关心的问题

  1. 增加了slot槽的计算,是不是比单机性能差?
    共16384个槽, slots槽计算方式公开的,HASH_ SLOT = CRC16(key) mod 16384。
    为了避免每次都需要服务器计算重定向,优秀的java客户端都实现了本地计算,并且缓存服务器slots分配,有变动时再更新本地内容,从而避免了多次重定向带来的性能损耗。(结合画图过程理解)

  2. redis集群大小,到底可以装多少数据?
    理论是可以做到16384个槽,每个槽对应一个实例,但是redis官方建议是最大1000个实例。存储足够大了。

  3. 集群节点间是怎么通信的?
    每个Redis群集节点都有一个额外的TCP端口,每个节点使用TCP连接与每个其他节点连接。检测和故障转移这些步骤基本和哨兵模式类似(毕竟是同一个软件,同一个作者设计)。

  4. ask和moved重 定向的区别
    重定向包括两种情况:

    • 若确定slot不属于当前节点,redis会 返回moved。
    • 若当前redis节点正在处理slot迁移,则代表此处请求对应的key暂时不在此节点,返回ask,告诉客户端本次请求重定向。
  5. 数据倾斜和访问倾斜的问题
    倾斜导致集群中部分节点数据多,压力大。解决方案分为前期和后期:

    • 前期是业务层面提前预测,哪些key是热点,在设计的过程中规避。
    • 后期是slot迁移,尽量将压力分摊(slot调整有自动rebalance、reshard和手动) 。
  6. slot手动迁移怎么做?
    迁移过程如下,大致描述如下:

    • 在迁移目的节点执行cluster setslot IMPORTING 命令,指明需要迁移的slot和迁移源节点。
    • 在迁移源节点执行cluster setslot MIGRATING <node |D>命令,指明需要迁移的slot和迁移目的节点。
    • 在迁移源节点执行cluster getkeysinslot获取该slot的key列表。
    • 在迁移源节点执行对每个key执行migrate命令,该命令会同步把该key迁移到目的节点。.
    • 在迁移源节点反复执行cluster getkeysinslot命令,直到该slot的列表为空。
    • 在迁移源节点和目的节点执行cluster setslot NODE ,完成迁移操作。
  7. 节点之间会交换信息,传递的消息包括槽的信息,带来带宽消耗。
    注意:避免使用大的一个集群,可以分多个集群。

  8. Pub/Sub发布订 阅机制
    注意:对集群内任意的一-个节点执行publish发布消息,这个消息会在集群中进行传播,其他节点接收到发布的消息。

  9. 读写分离

    • redis-cluster默认所有从节点上的读写,都会重定向到key对接槽的主节点上。
    • 可以通过readonly设置当前连接可读,通过readwrite取消当前连接的可读状态。

注意:主从节点依然存在数据不一致的问题

未完待续…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值