Redis教程

[DB]-Redis数据类型及数据结构解析

本文主要是介绍[DB]-Redis数据类型及数据结构解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1、数据类型

Redis中数据类型-《Redis In Action》

数据类型可以存储的值操作
STRING字符串、整数或浮点数对整个字符串或字符串其中一部分执行操作,对整数和浮点数执行自增自减
LIST列表从两端压入弹出元素;对单个或多个元素进行修剪;只保留一个范围内的元素
SET无序集合添加获取移出单个元素;检查元素是否存在;计算交并差集;从集合中随机获取元素
HASH键值对检查键是否存在
ZSET有序集合根据分值范围或成员获取元素;计算键的排名

STRING

在这里插入图片描述
get key 通过key获取value

set key value 存入键值对

del key 删除key对应的键值对

LIST

在这里插入图片描述

// rpush: 将value压入list末尾,不会去重
redis 127.0.0.1:6379 > rpush list-key item
(integer) 1
redis 127.0.0.1:6379 > rpush list-key item2
(integer) 2
redis 127.0.0.1:6379 > rpush list-key item
(integer) 3

// lrange:获取list中给定范围的数据
redis 127.0.0.1:6379 > lrange list-key 0 -1
1) "item"
2) "item2"
3) "item"

// lindex:获取给定索引下的数据 0为首位 -1 末位
redis 127.0.0.1:6379 > lindex list-key 1
"item2"

// lpop:弹栈
redis 127.0.0.1:6379 > lpop list-key
"item"
redis 127.0.0.1:6379 > lrange list-key 0 -1
1) "item2"
2) "item"

SET

在这里插入图片描述

// sadd:向集合中添加元素,会去重,添加成功返回1,否则返回0证明该元素已存在
redis 127.0.0.1:6379 > sadd set-key item
(integer) 1
redis 127.0.0.1:6379 > sadd set-key item2
(integer) 1
redis 127.0.0.1:6379 > sadd set-key item3
(integer) 1
redis 127.0.0.1:6379 > sadd set-key item
(integer) 0

// smembers:返回整个集合中的元素
redis 127.0.0.1:6379 > smembers set-key
1) "item"
2) "item2"
3) "item3"

// sismember:判断元素是否在集合中 0表示不在,1表示在
redis 127.0.0.1:6379 > sismember set-key item4
(integer) 0
redis 127.0.0.1:6379 > sismember set-key item
(integer) 1

// srem:移除集合中元素 存在该元素返回1,不存在返回0
redis 127.0.0.1:6379 > srem set-key item2
(integer) 1
redis 127.0.0.1:6379 > srem set-key item2
(integer) 0
redis 127.0.0.1:6379> smembers set-key
1) "item"
2) "item3"

HASH

在这里插入图片描述

// hset: 将key value成堆存储在hash表中,添加成功返回1,重复添加/更新value返回0
redis 127.0.0.1:6379 > hset hash-key sub-key1 value1
(integer) 1
redis 127.0.0.1:6379 > hset hash-key sub-key2 value2
(integer) 1
redis 127.0.0.1:6379 > hset hash-key sub-key1 value1
(integer) 0

// hgetall: 获取hash表中的全部键值对
redis 127.0.0.1:6379 > hgetall hash-key
1) "sub-key1"
2) "value1"
3) "sub-key2"
4) "value2"

// hdel: 删除给定key的键值对,删除成功返回1,不存在返回0
redis 127.0.0.1:6379> hdel hash-key sub-key2
(integer) 1
redis 127.0.0.1:6379> hdel hash-key sub-key2
(integer) 0

// hget: 根据key获取value
redis 127.0.0.1:6379> hget hash-key sub-key1
"value1"

ZSET-有序集合

在这里插入图片描述

// zadd: 将元素和对应的score添加进ZSET
redis 127.0.0.1:6379 > zadd zset-key 728 member1
(integer) 1
redis 127.0.0.1:6379 > zadd zset-key 982 member0
(integer) 1
redis 127.0.0.1:6379 > zadd zset-key 982 member0
(integer) 0

// zrange: 获取按scores排序后的数据 withscores是否携带scores
redis 127.0.0.1:6379 > zrange zset-key 0 -1 withscores
1) "member1"
2) "728"
3) "member0"
4) "982"

// zrangebyscore: 获取某一scores范围的数据
redis 127.0.0.1:6379 > zrangebyscore zset-key 0 800 withscores
1) "member1"
2) "728"

// zrem: 移除元素
redis 127.0.0.1:6379 > zrem zset-key member1
(integer) 1
redis 127.0.0.1:6379 > zrem zset-key member1
(integer) 0
redis 127.0.0.1:6379 > zrange zset-key 0 -1 withscores
1) "member0"
2) "982"

2、数据结构

字典

dict字典,dictht是字典中的散列表结构,使用拉链法解决哈希冲突。

字典 dict 的实现:

/*
 * 字典
 *
 * 每个字典使用两个哈希表,用于实现渐进式 rehash
 */
typedef struct dict {

    // 特定于类型的处理函数
    dictType *type;

    // 类型处理函数的私有数据
    void *privdata;

    // 哈希表(2 个)
    dictht ht[2];

    // 记录 rehash 进度的标志,值为 -1 表示 rehash 未进行
    int rehashidx;

    // 当前正在运作的安全迭代器数量
    int iterators;

} dict;

哈希表 dictht 的实现:

/*
 * 哈希表
 */
typedef struct dictht {

    // 哈希表节点指针数组(俗称桶,bucket)
    dictEntry **table;

    // 指针数组的大小
    unsigned long size;

    // 指针数组的长度掩码,用于计算索引值
    unsigned long sizemask;

    // 哈希表现有的节点数量
    unsigned long used;

} dictht;

哈希表 dictEntry 中的节点:

/*
 * 哈希表节点
 */
typedef struct dictEntry {

    // 键
    void *key;

    // 值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;

    // 链往后继节点
    struct dictEntry *next;

} dictEntry;

字典的实现图示:
在这里插入图片描述

扩容机制

dictAdd 在每次向字典添加新键值对之前, 都会对哈希表 ht[0] 进行检查, 对于 ht[0] 的 size 和 used 属性, 如果它们之间的比率 ratio = used / size 满足以下任何一个条件的话,rehash 过程就会被激活:

  1. 自然 rehash : ratio >= 1 ,且变量 dict_can_resize 为真。(没有执行 BGSAVE 或 BGREWRITEAOF时,即没有进行持久化时)
  2. 强制 rehash : ratio 大于变量 dict_force_resize_ratio (目前版本中, dict_force_resize_ratio 的值为 5 )。

Rehash执行过程

  1. 创建一个比 ht[0] -> table 更大的 ht[1] -> table;
  2. 将 ht[0] -> table 中的所有键值对迁移到 ht[1] -> table;
  3. 将原有 ht[0] 的数据清空,并将 ht[1] 替换为新的 ht[0];

为了保证redis的响应速度,rehash过程会被均摊到每个操作中,称为渐进式Rehash

渐进式Rehash

具体步骤:

  1. 为ht[1]分配空间,同时持有两个哈希表(一个空表,一个有数据)
  2. 维持一个计数器rehashidx(记录当前rehash进行到 ht[0] 哪个索引位置上),初始值为0
  3. 每次对字典增删改查,会顺带将ht[0]中的第一个不为空的索引上的数据迁移到ht[1],rehashidx++
  4. 直到rehash操作完成,rehashidx值设为-1

其他措施:

  • rehash过程中字典会同时使用两个哈希表,所有的查找、删除等操作,需要同时在 ht[0] ht[1]上进行
  • 添加时,直接添加到ht[1],保证ht[0]的节点数量在整个rehash过程中只减不增。

跳表

有序集合的底层实现之一
基于多指针有序链表实现。
查找时从上层指针开始查找,找到对应区间后再到下一层寻找。

在这里插入图片描述
与红黑树相比的优点:

  • 插入速度快,不需要旋转等维护平衡性
  • 实现简单
  • 支持无锁操作

3、持久化

RDB持久化

将某个时间点的所有数据都存放到硬盘上。

AOF持久化

将写命令添加到AOF文件(Append Only File)末尾,以保证文件能够记录对Redis的所有更改操作。
AOF持久化需要设置同步选项,确保写命令同步到磁盘文件上的时机。
同步选项:

  • always:同步每个写命令(对性能影响较大)
  • everysec:每秒同步
  • no:操作系统决定何时同步(对性能提升不大,但会增加数据丢失风险)

4、分片

分片是将数据划分为多个部分的方法,可以将数据存储到多台机器里面,这种方法在解决某些问题时可以获得线性级别的性能提升。
假设有 4 个 Redis 实例 R0,R1,R2,R3,还有很多表示用户的键 user:1,user:2,… ,有不同的方式来选择一个指定的键存储在哪个实例中。

  • 最简单的方式是范围分片,例如用户 id 从 0~1000 的存储到实例 R0 中,用户 id 从 1001~2000 的存储到实例R1 中,等等。但是这样需要维护一张映射范围表,维护操作代价很高。
  • 还有一种方式是哈希分片,使用 CRC32 哈希函数将键转换为一个数字,再对实例数量求模就能知道应该存储的实例。

根据执行分片的位置,可以分为三种分片方式:

  • 客户端分片:客户端使用一致性哈希等算法决定键应当分布到哪个节点。
  • 代理分片:将客户端请求发送到代理上,由代理转发请求到正确的节点上。
  • 服务器分片:Redis Cluster。

参考资料:
Redis中数据类型-《Redis In Action》
Redis设计与实现
CS-Notes
Redis中文网

这篇关于[DB]-Redis数据类型及数据结构解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!