Redis数据类型以及应用场景

前言

本篇文章主要参考《进大厂系列》系列-Redis常见面试题(带答案),记录redis的相关内容。

Redis数据类型

Redis支持5种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)等,还有一些高级的Redis数据类型,如HyperLogLog、Bitmap等。这些数据类型在底层都是使用redisObject来进行表示。redisObject中有三个重要的属性,分别是type、 encoding 和 ptr。
type表示保存的value的类型。通常有以下几种,也就是常见的五种数据结构:

  • 字符串 REDIS_STRING
  • 列表 REDIS_LIST
  • 集合 REDIS_SET
  • 有序集合 REDIS_ZSET
  • 字典 REDIS_HASH

encoding表示保存的value的编码,通常有以下几种:

#define REDIS_ENCODING_RAW 0            // 编码为字符串
#define REDIS_ENCODING_INT 1            // 编码为整数
#define REDIS_ENCODING_HT 2             // 编码为哈希表
#define REDIS_ENCODING_ZIPMAP 3         // 编码为 zipmap
#define REDIS_ENCODING_LINKEDLIST 4     // 编码为双端链表
#define REDIS_ENCODING_ZIPLIST 5        // 编码为压缩列表
#define REDIS_ENCODING_INTSET 6         // 编码为整数集合
#define REDIS_ENCODING_SKIPLIST 7       // 编码为跳跃表

String

String是简单的 key-value 键值对,value 不仅可以是 String,也可以是数字。String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。

struct sdshdr{
 int len;/*字符串长度*/
 int free;/*未使用的字节长度*/
 char buf[];/*保存字符串的字节数组*/
}

应用场景

  • 缓存功能:String字符串是最常用的数据类型,不仅仅是Redis,各个语言都是最基本类型,因此,利用Redis作为缓存,配合其它数据库作为存储层,利用Redis支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。
  • 计数器:许多系统都会使用Redis作为系统的实时计数器,可以快速实现计数和查询的功能。而且最终的数据结果可以按照特定的时间落地到数据库或者其它存储介质当中进行永久保存。
  • 共享用户Session:用户重新刷新一次界面,可能需要访问一下数据进行重新登录,或者访问页面缓存Cookie,但是可以利用Redis将用户的Session集中管理,在这种模式只需要保证Redis的高可用,每次用户Session的更新和获取都可以快速完成。大大提高效率。

命令

SET key value					设置key=value
GET key							或者键key对应的值
GETRANGE key start end			得到字符串的子字符串存放在一个键
GETSET key value		  		设置键的字符串值,并返回旧值
GETBIT key offset				返回存储在键位值的字符串值的偏移
MGET key1 [key2..]				得到所有的给定键的值
SETBIT key offset value			设置或清除该位在存储在键的字符串值偏移
SETEX key seconds value			键到期时设置值
SETNX key value					设置键的值,只有当该键不存在
SETRANGE key offset value		覆盖字符串的一部分从指定键的偏移
STRLEN key						得到存储在键的值的长度
MSET key value [key value...]	设置多个键和多个值
MSETNX key value [key value...]	设置多个键多个值,只有在当没有按键的存在时
PSETEX key milliseconds value	设置键的毫秒值和到期时间
INCR key						增加键的整数值一次
INCRBY key increment			由给定的数量递增键的整数值
INCRBYFLOAT key increment		由给定的数量递增键的浮点值
DECR key						递减键一次的整数值
DECRBY key decrement			由给定数目递减键的整数值
APPEND key value				追加值到一个键
---------------------------------------------------------------------------------------------
DEL key							如果存在删除键
DUMP key						返回存储在指定键的值的序列化版本
EXISTS key						此命令检查该键是否存在
EXPIRE key seconds				指定键的过期时间
EXPIREAT key timestamp			指定的键过期时间。在这里,时间是在Unix时间戳格式
PEXPIRE key milliseconds		设置键以毫秒为单位到期
PEXPIREAT key milliseconds-timestamp		设置键在Unix时间戳指定为毫秒到期
KEYS pattern					查找与指定模式匹配的所有键
MOVE key db						移动键到另一个数据库
PERSIST key						移除过期的键
PTTL key						以毫秒为单位获取剩余时间的到期键。
TTL key							获取键到期的剩余时间。
RANDOMKEY						从Redis返回随机键
RENAME key newkey				更改键的名称
RENAMENX key newkey				重命名键,如果新的键不存在
TYPE key						返回存储在键的数据类型的值。

Hash

这个是类似 Map 的一种结构,这个一般就是可以将结构化的数据,比如一个对象(前提是这个对象没嵌套其他的对象)给缓存在 Redis 里,然后每次读写缓存的时候,可以就操作 Hash 里的某个字段。但是这个的场景其实还是多少单一了一些,因为现在很多对象都是比较复杂的,比如你的商品对象可能里面就包含了很多属性,其中也有对象。

typedf struct dict{
    dictType *type;//类型特定函数,包括一些自定义函数,这些函数使得key和value能够存储
    void *private;//私有数据
    dictht ht[2];//两张hash表 
    int rehashidx;//rehash索引,字典没有进行rehash时,此值为-1
    unsigned long iterators; //正在迭代的迭代器数量
}dict;

typedef struct dictht{
     //哈希表数组
     dictEntry **table;
     //哈希表大小
     unsigned long size;
     //哈希表大小掩码,用于计算索引值
     //总是等于 size-1
     unsigned long sizemask;
     //该哈希表已有节点的数量
     unsigned long used; 
}dictht;

typedf struct dictEntry{
    void *key;//键
    union{
        void val;
        unit64_t u64;
        int64_t s64;
        double d;
    }v;//值
    struct dictEntry *next;//指向下一个节点的指针
}dictEntry;

应用场景

假设有多个用户及对应的用户信息,可以用来存储以用户ID为key,将用户信息序列化为比如json格式做为value进行保存。

命令

HDEL
HDEL key field[field...] 删除对象的一个或几个属性域,不存在的属性将被忽略
HEXISTS
HEXISTS key field 查看对象是否存在该属性域
HGET
HGET key field 获取对象中该field属性域的值
HGETALL
HGETALL key 获取对象的所有属性域和值
HINCRBY
HINCRBY key field value 将该对象中指定域的值增加给定的value,原子自增操作,只能是integer的属性值可以使用
HINCRBYFLOAT
HINCRBYFLOAT key field increment 将该对象中指定域的值增加给定的浮点数
HKEYS
HKEYS key 获取对象的所有属性字段
HVALS
HVALS key 获取对象的所有属性值
HLEN
HLEN key 获取对象的所有属性字段的总数
HMGET
HMGET key field[field...] 获取对象的一个或多个指定字段的值
HSET
HSET key field value 设置对象指定字段的值
HMSET
HMSET key field value [field value ...] 同时设置对象中一个或多个字段的值
HSETNX
HSETNX key field value 只在对象不存在指定的字段时才设置字段的值
HSTRLEN
HSTRLEN key field 返回对象指定field的value的字符串长度,如果该对象或者field不存在,返回0.
HSCAN
HSCAN key cursor [MATCH pattern] [COUNT count] 类似SCAN命令

List

List列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。List可以简单认为是链表或队列。

typedef struct listNode {
    struct listNode *prev;
    struct listNode *next;
    void *value;
} listNode;

typedef struct listIter {
    listNode *next;
    int direction;
} listIter;

typedef struct list {
    listNode *head;
    listNode *tail;
    void *(*dup)(void *ptr);
    void (*free)(void *ptr);
    int (*match)(void *ptr, void *key);
    unsigned long len;
} list;

应用场景

  1. 比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。
  2. 类似微博那种下拉不断分页等分页展示的功能
  3. 简单的消息队列

命令

BLPOP
BLPOP key1 [key2 ] timeout 取出并获取列表中的第一个元素,或阻塞,直到有可用
BRPOP
BRPOP key1 [key2 ] timeout 取出并获取列表中的最后一个元素,或阻塞,直到有可用
BRPOPLPUSH
BRPOPLPUSH source destination timeout 从列表中弹出一个值,它推到另一个列表并返回它;或阻塞,直到有可用
LINDEX
LINDEX key index 从一个列表其索引获取对应的元素
LINSERT
LINSERT key BEFORE|AFTER pivot value 在列表中的其他元素之后或之前插入一个元素
LLEN
LLEN key 获取列表的长度
LPOP
LPOP key 获取并取出列表中的第一个元素
LPUSH
LPUSH key value1 [value2] 在前面加上一个或多个值的列表
LPUSHX
LPUSHX key value 在前面加上一个值列表,仅当列表中存在
LRANGE
LRANGE key start stop 从一个列表获取各种元素
LREM
LREM key count value 从列表中删除元素
LSET
LSET key index value 在列表中的索引设置一个元素的值
LTRIM
LTRIM key start stop 修剪列表到指定的范围内
RPOP
RPOP key 取出并获取列表中的最后一个元素
RPOPLPUSH
RPOPLPUSH source destination 删除最后一个元素的列表,将其附加到另一个列表并返回它
RPUSH
RPUSH key value1 [value2] 添加一个或多个值到列表
RPUSHX
RPUSHX key value 添加一个值列表,仅当列表中存在

Set

Set理解为一堆值不重复的列表,类似数学领域中的集合概念,且Redis也提供了针对集合的求交集、并集、差集等操作。set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

Set底层编码有两种,分别是intset和hashtable。如果value可以转成整数值,并且长度不超过512的话就使用intset存储,否则采用hashtable。

typedef struct intset{
    uint32_t encoding;//编码方式

    uint32_t length;//集合包含的元素数量

    int8_t contents[];//保存元素的数组
}intset;

应用场景

Set的应用场景可以理解为Set之间的交集、并集以及差集操作。如微博的共同好友,通过求两个用户好友Set的交集得到。

命令

SADD
SADD key member [member ...] 添加一个或者多个元素到集合(set)里
SACRD
SCARD key 获取集合里面的元素数量
SDIFF
SDIFF key [key ...] 获得队列不存在的元素
SDIFFSTORE
SDIFFSTORE destination key [key ...] 获得队列不存在的元素,并存储在一个关键的结果集
SINTER
SINTER key [key ...] 获得两个集合的交集
SINTERSTORE
SINTERSTORE destination key [key ...] 获得两个集合的交集,并存储在一个集合中
SISMEMBER
SISMEMBER key member 确定一个给定的值是一个集合的成员
SMEMBERS
SMEMBERS key 获取集合里面的所有key
SMOVE
SMOVE source destination member 移动集合里面的一个key到另一个集合
SPOP
SPOP key [count] 获取并删除一个集合里面的元素
SRANDMEMBER
SRANDMEMBER key [count] 从集合里面随机获取一个元素
SREM
SREM key member [member ...] 从集合里删除一个或多个元素,不存在的元素会被忽略
SUNION
SUNION key [key ...] 添加多个set元素
SUNIONSTORE
SUNIONSTORE destination key [key ...] 合并set元素,并将结果存入新的set里面
SSCAN
SSCAN key cursor [MATCH pattern] [COUNT count] 迭代set里面的元素

zset

zset是排序的Set。zset添加、删除和测试的时间复杂度均为O(1)(固定时间,无论里面包含的元素集合的数量)。列表的最大长度为 2 32 − 1 2^{32}- 1 2321元素。zset的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

zset的编码有两种,分别是:ziplist、skiplist。当zset的长度小于 128,并且所有元素的长度都小于 64 字节时,使用ziplist存储;否则使用 skiplist 存储。

应用场景

  1. 微博的热搜榜
  2. 权重消息队列。比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务,让重要的任务优先执行。

命令

ZADD
ZADD key score1 member1 [score2 member2] 添加一个或多个成员到有序集合,或者如果它已经存在更新其分数
ZCARD
ZCARD key 得到的有序集合成员的数量
ZCOUNT
ZCOUNT key min max 计算一个有序集合成员与给定值范围内的分数
ZINCRBY
ZINCRBY key increment member 在有序集合增加成员的分数
ZINTERSTORE
ZINTERSTORE destination numkeys key [key ...] 多重交叉排序集合,并存储生成一个新的键有序集合。
ZLEXCOUNT
ZLEXCOUNT key min max 计算一个给定的字典范围之间的有序集合成员的数量
ZRANGE
ZRANGE key start stop [WITHSCORES] 由索引返回一个成员范围的有序集合(从低到高)
ZRANGEBYLEX
ZRANGEBYLEX key min max [LIMIT offset count]返回一个成员范围的有序集合(由字典范围)
ZRANGEBYSCORE
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 返回有序集key中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员,有序集成员按 score 值递增(从小到大)次序排列
ZRANK
ZRANK key member 确定成员的索引中有序集合
ZREM
ZREM key member [member ...] 从有序集合中删除一个或多个成员,不存在的成员将被忽略
ZREMRANGEBYLEX
ZREMRANGEBYLEX key min max 删除所有成员在给定的字典范围之间的有序集合
ZREMRANGEBYRANK
ZREMRANGEBYRANK key start stop 在给定的索引之内删除所有成员的有序集合
ZREMRANGEBYSCORE
ZREMRANGEBYSCORE key min max 在给定的分数之内删除所有成员的有序集合
ZREVRANGE
ZREVRANGE key start stop [WITHSCORES] 返回一个成员范围的有序集合,通过索引,以分数排序,从高分到低分
ZREVRANGEBYSCORE
ZREVRANGEBYSCORE key max min [WITHSCORES] 返回一个成员范围的有序集合,以socre排序从高到低
ZREVRANK
ZREVRANK key member 确定一个有序集合成员的索引,以分数排序,从高分到低分
ZSCORE
ZSCORE key member 获取给定成员相关联的分数在一个有序集合
ZUNIONSTORE
ZUNIONSTORE destination numkeys key [key ...] 添加多个集排序,所得排序集合存储在一个新的键
ZSCAN
ZSCAN key cursor [MATCH pattern] [COUNT count] 增量迭代排序元素集和相关的分数

Bitmap

位图是支持按 bit 位来存储信息,可以用来实现 布隆过滤器(BloomFilter)。Bitmap 的底层数据结构用的是 String 类型的 SDS 数据结构来保存位数组,Redis 把每个字节数组的 8 个 bit 位利用起来,每个 bit 位 表示一个元素的二值状态(不是 0 就是 1)。

应用场景

  1. 用户签到
  2. 统计活跃用户
  3. 两亿用户最近 7 天的签到情况,统计 7 天内连续签到的用户总数。

命令

SETBIT <key> <offset> <value>
GETBIT <key> <offset>

可参考:Redis 实战篇:巧用 Bitmap 实现亿级数据统计

HyperLogLog

供不精确的去重计数功能,比较适合用来做大规模数据的去重统计,例如统计 UV;

struct hllhdr {
    char magic[4];      /* "HYLL",对应源码注释中的HYLL*/
    uint8_t encoding;   /* HLL_DENSE or HLL_SPARSE.稀疏/密集存储结构标记,对应源码注释中的E */
    uint8_t notused[3]; /* 保留3字节备用,目前未使用,值为0,对应源码注释中的N/U */
    uint8_t card[8];    /* Cached cardinality(基数缓存), little endian. 对应源码注释中的Cardin,cardinality<基数> */
    uint8_t registers[]; /* Data bytes. */
};

应用场景

  • 统计注册 IP 数
  • 统计每日访问 IP 数
  • 统计页面实时 UV 数
  • 统计在线用户数
  • 统计用户每天搜索不同词条的个数

命令

PFADD key element 
PFCOUNT key 
PFMERGE destkey sourcekey

Geospatial

可以用来保存地理位置,并作位置距离计算或者根据半径计算位置等。

应用场景

记录地理位置,计算距离以及附近的人。

命令

geoadd:添加地理位置的坐标。
geopos:获取地理位置的坐标。
geodist:计算两个位置之间的距离。
georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
geohash:返回一个或多个位置对象的 geohash 值。

参考

  1. 《Redis设计与实现》
  2. 《进大厂系列》系列-Redis常见面试题(带答案)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值