当前位置 : 首页 » 文章分类 :  开发  »  Redis-命令与数据类型

Redis-命令与数据类型

Redis 相关笔记

redis官方命令手册
https://redis.io/commands

redis官方文档
https://redis.io/documentation

Redis 命令参考
http://doc.redisfans.com/index.html

《Redis 设计与实现》(第一版)
https://redisbook.readthedocs.io/en/latest/index.html

Redis中文官网 - redis文档中心
http://www.redis.cn/documentation.html

redis中文网 - redis教程
http://www.redis.net.cn/tutorial/3501.html

《今天面试了吗》-Redis
https://juejin.im/post/5dccf260f265da0bf66b626d


Connection 连接

SELECT index 选择数据库

切换到指定的数据库,数据库索引号 index 用数字值指定,以 0 作为起始索引值。
默认使用 0 号数据库。

Server 服务器

info 查看全部服务器信息

INFO [section] 返回 Redis 服务器的各种信息和统计数值。
通过给定可选的参数 section ,可以让命令只返回某一部分的信息,例如 info server 只返回 server section 信息

各 section 主要内容

  • server : 一般 Redis 服务器信息
  • redis_version : Redis 服务器版本
  • os : Redis 服务器的宿主操作系统
  • multiplexing_api : Redis 所使用的事件处理机制
  • clients : 已连接客户端信息
  • connected_clients : 已连接客户端的数量(不包括通过从属服务器连接的客户端)
  • memory : 内存信息
  • used_memory : 由 Redis 分配器分配的内存总量,以字节(byte)为单位
  • used_memory_human : 以人类可读的格式返回 Redis 分配的内存总量
  • used_memory_rss : 从操作系统的角度,返回 Redis 已分配的内存总量(俗称常驻集大小)。这个值和 top 、 ps 等命令的输出一致。
  • persistence : RDB 和 AOF 的相关信息
  • stats : 一般统计信息
  • keyspace_hits: 命中次数
  • keyspace_misses: miss 次数
  • replication : 主/从复制信息
  • role: 当前节点的角色 master/slave
  • cpu : CPU 计算量统计信息
  • commandstats : Redis 命令统计信息
  • cluster : Redis 集群信息
  • keyspace : 数据库相关的统计信息
> info
# Server
redis_version:3.2.10
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:7be5ac88fa8b3573
redis_mode:cluster
os:Linux 4.14.0_1-0-0-15 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:51244
run_id:027f765ddca6f9b6ad6f74ba0470ffbf7d847e99
tcp_port:8479
uptime_in_seconds:1695642
uptime_in_days:19
hz:10
lru_clock:4402423
executable:/home/aicu-tob/software/redis-cluster-new/./bin/redis-server
config_file:/home/aicu-tob/software/redis-cluster-new/./conf/8479/redis.conf

# Clients
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0

# Memory
used_memory:1312392
used_memory_human:1.25M
used_memory_rss:4808704
used_memory_rss_human:4.59M
used_memory_peak:1403184
used_memory_peak_human:1.34M
total_system_memory:201150214144
total_system_memory_human:187.34G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:3.66
mem_allocator:jemalloc-4.0.3

# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1597824601
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:1
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_current_size:165818
aof_base_size:0
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0

# Stats
total_connections_received:3031
total_commands_processed:6116
instantaneous_ops_per_sec:0
total_net_input_bytes:318050
total_net_output_bytes:7752048
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:169
evicted_keys:0
keyspace_hits:1440
keyspace_misses:266
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:350
migrate_cached_sockets:0

# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

# CPU
used_cpu_sys:715.56
used_cpu_user:381.94
used_cpu_sys_children:0.22
used_cpu_user_children:0.04

# Cluster
cluster_enabled:1

# Keyspace
db0:keys=2,expires=0,avg_ttl=0

info memory 查看内存信息

127.0.0.1:6379> info memory
# Memory
used_memory:7770256
used_memory_human:7.41M
used_memory_rss:16969728
used_memory_rss_human:16.18M
used_memory_peak:13477064
used_memory_peak_human:12.85M
used_memory_peak_perc:57.66%
used_memory_overhead:6647776
used_memory_startup:1470528
used_memory_dataset:1122480
used_memory_dataset_perc:17.82%
allocator_allocated:7858200
allocator_active:9142272
allocator_resident:15388672
total_system_memory:270359752704
total_system_memory_human:251.79G
used_memory_lua:41984
used_memory_lua_human:41.00K
used_memory_scripts:2024
used_memory_scripts_human:1.98K
number_of_cached_scripts:6
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.16
allocator_frag_bytes:1284072
allocator_rss_ratio:1.68
allocator_rss_bytes:6246400
rss_overhead_ratio:1.10
rss_overhead_bytes:1581056
mem_fragmentation_ratio:2.20
mem_fragmentation_bytes:9240496
mem_not_counted_for_evict:0
mem_replication_backlog:1048576
mem_clients_slaves:20512
mem_clients_normal:4099928
mem_aof_buffer:0
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0
lazyfreed_objects:0

查看redis缓存lua脚本个数及内存占用

used_memory_scripts_human Redis缓存Lua脚本所占用的内存
number_of_cached_scripts 缓存脚本个数

Redis 支持两种方式调用Lua脚本, 一种是通过 EVAL script numkeys key [key ...] arg [arg ...] 在命令中直接将Lua脚本当做参数专递给Redis执行

但是由于考虑到Lua脚本本身可能体积较大, 如果每次调用同一个Lua脚本都要重新将该脚本原封不动的传递给Redis一次, 不仅给网络带宽带来了一定的开销, 也会影响Redis的性能, Redis支持另外一种使用Lua的方法, 先调用 SCRIPT LOAD script 将Lua脚本加载到Redis服务内部, 并且会返回给客户端一个跟该Lua向关联的Sha1码, 下次调用该Lua脚本的时候, 只需通过 EVALSHA sha1 numkeys key [key ...] arg [arg ...] 命令, 将Sha1码当做参数进行传递即可.

使用 EVALSHA 命令直接通过sha1调用相应Lua脚本的前提是我们必须将Lua脚本缓存在Redis服务内部, Redis使用 f-sha1 作为键, Lua脚本作为值, 将其存放在 server.lua_scripts 字典内部, 方便客户直接使用sha1进行查找。

可以触发缓存 Lua 脚本的 EVAL 命令和 SCRIPT LOAD 命令 使用的内存并不受 maxmemory 限制,如果用户滥用Lua脚本, 可能会造成Redis的内存无法限制的问题。

滥用Lua导致Redis内存无法被限制
https://axlgrep.github.io/tech/redis-memory-control.html


Key 键

keys pattern 模糊查找key

KEYS pattern

redis中允许模糊查询的有3个通配符,分别是:*, ?, []
*:通配任意多个字符
?:通配单个字符
[]:通配括号内的某一个字符

查找所有符合给定模式 pattern 的 key 。
KEYS * 匹配数据库中所有 key 。
KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
KEYS h*llo 匹配 hllo 和 heeeeello 等。
KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。

模糊查找keys并打印到一行

利用 xargs 命令处理 keys 命令结果:

redis-cli -h host keys "uds-af-*" | xargs

注意要在远程连接redis的linux命令行中执行,不要登录到redis命令行执行。

模糊匹配1号数据库的keys

redis-cli -h host -n 1 keys "uds-af-*"
redis-cli -h host -p 8379 -a 123 -n 1 keys "*" 列出 1 号数据库的全部key
redis-cli -h host -p 8379 -a 123 -n 1 keys "*"|xargs redis-cli -h host -p 8379 -a 123 -n 1 del 列出1号数据库的全部key并都删除

统计模糊匹配keys的个数

利用 wc 命令统计 keys 命令结果的行数:

redis-cli -h host keys "uds-af-*" | wc -l

注意要在远程连接redis的linux命令行中执行,不要登录到redis命令行执行。

模糊匹配keys并get值

String类型:

redis-cli -h redis-host keys "uds-af-*" | xargs redis-cli -h redis-host mget

批量删除模糊匹配的keys

在可远程连接redis的跳板机或本地机器上执行:

redis-cli -h redis-host keys "uds-af-*" | xargs redis-cli -h redis-host del

先通过redis客户端执行keys命令,模糊搜索出匹配的key,通过xargs命令,将结果整理为空格分隔,作为后面redis的del命令的输入。
注意要在远程连接redis的linux命令行中执行,不要登录到redis命令行执行。

生产系统禁止使用keys命令

Redis是单线程的!!!
单线程意味着任何一条命令的执行都是串行的,也就是按顺序一条一条的执行。
那么当你执行的命令耗时就会导致后续的Redis访问都会阻塞。

使用scan代替keys

SCAN cursor [MATCH pattern] [COUNT count]
scan 命令用来分批次扫描Redis记录,保证Redis不会因为耗时导致服务不可用。

scan 第一个参数是游标,表示从游标开始
例如
从下标 0 开始 匹配 user-pre 开头的 key,每次返回10个。
scan 0 match user-pre* count 10

scan 851968 match uds-a* count 10
294912
uds-a-336853226

scan 第一个参数是游标,表示从游标开始
返回的第一行是游标,第二行是匹配到的数据,
如果第一行返回0,表示没有更多数据,否则下次使用scan时,就要用第一行返回的值作为scan的游标

每次返回的,并不一定是 count 个,但只要命令返回的游标不是 0 , 应用程序就不应该将迭代视作结束。

SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。


del key key1 key2 删除一个或多个key

DEL key [key …]
删除给定的一个或多个 key。
不存在的 key 会被忽略。
返回值: 被删除 key 的数量。

expire key seconds 设置key的过期时间为seconds秒

expire key seconds 设置key的有效时间 单位为秒

pexpire key milliseconds 设置毫秒过期时间

这个命令和 EXPIRE 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间,而不像 EXPIRE 命令那样,以秒为单位。

ttl key 返回key的生存时间(秒)

TTL key
以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。

返回值:
当 key 不存在时,返回 -2 。
当 key 存在但没有设置剩余生存时间时,返回 -1 。
否则,以秒为单位,返回 key 的剩余生存时间。

persist key 将key设置为永久有效

PERSIST key
移除给定 key 的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过期的 key )。
设置有时效性的key为持久key

type key 查看key的类型

TYPE key
返回 key 所储存的值的类型。

可用版本:>= 1.0.0
时间复杂度:O(1)

返回值:
none (key不存在)
string (字符串)
list (列表)
set (集合)
zset (有序集)
hash (哈希表)


String 字符串

get key

返回 key 所关联的字符串值。
如果 key 不存在那么返回特殊值 nil 。
假如 key 储存的值不是字符串类型,返回一个错误,因为 GET 只能用于处理字符串值。

mget key [key ...]

返回所有(一个或多个)给定 key 的值。
如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。

set key value [EX seconds] [PX mills] [NX|XX]

https://redis.io/commands/set

set key value [EX seconds] [PX milliseconds] [NX|XX]
将字符串值 value 关联到 key 。
如果 key 已经持有其他值, SET 就覆写旧值,无视类型。
对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。

可选参数:
从 Redis 2.6.12 版本开始, SET 命令的行为可以通过一系列参数来修改:
EX second 设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
PX millisecond 设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
NX 只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
XX 只在键已经存在时,才对键进行设置操作。
因为 SET 命令可以通过参数来实现和 SETNX, SETEX, PSETEX 三个命令同等的效果,所以将来的 Redis 版本可能会废弃并最终移除这三个命令。

KEEPTTL 保留 key 原来的过期时间

GET 若key已存在返回旧值,否则返回 nil
EXAT timestamp-seconds 在指定的时间戳(秒)过期
PXAT timestamp-milliseconds 在指定的时间戳(毫秒)过期

返回值:
在 Redis 2.6.12 版本以前, SET 命令总是返回 OK 。
从 Redis 2.6.12 版本开始, SET 在设置操作成功完成时,才返回 OK 。
如果设置了 NX 或者 XX ,但因为条件没达到而造成设置操作未执行,那么命令返回空批量回复(NULL Bulk Reply)。

版本历史:
2.6.12 开始,增加 EX, PX, NX, XX 选项
6.0 开始,增加 KEEPTTL 选项
6.2 开始,增加 GET, EXAT, PXAT 选项

setex key seconds value

将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。

如果 key 已经存在, SETEX 命令将覆写旧值。

这个命令类似于以下两个命令:
SET key value
EXPIRE key seconds # 设置生存时间
不同之处是, SETEX 是一个原子性(atomic)操作,关联值和设置生存时间两个动作会在同一时间内完成,该命令在 Redis 用作缓存时,非常实用。

返回值:
设置成功时返回 OK 。
当 seconds 参数不合法时,返回一个错误。

setnx key value

SETNX key value

将 key 的值设为 value ,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。

SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

返回值:
设置成功,返回 1 。
设置失败,返回 0 。

mset key value [key value ...]

同时设置一个或多个 key-value 对。
如果某个给定 key 已经存在,那么 MSET 会用新值覆盖原来的旧值,如果这不是你所希望的效果,请考虑使用 MSETNX 命令:它只会在所有给定 key 都不存在的情况下进行设置操作。
MSET 是一个原子性(atomic)操作,所有给定 key 都会在同一时间内被设置,某些给定 key 被更新而另一些给定 key 没有改变的情况,不可能发生。

incr key 增加1

将 key 中储存的数字值增一。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
本操作的值限制在 64 位(bit)有符号数字表示之内。

这是一个针对字符串的操作,因为 Redis 没有专用的整数类型,所以 key 内储存的字符串被解释为十进制 64 位有符号整数来执行 INCR 操作。

可用版本:>= 1.0.0
时间复杂度: O(1)
返回值: 执行 INCR 命令之后 key 的值。

http://doc.redisfans.com/string/incr.html

Redis原子计数器incr,防止并发请求
https://blog.csdn.net/Roy_70/article/details/78260826

incr/decr key是原子的

Redis所有单个命令的执行都是原子性的,这与它的单线程机制有关;
Redis命令的原子性使得我们不用考虑并发问题,可以方便的利用原子性自增操作INCR实现简单计数器功能;

即使服务是多机器多进程的,incr/decr 也能保证每次返回的结果不会出现相同的值.

incrby key n 增加n

incrby key increment
将 key 所储存的值加上增量 increment 。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
本操作的值限制在 64 位(bit)有符号数字表示之内。

可用版本:>= 1.0.0
时间复杂度:O(1)
返回值:加上 increment 之后, key 的值。

decr key

将 key 中储存的数字值减一。

如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。

如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

本操作的值限制在 64 位(bit)有符号数字表示之内。

setbit key offset value 设置指定偏移量的bit

SETBIT key offset value

对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。
位的设置或清除取决于 value 参数,可以是 0 也可以是 1 。
当 key 不存在时,自动生成一个新的字符串值。
字符串会进行伸展(grown)以确保它可以将 value 保存在指定的偏移量上。当字符串值进行伸展时,空白位置以 0 填充。
offset 参数必须大于或等于 0 ,小于 2^32 (bit 映射被限制在 512 MB 之内)。

对使用大的 offset 的 SETBIT 操作来说,内存分配可能造成 Redis 服务器被阻塞。具体参考 SETRANGE 命令,warning(警告)部分。

可用版本:>= 2.2.0
时间复杂度: O(1)
返回值:指定偏移量原来储存的位。

getbit key offset 获取指定偏移量的bit

对 key 所储存的字符串值,获取指定偏移量上的位(bit)。
当 offset 比字符串值的长度大,或者 key 不存在时,返回 0 。

bitmap的空间占用

Redis 其实只支持 5 种数据类型,并没有 BitMap 这种类型,BitMap 底层是基于 Redis 的字符串类型实现的。

假如 BitMap 偏移量的最大值是 OFFSET_MAX, 那么它底层占用的空间就是:
(OFFSET_MAX/8)+1 = 占用字节数

因为字符串内存只能以字节分配,所以上面的单位是字节。

但是需要注意,Redis 中字符串的最大长度是 512M,所以 BitMap 的 offset 值也是有上限的,其最大值是:
8 * 1024 * 1024 * 512 = 2^32

由于 C语言中字符串的末尾都要存储一位分隔符,所以实际上 BitMap 的 offset 值上限是:
(8 * 1024 * 1024 * 512) -1 = 2^32 - 1

Redis中bitmap的妙用

Redis中bitmap的妙用
https://segmentfault.com/a/1190000008188655


strlen

STRLEN key
返回 key 所储存的字符串值的长度。
当 key 储存的不是字符串值时,返回一个错误。

可用版本:>= 2.2.0
复杂度:O(1)
返回值:字符串值的长度。
当 key 不存在时,返回 0 。


APPEND key value

如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。
如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。

可用版本: >= 2.0.0
时间复杂度: 平摊O(1)
返回值: 追加 value 之后, key 中字符串的长度。


Hash 哈希表

hget key field 返回哈希表key中的field字段值

HGET key field
返回哈希表 key 中给定域 field 的值。

hset key field value 设置哈希表key中的field值

将哈希表 key 中的域 field 的值设为 value 。
如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。
如果域 field 已经存在于哈希表中,旧值将被覆盖。

hmget key field field2 返回哈希表key中多个字段值

HMGET key field [field …]
返回哈希表 key 中,一个或多个给定域的值。
如果给定的域不存在于哈希表,那么返回一个 nil 值。
因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。

hgetall key 返回哈希表key的所有字段和值

HGETALL key
返回哈希表 key 中,所有的域和值。
在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。


List 列表(双向链表,有序)

列表(list)类型是用来存储多个字符串,元素从左到右组成一个有序的集合.列表中的每个字符串被称为元素(element),一个列表最多可以存储(2的32次方)-1个元素.
在redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定所有下标的元素等.

列表类型有两个特点:
1、列表中的元素是有序的,这就意味着可以通过索引下标获取某个元素或者某个范围内的元素列表.
2、列表中的元素可以是重复的.

插入

从右边插入元素.  rpush key value [value…]
从左边插入元素.  lpush key value [value….]  使用方法与rpush一样,从左侧插入.   

查询

(2) 获取列表指定索引下的元素  lindex key index

(3) 获取列表长度  llen key

lrange key start stop 返回列表key指定区间内的元素

LRANGE key start stop
返回列表 key 中指定区间内的元素,偏移量 start 和 stop 指定的区间是闭区间,前后都包含。
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推,-N 表示第一个元素。

注意LRANGE命令和编程语言区间函数的区别
假如你有一个包含一百个元素的列表,对该列表执行 LRANGE list 0 10 ,结果是一个包含11个元素的列表,这表明 stop 下标也在 LRANGE 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如Ruby的 Range.new 、 Array#slice 和Python的 range() 函数。

超出范围的下标
超出范围的下标值不会引起错误。
如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大,那么 LRANGE 返回一个空列表。
如果 stop 下标比 end 下标还要大,Redis将 stop 的值设置为 end 。

1、lrange 查询 list 全部数据

10.233.1.123:6379> lrange key1 0 -1
1) "value1"
2) "value2"
3) "value3"

2、集群方式执行时,报错不识别参数 -1

redis-cli --cluster call localhost:6379 lrange key1 0 -1
Unrecognized option or bad number of args for: '-1'

删除

(1) 从列表左侧或右侧弹出元素.  lpop key  rpop key  将列表最左侧与右侧的元素弹出来.

(2) 删除指定元素  lrem key count value
lrem命令会从列表中找到等于value的元素进行删除,根据count的不同分为三种:
count>0,从列表中删除指定数量(count)的元素.
count<0,从列表中删除count绝对值数量的元素.
count=0,删除所有.

(3) 按照索引范围修剪列表  ltrim key start end

修改

修改指定索引下标的元素:  lset key index value

阻塞操作

阻塞式弹出:   blpop key [key…] timeout  brpop key [key…] timeout
blpop与brpop命令是lpop和rpop命令的阻塞版本,他除了弹出方向不同,使用方法基本相同,所以下面以brpop命令进行说明,
brpop命令包含两个参数:
1)列表为空:如果timeout等于3,那么客户端等到三秒后返回,如果timeout=0,那么客户端将一直阻塞,直到弹出成功.
2)列表不为空:客户端会立刻返回.

在使用阻塞弹出命令时,有两点需要注意.
第一点:如果是多个键,那么会从左到右遍历键,一旦有一个键能弹出元素客户端就会立刻返回.
第二点:如果多个客户端同时对一个键进行操作,那么最先执行命令的客户端可以获取到值.

内部数据结构(压缩列表,链表)

列表类型的内部编码有两种
1、ziplist(压缩列表):当列表的元素个数大于 list-max-ziplist-entries 配置(默认为512个),同时列表中每个元素的长度小于 list-max-ziplist-value 配置(默认为64字节).

2、linkedlist(链表):当列表的长度或值得大小不满足ziplist的要求,redis会采用linkedlist为列表的内部实现编码.

使用场景(阻塞队列)

1、消息队列:redis的lpush-brpop命令组合即可实现阻塞队列,生产者客户端使用lpush命令向列表插入元素.消费者客户端使用brpop命令阻塞式的”抢”列表中的尾部元素.多个客户端保证消息的负载均衡与可用性.

2、文章列表:每个用户都有属于自己的文章列表.此时可以考虑使用列表,因为列表不但是有序的,同时支持使用lrange按照索引范围获取多个元素.

3、开发提示:列表的使用场景有很多如: lpush+lpop=Stack(栈)、lpush+rpop=queue(队列)、lpush+brpop=message queue、lpush+ltrim=Capped Collection(有限集合)

4、twitter的关注列表,粉丝列表都可以用list结构来实现。


Set 集合(无序,不可重复)

集合(set)类型也是用来保存多个的字符串元素,但和列表不同的是:它的元素是无序且不可重复的,不能通过索引获取元素

操作命令

集合内操作

(1) 添加元素  sadd key value [value…]  返回结果为添加成功的元素数量.

(2) 删除元素  srem key value [value…]  返回结果为删除成功的元素数量.

(3) 获取元素个数  scard key

(4) 判断元素是否在集合中  sismember key value

(5) 随机从集合中返回指定个数元素  srandmember key [count]  [count]是可选参数,如果不写默认为:1.

(6) 从集合中随机弹出元素  spop key  spop操作可以从集合中随机弹出一个元素.

(7) 获取集合的所有元素  smembers key  获取集合所有元素,且返回结果是无序的.

集合间操作

(1) 求多个集合的交集  sinter key [key…]

(2) 求多个集合的并集  sunion key [key…]

(3) 求多个集合的差集  sdiff key [key…]

(4) 将交集、并集、差集的结果保存.
sinterstore storeKey key [key…]
sunionstore storeKey key [key…]
sdiffstore storeKey key [key…]
集合间的运算在元素比较多的情况下会比较耗时,所以redis提供了上面三个命令(原命令+store)将集合间交集、并集、差集的结果保存到storeKey中,例如将user:1:follows和user:2:follows两个集合之间的交集结果保存到user:1_2:follows中

内部数据结构(哈希表,整数集合)

集合类型的内部编码有两种:
1、intset(整数集合)
当集合中的元素全是整数,且长度不超过 set-max-intset-entries (默认为512个)时,redis会选用intset作为内部编码.

2、hashtable(哈希表)
当集合无法满足intset的条件时,redis会使用hashtable作为内部编码.

使用场景(用户标签)

集合类型比较典型的使用场景是标签(tag).例如一个用户可能对音乐感兴趣,另一个用户对新闻感兴趣,这些想去点就是标签.有了这些数据就可以获得喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验来说比较重要.

redis有序集合性能 列表、集合、有序集合
https://blog.csdn.net/ttomqq/article/details/78548489


ZSet 有序集合(score排序)

zset 有序集合,保留了集合不能重复元素的特性,给每个元素(member)设置一个分数(score)作为排序的依据。

集合内操作命令

zadd key score mem 添加成员(logn复杂度)

zadd key score member [score member ...] 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值,并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。
score 值可以是整数值或双精度浮点数。
如果 key 不存在,则创建一个空的有序集并执行 ZADD 操作。
当 key 存在但不是有序集类型时,返回一个错误。

时间复杂度:O(M*log(N)), N 是有序集的基数, M 为成功添加的新成员的数量。

返回值:被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员。

Redis 3.2 为 zadd 命令添加了nx、xx、ch、incr四个选项:
nx:member必须不存在,才可以设置成功,用于添加.
xx:member必须存在,才可以设置成功,用于添加.
ch:返回此次操作后,有序集合元素和分数发生变化的个数.
incr: 对score进行添加操作,相当于后面介绍的zincrby.

有序集合相比集合提供了排序字段,但是也产生了代价,zadd的时间复杂度是O(log(n)),sadd的时间复杂度为O(1).

127.0.0.1:6379> zadd myzset1 1 tom 2 jerry
(integer) 2
127.0.0.1:6379> zadd myzset1 2 jerry 5 matt
(integer) 1

zcard key 获取成员个数

zcard key 获取zset指定key的成员个数

时间复杂度: O(1)

返回值:
当 key 存在且是有序集类型时,返回有序集的基数。
当 key 不存在时,返回 0

127.0.0.1:6379> zcard myzset1
(integer) 2

zcount key min max 分数范围内的成员个数

zcount key min max 返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。

时间复杂度: O(log(N)+M), N 为有序集的基数, M 为值在 min 和 max 之间的元素的数量。

127.0.0.1:6379> zcount myzset1 2 5
(integer) 2

zincrby key increment member 给指定成员增加分数

zincrby key increment member 为有序集 key 的成员 member 的 score 值加上增量 increment
可以通过传递一个负数值 increment ,让 score 减去相应的值,比如 ZINCRBY key -5 member ,就是让 member 的 score 值减去 5 。
当 key 不存在,或 member 不是 key 的成员时, ZINCRBY key increment member 等同于 ZADD key increment member 。
当 key 不是有序集类型时,返回一个错误。
score 值可以是整数值或双精度浮点数。

时间复杂度: O(log(N))

返回值: member 成员的新 score 值,以字符串形式表示。

127.0.0.1:6379> zincrby myzset1 2.33 tom
"1003.33"
127.0.0.1:6379> zscore myzset1 tom
"1003.33"

zscore key mem 获取成员分数

zscore key mem 返回有序集 key 中,成员 member 的 score 值。
如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil 。

时间复杂度: O(1)

127.0.0.1:6379> zscore myzset1 tom
"1"

zrank key mem 获取成员排名(升序)

zrank key member 获取成员排名,返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。
排名以 0 为底,也就是说, score 值最小的成员排名为 0 。
使用 zrevrank 命令可以获得成员按 score 值递减(从大到小)排列的排名。

时间复杂度: O(log(N))

127.0.0.1:6379> zrank myzset1 jerry
(integer) 1
127.0.0.1:6379> zrank myzset1 tom
(integer) 0
127.0.0.1:6379> zrank myzset1 matt
(integer) 2

zrevrank key member 获取成员排名(降序)

zrevrank key member 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。
排名以 0 为底,也就是说, score 值最大的成员排名为 0 。
使用 zrank 命令可以获得成员按 score 值递增(从小到大)排列的排名。

时间复杂度: O(log(N))

127.0.0.1:6379> zrevrank myzset1 matt
(integer) 0
127.0.0.1:6379> zrevrank myzset1 jerry
(integer) 1

zrange key start stop [withscores] 排名范围内的元素列表(升序)

zrange key start stop [withscores] 返回有序集 key 中,指定排名区间内的成员。成员的位置按 score 值递增(从小到大)来排序。
具有相同 score 值的成员按字典序(lexicographical order )来排列。
如果你需要成员按 score 值递减(从大到小)来排列,请使用 zrevrange 命令。
下标参数 start 和 stop 都以 0 为底,也就是说,以 0 表示有序集第一个成员,以 1 表示有序集第二个成员,以此类推。
你也可以使用负数下标,以 -1 表示最后一个成员, -2 表示倒数第二个成员,以此类推。比如 zrange 0 -1 withscores 表示查询全部成员及分数。

超出范围的下标并不会引起错误。
比如说,当 start 的值比有序集的最大下标还要大,或是 start > stop 时, ZRANGE 命令只是简单地返回一个空列表。
另一方面,假如 stop 参数的值比有序集的最大下标还要大,那么 Redis 将 stop 当作最大下标来处理。

可以通过使用 WITHSCORES 选项,来让成员和它的 score 值一并返回,返回列表以 value1,score1, …, valueN,scoreN 的格式表示。

时间复杂度: O(log(N)+M), N 为有序集的基数,而 M 为结果集的基数。

127.0.0.1:6379> zrange myzset1 0 1
1) "tom"
2) "jerry"
127.0.0.1:6379> zrange myzset1 0 1 withscores
1) "tom"
2) "1"
3) "jerry"
4) "2"

zrevrange key start stop [withscores] 排名范围内的元素列表(降序)

zrevrange key start stop [withscores] 返回有序集 key 中,指定排名区间内的成员。成员的位置按 score 值递减(从大到小)来排列。
具有相同 score 值的成员按字典序的逆序(reverse lexicographical order)排列。
除了成员按 score 值递减的次序排列这一点外, ZREVRANGE 命令的其他方面和 zrange 命令一样。

zrangebyscore key min max 分值范围内的元素列表(升序)

zrangebyscore key min max [withscores] [limit offset count] 返回指定分数范围的成员列表
返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
具有相同 score 值的成员按字典序(lexicographical order)来排列(该属性是有序集提供的,不需要额外的计算)。

可选的 WITHSCORES 参数决定结果集是单单返回有序集的成员,还是将有序集成员及其 score 值一起返回。

分页
可选的 LIMIT 参数指定返回结果的数量及区间(就像SQL中的 SELECT LIMIT offset, count ),注意当 offset 很大时,定位 offset 的操作可能需要遍历整个有序集,此过程最坏复杂度为 O(N) 时间。

无限区间
min 和 max 可以是 -inf 和 +inf ,这样一来,你就可以在不知道有序集的最低和最高 score 值的情况下,使用 ZRANGEBYSCORE 这类命令。

开区间
默认情况下,区间的取值使用闭区间 (小于等于或大于等于),你也可以通过给参数前增加 ( 符号来使用可选的开区间 (小于或大于)。
例如
zrangebyscore zset (1 5 表示返回 1 < score <= 5 的成员
ZRANGEBYSCORE zset (5 (10 返回所有符合条件 5 < score < 10 的成员。

时间复杂度: O(log(N)+M), N 为有序集的基数, M 为被结果集的基数。

127.0.0.1:6379> zrangebyscore myzset1 0 1000
(empty array)
127.0.0.1:6379> zrangebyscore myzset1 0 3000
1) "tom"
2) "jerry"
127.0.0.1:6379> zrangebyscore myzset1 0 2002
1) "tom"
2) "jerry"
127.0.0.1:6379> zrangebyscore myzset1 0 (2002
1) "tom"
127.0.0.1:6379> zrangebyscore myzset1 0 2002 withscores
1) "tom"
2) "1001"
3) "jerry"
4) "2002"

zrevrangebyscore key min max 分值范围内的元素列表(降序)

zrevrangebyscore key min max [withscores] [limit offset count]
返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
具有相同 score 值的成员按字典序(lexicographical order)来排列(该属性是有序集提供的,不需要额外的计算)。

zrem key member 删除成员

zrem key member [member...] 移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。
当 key 存在但不是有序集类型时,返回一个错误。

时间复杂度: O(M*log(N)), N 为有序集的基数, M 为被成功移除的成员的数量。

返回值: 被成功移除的成员的数量,不包括被忽略的成员。

127.0.0.1:6379> zrange myzset1 0 -1 withscores
1) "tom"
2) "1003.33"
3) "jerry"
4) "2002"
5) "matt"
6) "5005"
127.0.0.1:6379> zrem myzset1 matt
(integer) 1
127.0.0.1:6379> zrange myzset1 0 -1 withscores
1) "tom"
2) "1003.33"
3) "jerry"

zremrangebyrank key start stop 删除排名范围内的成员

zremrangebyrank key start stop 移除有序集 key 中,指定排名(rank)区间内的所有成员。
区间分别以下标参数 start 和 stop 指出,包含 start 和 stop 在内。
下标参数 start 和 stop 都以 0 为底,也就是说,以 0 表示有序集第一个成员,以 1 表示有序集第二个成员,以此类推。
你也可以使用负数下标,以 -1 表示最后一个成员, -2 表示倒数第二个成员,以此类推。

时间复杂度: O(log(N)+M), N 为有序集的基数,而 M 为被移除成员的数量。

127.0.0.1:6379> zrange myzset1 0 -1 withscores
 1) "hanmeimei"
 2) "77"
 3) "lilei"
 4) "99"
 5) "tom"
 6) "1003.33"
 7) "jim"
 8) "1460"
 9) "jerry"
1)  "2002"
2)  "sam"
3)  "3333"
127.0.0.1:6379> zremrangebyrank myzset1 0 1
(integer) 2
127.0.0.1:6379> zrange myzset1 0 -1 withscores
1) "tom"
2) "1003.33"
3) "jim"
4) "1460"
5) "jerry"
6) "2002"
7) "sam"
8) "3333"

zremrangebyscore key min max 删除分数范围内的成员

zremrangebyscore key min max 删除指定分数范围的成员
移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。

时间复杂度: O(log(N)+M), N 为有序集的基数,而 M 为被移除成员的数量。

返回值: 被移除成员的数量。

127.0.0.1:6379> zrange myzset1 0 -1 withscores
1) "tom"
2) "1003.33"
3) "jim"
4) "1460"
5) "jerry"
6) "2002"
7) "sam"
8) "3333"
127.0.0.1:6379> zremrangebyscore myzset1 1000 2000
(integer) 2
127.0.0.1:6379> zrange myzset1 0 -1 withscores
1) "jerry"
2) "2002"
3) "sam"
4) "3333"

集合间的操作命令

(1) 交集
zinterstore storeKey keyNum key [key …] [weights weight [weight…]] [aggregate sum|min|max]  
参数说明:
storeKey:交集计算结果保存到这个键下.
keyNum:需要做交集的键的个数.
key[key …]:需要做交集的键.
weights weight [weight…]:每个键的权重,在做交集计算时,每个键中的每个member的分值会和这个权重相乘,每个键的权重默认为1.
aggregate sum|min|sum:计算成员交集后,分值可以按照sum(和)、min(最小值)、max(最大值)做汇总.默认值为sum.

(2) 并集  
zunionstore storeKey keyNum key [key…] [weights weight [weight…]] [aggregate sum|min|max]  
该命令的所有参数和zinterstore是一致的,只不过做的是并集计算

内部数据结构(跳跃表,压缩列表)

1、ziplist(压缩列表)
当有序集合的元素小于 zset-max-ziplist-entries 配置(默认是128个),同时每个元素的值都小于 zset-max-ziplist-value (默认是64字节)时,Redis会用ziplist来作为有序集合的内部编码实现,ziplist可以有效的减少内存的使用

2、skiplist(跳跃表)    
当ziplist的条件不满足时,有序集合将使用skiplist作为内部编码的实现,来解决此时ziplist造成的读写效率下降的问题.

redis有序集合性能 列表、集合、有序集合
https://blog.csdn.net/ttomqq/article/details/78548489


Redis数据类型

redisObject

Redis 对外提供了5种value数据类型, String(字符串)、list(链表)、set(集合)、zset(有序集合)和hash(哈希)
这些类型每个都对应一个或多个内部数据类型,Redis 通过 redisObject 对象将外部类型和内部类型映射起来。


Redis数据类型映射

redis内部使用一个redisObject对象来表示所有的key和value
redisObject最主要的信息如上图所示:
type 表示一个value对象具体是何种数据类型
encoding是不同数据类型在redis内部的存储方式。比如:type=string表示value存储的是一个普通字符串,那么encoding可以是raw或者int。如果是 int 则代表实际 redis 内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如:”123” “456”这样的字符串。


redis各数据类型的使用场景

string
计数器应用

list
取最新n个数据的操作
消息队列
删除与过滤
实时分析正在发生的情况,用于数据统计与防垃圾邮件

set
unique操作,获取某段时间说有数据的排重值
实时系统,反垃圾系统
共同好友,二度好友
利用唯一性,可以统计访问法网站的所有独立IP
好友推荐的时候,根据tag求交集,大于某个threshold就可推荐

hash
存储、读取、修改用户属性

sorted set
排行榜应用,取top n操作
需要精准设定过期时间的应用(时间戳作为score)
带有权重的元素,比如一个游戏的用户得分排行榜
过期项目处理,按照时间排序

redis有序集合性能 列表、集合、有序集合
https://blog.csdn.net/ttomqq/article/details/78548489


redis中哪些操作是O(n)复杂度的?

1、List: lindex、lset、linsert
LINDEX key index 返回列表 key 中,下标为 index 的元素。 O(N)
LSET key index value 将列表 key 下标为 index 的元素的值设置为 value O(N)
LINSERT key BEFORE|AFTER pivot value 将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。 O(N)

2、Hash: hgetall、hkeys、hvals
HGETALL key 返回哈希表 key 中,所有的域和值。 O(N)
HKEYS key 返回哈希表 key 中的所有域。 O(N)
HVALS key 返回哈希表 key 中所有域的值。 O(N)

3、Set: smembers、sunion、sunionstore、sinter、sinterstore、sdiff、sdiffstore
SMEMBERS key 返回集合 key 中的所有成员。 O(N), N 为集合的基数。
SUNION key [key …] 返回一个集合的全部成员,该集合是所有给定集合的并集。 O(N), N 是所有给定集合的成员数量之和。
SUNIONSTORE destination key [key …] 这个命令类似于 SUNION 命令,但它将结果保存到 destination 集合,而不是简单地返回结果集。
SINTER key [key …] 返回一个集合的全部成员,该集合是所有给定集合的交集。O(MN),N 为给定集合当中基数最小的集合, M 为给定集合的个数
SINTERSTORE destination key [key …] 这个命令类似于 SINTER 命令,但它将结果保存到 destination 集合,而不是简单地返回结果集。O(MN),N 为给定集合当中基数最小的集合, M 为给定集合的个数
SDIFF key [key …] 返回一个集合的全部成员,该集合是所有给定集合之间的差集。O(N) sdiff set1 set2 的结果是在 set1 中但不在 set2 中的value

4、Sorted Set: zrange、zrevrange、zrangebyscore、zrevrangebyscore、zremrangebyrank、zremrangebyscore
ZRANGE key start stop [WITHSCORES] 返回有序集 key 中,指定区间内的成员。 其中成员的位置按 score 值递增(从小到大)来排序。 O(log(N)+M), N 为有序集的基数,而 M 为结果集的基数。
ZREVRANGE key start stop [WITHSCORES] 返回有序集 key 中,指定区间内的成员。 其中成员的位置按 score 值递减(从大到小)来排列。 O(log(N)+M), N 为有序集的基数,而 M 为结果集的基数。
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。 O(log(N)+M), N 为有序集的基数, M 为被结果集的基数。
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] 返回有序集 key 中, score 值介于 max 和 min 之间(默认包括等于 max 或 min )的所有的成员。有序集成员按 score 值递减(从大到小)的次序排列。 O(log(N)+M), N 为有序集的基数, M 为结果集的基数。
ZREMRANGEBYRANK key start stop 移除有序集 key 中,指定排名(rank)区间内的所有成员。 O(log(N)+M), N 为有序集的基数,而 M 为被移除成员的数量。
ZREMRANGEBYSCORE key min max 移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。 O(log(N)+M), N 为有序集的基数,而 M 为被移除成员的数量。

reids中哪些操作是O(1)复杂度的?


Redis内部数据结构

Redis 作为一个基于key/value的内存数据库,使用ANSI C语言实现,以其高性能和支持丰富的数据结构闻名于世,而其数据结构也是其高性能的基础。
在Redis内部,有非常多的数据结构:sds(简单动态字符串),list,intset(整数集合),hash(字典),zskiplist(跳跃表),ziplist(压缩表)等。
以redis3.2的正式版源码分析

sds简单动态字符串

Redis采用动态字符串的形式,用len记录长度,这样可以在 O(1) 的复杂度内获取字符串长度;根据不同的类型和字符串的长短,分别用不同类型的sdshdr,可以节约不少空间;将alloc和len分离,可以在一定的范围内节省分配内存所用的时间;在Redis中,运用了大量的指针移动技巧来获取void*对象,也提高了程序的运行效率。

c中char * 字符串的问题

char * 字符串以 \0 作为结尾,并不能高效地支持长度计算和追加(append)这两种操作:
1、每次计算字符串长度 strlen(s) 的复杂度为 O(n)
2、对字符串进行 N 次追加,必定需要对字符串进行 N 次内存重分配(realloc)。

sdshdr数据结构

typedef char *sds;

struct sdshdr {
    // buf 已占用长度
    int len;

    // buf 剩余可用长度
    int free;

    // 实际保存字符串数据的地方
    char buf[];
};

类型 sds 是 char * 的别名(alias),而结构 sdshdr 则保存了 len 、 free 和 buf 三个属性。

sds对append操作的优化

当调用 SET 命令创建 sdshdr 时, sdshdr 的 free 属性为 0 , Redis 也没有为 buf 创建额外的空间。
但是,在执行 APPEND 之后, Redis 为 buf 创建了多于所需空间一倍的大小。
这样一来, 如果将来再次对同一个 sdshdr 进行追加操作, 只要追加内容的长度不超过 free 属性的值, 那么就不需要对 buf 进行内存重分配。

简单动态字符串 - Redis 设计与实现
https://redisbook.readthedocs.io/en/latest/internal-datastruct/sds.html


list双向链表

Redis中,list的实现是一个双端链表,这样可以方便的获取其前后的节点值,方便之后对节点的查找
链表结点:

typedef struct listNode { /*节点*/
struct listNode *prev;
struct listNode *next;
void *value; /*value用函数指针类型,决定了value可以是sds,list,set,dict等类型*/
} listNode;

链表结构:

typedef struct list { /*链表结构*/
  listNode *head; /*头节点*/
  listNode *tail; /*尾节点*/
  void *(*dup)(void *ptr); /*复制节点*/
  void (*free)(void *ptr); //释放节点
  int (*match)(void *ptr, void *key);  // 匹配节点,返回key值的index
  unsigned long len; /*记录链表的长度*/
} list;

intset整数集合

当一个集合元素只有整数并且数量元素不多的时候,可以选择用整数集合来作为其底层实现。

typedef struct intset { /*整数集合的数据结构*/
uint32_t encoding; //编码方式
uint32_t length;
int8_t contents[];
} intset;

contents数组,它存储集合中的内容,并且以从小到大的顺序排列,并保证其没有重复的元素。

Redis内部数据结构的实现
https://blog.csdn.net/a6833916180/article/details/51596013


dict字典(哈希表)

字典结构是整个Redis的核心数据结构,基本上是其内部结构的缩影。

typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;

dictEntry是最核心的字典结构的节点结构,它保存了key和value的内容;另外,next指针是为了解决hash冲突,字典结构的hash冲突解决方法是拉链法,对于hashcode重复的节点以链表的形式存储。

typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask; /*hash表的掩码,总是size-1,用于计算hash表的索引值*/
unsigned long used;
} dictht;

dictht是节点dictEntry的持有者,将dictEntry结构串起来,table就是hash表,其实dictEntry *table[]这样的书写方式更容易理解些,size就是table数组的长度,used标志已有节点的数目。

typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
int iterators; /* number of iterators currently running */
} dict;

dict是最外层的字典结构的接口形式,type标志类型,privdata标志其私有数据,dict持有两个dictht结构,一个用来存储数据,一个用来在rehash时使用,rehashidx标志是否正在rehash(因为Redis中rehash是一个渐近的过程,正在rehash的时候rehashidx记录rehash的阶段,否则为-1)。
注:rehash是一个为了让负载因子(load_factor=used/size)控制在一个合理的范围内而重新分配内存和扩展结构的过程。
iterators是一个迭代器,用于记录当前迭代的数目。

Redis内部数据结构的实现
https://blog.csdn.net/a6833916180/article/details/51596013


zskiplist跳跃表

跳表是一种实现起来很简单,单层多指针的链表,它查找效率很高,堪比优化过的二叉平衡树,且比平衡树的实现,简单的多的多。
跳表在Redis中仅仅作为zset(有序集合)的底层实现出现
zadd的时间复杂度是O(log(n))

ziplist压缩表

ziplist是一个编码后的列表,是由一系列特殊编码的连续内存块组成的顺序型数据结构,特殊的设计使得内存操作非常有效率,此列表可以同时存放字符串和整数类型,列表可以在头尾各边支持推加和弹出操作在O(1)常量时间,但是,因为每次操作涉及到内存的重新分配释放,所以加大了操作的复杂性 。

typedef struct zlentry {
//prevrawlen为上一个数据结点的长度,prevrawlensize为记录该长度数值所需要的字节数
unsigned int prevrawlensize, prevrawlen;
//len为当前数据结点的长度,lensize表示表示当前长度表示所需的字节数
unsigned int lensize, len;
//数据结点的头部信息长度的字节数
unsigned int headersize;
//编码的方式
unsigned char encoding;
//数据结点的数据(已包含头部等信息),以字符串形式保存
unsigned char *p;
} zlentry;

压缩表之所以成为压缩表,是因为它起到了一定的压缩功能,对于其他的数据结构为了快速定位,使用了大量的指针结构,这样对于长度较大的数据优势明显,但是对于长度非常小的数据,比如说一个表里的每一个数据长度都很短,但是数据量并不小,这样的话,就会出现大量的指针结构,造成内存浪费,而压缩表则分配了一块连续内存来存储,就避免了大量的指针结构,节省了内存。另外,ziplist也使用了动态分配内存的方法,也一定程度上避免了内存的浪费。

Redis内部数据结构的实现
https://blog.csdn.net/a6833916180/article/details/51596013


上一篇 Redis-基础

下一篇 Redis-安装部署运维

阅读
评论
12.8k
阅读预计51分钟
创建日期 2017-07-05
修改日期 2024-06-02
类别
目录
  1. Connection 连接
    1. SELECT index 选择数据库
  2. Server 服务器
    1. info 查看全部服务器信息
    2. info memory 查看内存信息
      1. 查看redis缓存lua脚本个数及内存占用
  3. Key 键
    1. keys pattern 模糊查找key
      1. 模糊查找keys并打印到一行
      2. 模糊匹配1号数据库的keys
      3. 统计模糊匹配keys的个数
      4. 模糊匹配keys并get值
      5. 批量删除模糊匹配的keys
      6. 生产系统禁止使用keys命令
      7. 使用scan代替keys
    2. del key key1 key2 删除一个或多个key
    3. expire key seconds 设置key的过期时间为seconds秒
    4. pexpire key milliseconds 设置毫秒过期时间
    5. ttl key 返回key的生存时间(秒)
    6. persist key 将key设置为永久有效
    7. type key 查看key的类型
  4. String 字符串
    1. get key
    2. mget key [key ...]
    3. set key value [EX seconds] [PX mills] [NX|XX]
    4. setex key seconds value
    5. setnx key value
    6. mset key value [key value ...]
    7. incr key 增加1
      1. incr/decr key是原子的
    8. incrby key n 增加n
    9. decr key
    10. setbit key offset value 设置指定偏移量的bit
    11. getbit key offset 获取指定偏移量的bit
      1. bitmap的空间占用
      2. Redis中bitmap的妙用
    12. strlen
    13. APPEND key value
  5. Hash 哈希表
    1. hget key field 返回哈希表key中的field字段值
    2. hset key field value 设置哈希表key中的field值
    3. hmget key field field2 返回哈希表key中多个字段值
    4. hgetall key 返回哈希表key的所有字段和值
  6. List 列表(双向链表,有序)
    1. 插入
    2. 查询
      1. lrange key start stop 返回列表key指定区间内的元素
    3. 删除
    4. 修改
    5. 阻塞操作
    6. 内部数据结构(压缩列表,链表)
    7. 使用场景(阻塞队列)
  7. Set 集合(无序,不可重复)
    1. 操作命令
      1. 集合内操作
      2. 集合间操作
    2. 内部数据结构(哈希表,整数集合)
    3. 使用场景(用户标签)
  8. ZSet 有序集合(score排序)
    1. 集合内操作命令
      1. zadd key score mem 添加成员(logn复杂度)
      2. zcard key 获取成员个数
      3. zcount key min max 分数范围内的成员个数
      4. zincrby key increment member 给指定成员增加分数
      5. zscore key mem 获取成员分数
      6. zrank key mem 获取成员排名(升序)
      7. zrevrank key member 获取成员排名(降序)
      8. zrange key start stop [withscores] 排名范围内的元素列表(升序)
      9. zrevrange key start stop [withscores] 排名范围内的元素列表(降序)
      10. zrangebyscore key min max 分值范围内的元素列表(升序)
      11. zrevrangebyscore key min max 分值范围内的元素列表(降序)
      12. zrem key member 删除成员
      13. zremrangebyrank key start stop 删除排名范围内的成员
      14. zremrangebyscore key min max 删除分数范围内的成员
    2. 集合间的操作命令
    3. 内部数据结构(跳跃表,压缩列表)
  9. Redis数据类型
    1. redisObject
    2. redis各数据类型的使用场景
      1. redis中哪些操作是O(n)复杂度的?
      2. reids中哪些操作是O(1)复杂度的?
  10. Redis内部数据结构
    1. sds简单动态字符串
      1. c中char * 字符串的问题
      2. sdshdr数据结构
      3. sds对append操作的优化
    2. list双向链表
    3. intset整数集合
    4. dict字典(哈希表)
    5. zskiplist跳跃表
    6. ziplist压缩表

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论