• A+

Redis基础 常用类型 时间复杂度

https://blog.csdn.net/zzm848166546/article/details/80360665 原文链接

redis 一个开源的,基于内存的结构化数据存储媒介,可以作为数据库,缓存,消息队列等。
数据类型 有String hash list set sortedset 位图,hyperloglogs等 。

Redis 主要的功能都是基于单线程模型实现的,它是使用一个线程来服务所有的客户端的请求,同事 redis 也采用了 非阻塞式IO,并精细化地优化各种命令的算法时间复杂度,这些说明了什么嗯?

  • redis 是线程安全的,因为只有一个线程,其所有的操作都是原子性的,不会因为并发产生数据异常
  • reids 的速度非常快,因为使用非阻塞IO ,IO可以多路复用,并且大部分的命令的算法时间复杂度都是O(1)
  • 注意 使用高耗时的命令是非常危险的,这会占用唯一的一个线程的大量处理时间,导致所有的请求都被拖慢,比如说时间复杂度为O(n) keys 这个命令 ,是严格禁止在生成环境下使用的。

关于 key 的 一些注意事项

  • 不要使用过长的 key ,过长的 key 消耗内存不说,还会让查询的效率降低
  • key可读性也要注意,不能太短,也不能随意 设置key
  • 最好使用同一规范来设计 key,比如 " object-type🆔attr" ,这样设计出来的key 可能是 “ user:10001”,“commnet🔢reply-to”
  • redis 允许的最大 key 长度是 512 MB,value 的长度限制也是 512MB。

String
String 是 Redis 的基础数据类型,redis 没有 int Float ,Boolean 等数据类型的概念,所有的基本类型在Redis 都可以通过String 体现
String的 常用命令

  • set 为一个 key 设置 value,可以配合 ex/px 参数指定key 的有效期,通过nx/xx 参数对key 是否存在的情况进行区别操作,时间复杂度为 O(1)
  • get 获取 key 对应的 value 时间复杂度为 O(1)
  • getset 为一个key 设置value 并返回key 的原 value ,时间复杂度为 O(1)
  • mset 为 多个 key 设置 value ,时间复杂度为 O(n)
  • msetnx 同 mset ,如果指定的key 中有任意一个已经存在,则不进行任何操作,时间复杂度为 O(n)

List
redis 的 list 是链表型的数据结构,可以使用 lpush、rpush、lpop、rpop 等命令在List 的两端执行插入和移除元素操作, List 而已支持在特定的 index 上插入和读取元素的功能,但是其时间复杂度就比较高里的,谨慎使用。

  • lpush 向指定 List 的左侧(也就是头部)插入一个或者多个元素,返回插入一户list 的长度,时间复杂度为 O(n),n 为插入元素的数量
  • rpush 向 List 的的右侧,也就是尾部,插入一个或者多个元素
  • lpop 是从 List 的头部移除一个元素,就是取最后面添加的元素,并返回该元素,时间复杂度为 O(1)
  • rpop 从 List 的尾部移除一个元素并返回
  • lpushx、rpushx 和 lpush rpush 类似,区别就是,如果 操作的 key 不存在,则不会进行任务号的操作
  • llen 是返回 指定list 的长度,时间复杂度为 O(1)
  • lrange 返回指定 list 中 指定范围的元素,时间复杂度为 O(n), 这个命令要控制获取 list 元素的个数,太大会导致 redis 延迟响应,另外对于不可预知长度的list 禁止 lrange key 0 -1 这样的遍历操作。
  • 注意 谨慎使用 命令 lindex 返回指定的下标的元素,如果下标越界 返回 nil, index 数值是回环的,也就是说 -1 代表最后一个位置,-2 代表倒水第二个位置,该命令的时间复杂度为 O(n) ; lset 命令将指定 list 指定 index 的元素设置为 value ,如果下标越界,则返回错误,其时间复杂度为 O(n),操作 首尾的话 复杂度为 O(1); linsert 向指定 List 中指定元素之前或者之后添加一个新元素,并返回操作后的list长度,如果指定的元素不存在,返回 -1,如果指定的 key 不存在,则不进行任何操作 ,时间复杂度也为 O(n)

Hash
哈希表,redis 的hash 和传统的 hash 一样,是一种 field-value 的数据结构,Hash 适合用于表现对象类型的数据, hash 中的 field 对应 对象的 field 即可。
Hash 的优点包括

  1. 可以实现二元查询
  2. 比起将整个对象序列化后作为String 存储,hash 能够有效的叫声网络传输的消耗
  3. 当使用 hash 维护一个集合的时候,提供了比 List 效率更高的随机访问命令

与Hash 相关常用的命令

  • hset 将key 对应的 Hash 中的 field 设置为 value ,如果该 hash 不存在,则会自动创建一个 时间复杂度为 O(1)
  • hget 返回指定Hash 中 field 字段的值,时间复杂度为 O(1)
  • hmset/hmget 和 hset 和 hget 类似,可以批量操作同一个key 下的多个 field,jedis 中你可以 使用一个key 存储一个 map对象进去,其时间复杂度是 O(n),n为一次操作的 field 的数量
  • hsetnx 通 hset 如果 field 存在,则不会进行任何的操作,O(1) 复杂度
  • hexists 是否存在 field,存在返回 -1 ,不存在返回0 O(1)复杂度
  • hdel 删除指定 hash 中的 field (一个或者 多个),时间复杂度为O(n),n为操作的 field 数量
  • hincrby 同 incrby ,对指定Hash 中的 field 进行 incrby 操作,O(1) 复杂度
    需要谨慎使用的命令
  • hgetall 返回指定hash中所有的 field-value,返回结果是数组,数组中field 和value 交替出现,O(n) 复杂度
  • hkeys/hvals 返回指定 Hash 中 所有的 field/value O(n) 复杂度。
    上面 3 个命令都会将 hahs 整个遍历,尽量使用 hscan 进行游标式遍历。

Set
redis set 是无序的,不可重复的String 集合
与 set 相关常用命令

  • sadd 向指定的 set 中添加一个或者多个 member ,如果指定的set 不存在,自会自动创建一个,时间复杂度为 O(n),n 为添加的个数
  • srem 从指定的 set 移除 1 个或者多个 member,时间复杂度为O(n), n为返回的member个数
  • srandmember 从指定的set 随机返回 1 个或者多个 member ,时间复杂度为 O(n), n为返回的member 的个数
  • spop 从指定的set 移除并返回 n个 member 时间复杂度为 O(n),n为移除的member的个数
  • scard 返回指定 set 中 member的格式 ,时间复杂度为O(1)
  • sismember 判断指定的 value 是否存在于指定的 set 中,时间复杂度为O(1)
  • smove 将指定的member 从一个 set 移动到另外的 set

慎用的set 命令

smembers 返回指定 hash 中所有的 member ,O(n)
sunion / sunionstore 计算多个 set 的并集并返回/存储到另外一个 set 中,O(n)
sinter/sinterstore 计算多个set 的交集并返回/ 存储至另外一个set 中,O(n)
sdiff / sdiffstore  计算 1 个 set 与 1 个或者多个 set 的并集并返回 
存储到另外的 set 中,O(n)
  • 1
  • 2
  • 3
  • 4
  • 5

上面的几个命令计算量大,特别是在 set 尺寸不可知的情况下,避免使用,可以考虑 sscan 命令遍历获取相关的 set 的全部 member 。

其他命令

 exists 判断是否存在某个 key  存在 返回 1,不存在 返回 0  O(1)
 del 删除指定 key 及其对应的  value ,时间复杂度为 O(n) , n 为删除 key 的数量
 expire/pexpire 为一个 key 设置有效期,单位为 秒 或者 毫秒,时间复杂度为 O(1)
 TTL/PTTL  返回一个 key 的有效时间,单位为秒或者毫秒。O(1)
 rename  /  renamenx 将 key 重命名为 newkey,使用 rename 时候,如果 newkey 已经
 存在其值会被覆盖 ,使用 renamenx 时,如果 newkey 已经存在,则不会进行任何的操作。O(1)
 type 返回 key 的类型,O(1)
 config get  获得 redis 某配置项当前的值,可以使用 * 通配符 O(1)
 config set 为 redis 某配置项设置新值,时间复杂度为 O(1)
 config rewrite 让 redis 重新加载 redis.conf 的配置 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

数据持久化
redis 提供了将数据定期自动持久化至硬盘的能力, 包括 RDB 和 AOF 两种方案,两种方案分别有其长处和短板,可以配合起来同时执行,确保数据的稳定性。

必须使用数据持久化吗
redis 数据持久化是可以关闭的。如果仅仅当做缓存来用,可以关闭,毕竟这里的树都是其他拷贝
但通常来说,任然建议至少开始 RDB 方式的数据持久化,因为

  • RDB 几乎不损耗redis的性能, rdb 进行持久化的时候, redis 主进程唯一需要做得事情是 fork 出一个子进程 ,所有的 持久化工作都是有子进程完成
  • redis 无论什么原因挂掉之后,重启事能自动恢复到上一次RDB快照中记录的数据,这省去了手工从其他数据源同步数据的过程,二期要比其他任何的数据恢复方式都要快

RDB
采用RDB 的持久化方式,redis 会定期保存数据快照到一个 rdb 文件中,并在启动时自动加载 rdb文件,恢复之前保存的数据,可以在 redis.conf 中配置 redis 进行快照保存的时机

save [ seconds ] [changes]
意思是在 seconds 秒内如果发生了 changes 次数据修改,则进行一次 RDB 快照保存 
eg.  save 60 100
  • 1
  • 2
  • 3

在 conf 文件中 可以配置多条 save 策略

save 900 1 
save 300 10 
save 60 10000
  • 1
  • 2
  • 3

还可以通过手工 bgsave 命令触发 rdb 快照保存

下面说说 rdb 的优点

  • 对性能影响小 子进程 操作 rdb
  • 每次生成一个完整的 rdb 文件,作为非常可靠的灾难恢复手段
  • rdb文件恢复数据比 aof 快很多

RDB 缺点是

  • 快照是定期生成的,所以可能干好在 两次 rdb 之间 redis 挂了,那么这个自从上一次到这一次之间的数据,或多或少地会丢失部分数据
  • 如果数据集非常的大 而且 CPU 不够强,Redis 是 fork 子进程时可能会消耗相对较长的时间,因此影响这期间客户端的请求。

AOF
采用 aof 持久化方式,redis 会把每一次的写请求都放在一个日志文件里,redis 重启的时候,会把 aof 文件中的记录的所有的写操作顺序的执行一遍,确保数据恢复到最新
aof 默认是关闭的,需要开启进行如下配置

appendonly yes
  • 1

aof 提供了 3 中 fsync 配置 always 、everysec 、no

appendfsync no 不进行 fsync 将 flush 文件的时机交给 os 决定,速度最快
appendfsync always 每写入一条日志就进行一次的 fsync 操作,数据安全性最高,单数速度最慢
appendfsync 折中的做法,交给后台线程每秒 fsync 一次
  • 1
  • 2
  • 3

随着 aof 不断的记录写操作日子,必定会出现一些无用的日志,例如某个时间点执行了 set key1 “abc” ,在此之后又执行了 set key1 “bcd”,那么第一条命令显然是没有用的,大量的无用日志会使得 aof 文件过大,也会让数据恢复的时间加长。基于这种情况,redis 提供了 aof rewrite 功能,只保留能够把书恢复到最新状态的最小写操作集。
aof rewrite 命令 可以通过 bgrewriteaof 命令触发,也可以通过 redis 定期自动执行。

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min--size 64mb

两行的含义是  reids 在每次 aof  rewrite 时,会记录rewrite 后 aof 的大小,当 aof 日志
	在该基础上增长了 100% 后,自动进行 aof rewrite 操作,同时如果增长的大小没有达到 64mb
	 则不胡 rewrite
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

aof 的优点

  • 安全性很高, appendfsync always 时候,任何写入的数据都不会丢失,但是这个策略一般不会使用,太慢了
  • aof 在发生断电的时候也不会损坏,即使出现某条日志写入一半的情况,也可以使用 redis-check-aof 工具轻松修复
  • aof 文件易读

aof 的缺点

  • aof 的文件通常都比 rdb 文件大
  • 性能消耗比 rdb 高
  • 数据恢复比 rdb 慢

内存管理与数据淘汰机制

最大内存的设置
默认情况下 32 位 OS 中, redis 最大使用 3 GB 内存, 64 位 中没有限制
使用 redis 时候,要对存储的数据大小有一个预估,并为其设置最大的使用内存,否则 64 位 OS 中 redis 会无限制地占用内存。

maxmemory 100mb
通过这个参数配置 redis 使用的最大内存
所属分类:问答

全部评论: 0

    我有话说:
    ×