Redis安装和数据结构

目录


  • `String`

    • 简介

    • 命令

    • 应用场景

    • 底层结构和源码分析

  • `List`

    • 简介

    • 命令

    • 应用场景

    • 底层结构和源码分析

  • `Set`

    • 简介

    • 命令

    • 应用场景

    • 底层结构和源码分析

  • `Hash`

    • 简介

    • 命令

    • 应用场景

    • 底层结构和源码分析

  • `Sorted set`

    • 简介

    • 命令

    • 应用场景

    • 底层结构和源码分析

  • `Stream`

    • 简介

    • 命令

    • 应用场景

  • `Geospatial`

    • 简介

    • 命令

    • 应用场景

  • `HyperLogLog`

    • 简介

    • 命令

    • 应用场景

  • `Bitmap`

    • 简介

    • 命令

    • 应用场景


环境说明

Redis 版本:redis 7.0.2

阅读源码软件:Understand(个人推荐,挺好用)

画图软件:ProcessOn

Docker 安装Redis

# 拉取最新版本镜像
docker pull redis:latest
# 查看镜像
docker images
# 启动容器
docker run 
-p 6379:6379 --name redis 
-v/usr/local/redis/redis.conf:/etc/redis/redis.conf 
-v /usr/l/redis/data:/data:rw 
-d redis redis-server /etc/redis/redis.conf

进入容器使用redis-cli命令连接Redis server。

使用info命令可以查看server信息:

# Server
redis_version:6.2.6
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:b61f37314a089f19
redis_mode:standalone
os:Linux 3.10.0-1160.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:10.2.1
process_id:1
process_supervised:no
run_id:b0cdd29712a779526f2b154990f92fb53bbe934b
tcp_port:6379
server_time_usec:1661348618573300
uptime_in_seconds:327
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:404234
executable:/data/redis-server
config_file:
io_threads_active:0

#
 Clients
connected_clients:1
cluster_connections:0
maxclients:10000
client_recent_max_input_buffer:16
client_recent_max_output_buffer:0
blocked_clients:0
tracking_clients:0
clients_in_timeout_table:0

#
 Memory
used_memory:873608
used_memory_human:853.13K
used_memory_rss:10076160
used_memory_rss_human:9.61M
used_memory_peak:931736
used_memory_peak_human:909.90K
used_memory_peak_perc:93.76%
used_memory_overhead:830376
used_memory_startup:809880
used_memory_dataset:43232
used_memory_dataset_perc:67.84%
allocator_allocated:1051152
allocator_active:1273856
allocator_resident:3788800
total_system_memory:3953963008
total_system_memory_human:3.68G
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.21
allocator_frag_bytes:222704
allocator_rss_ratio:2.97
allocator_rss_bytes:2514944
rss_overhead_ratio:2.66
rss_overhead_bytes:6287360
mem_fragmentation_ratio:12.13
mem_fragmentation_bytes:9245320
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:20496
mem_aof_buffer:0
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0
lazyfreed_objects:0

#
 Persistence
loading:0
current_cow_size:0
current_cow_size_age:0
current_fork_perc:0.00
current_save_keys_processed:0
current_save_keys_total:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1661348291
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0
aof_enabled:0
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_last_cow_size:0
module_fork_in_progress:0
module_fork_last_cow_size:0

#
 Stats
total_connections_received:1
total_commands_processed:1
instantaneous_ops_per_sec:0
total_net_input_bytes:31
total_net_output_bytes:20324
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:0
expired_stale_perc:0.00
expired_time_cap_reached_count:0
expire_cycle_cpu_milliseconds:9
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
total_forks:0
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
tracking_total_keys:0
tracking_total_items:0
tracking_total_prefixes:0
unexpected_error_replies:0
total_error_replies:0
dump_payload_sanitizations:0
total_reads_processed:2
total_writes_processed:1
io_threaded_reads_processed:0
io_threaded_writes_processed:0

#
 Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:ce9dfeb406ff6a10988f3bda6f70c46f65061962
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

#
 CPU
used_cpu_sys:0.566599
used_cpu_user:0.603875
used_cpu_sys_children:0.010103
used_cpu_user_children:0.001248
used_cpu_sys_main_thread:0.560288
used_cpu_user_main_thread:0.605111

#
 Modules

#
 Errorstats

#
 Cluster
cluster_enabled:0

#
 Keyspace

数据结构

String

简介

string类型存储字节序列,可以包括数字类型、文本、序列化对象等等。默认情况下最大大小为512MB。

命令

命令:SET key value [NX | XX] [GET] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]

  • EX,失效时间,单位秒
  • PX,失效时间,单位毫秒
  • EXAT,失效时间,单位时间戳(秒),到达时间戳时失效
  • PXAT,失效时间,单位时间戳(毫秒)
  • NX,不存在key时设置
  • XX,存在key时设置

示例:

set name erpang 
get name
set name erpang nx
set order:lock 1 EX 10 NX # 简单分布式锁实现,超时时间为10s的锁

应用场景

单值缓存

比如缓存某个账号的Token:

 # 缓存账号名为erpang的token 123456 10分钟,10分钟后获取不到token可以
 set erpang:token 123456 ex 600

对象缓存

比如缓存某个用户的信息:

SET user:10 ""{'username': 'erpang', 'age': 25}""

分布式锁

比如锁住某个任务:

# 根据任务ID获取锁,因为有nx,所有并发情况下只有一个线程会执行成功
set task:1000 true ex 60 nx 
# 执行业务

#
 释放锁
del task:1000

但其实这个锁存在问题,比如业务执行时间超过了过期时间,会有其他线程也能获取到锁。

计数器

通过Redis原子操作INCR(返回值为key的value)进行计数:

# 给博客ID为121的阅读量增加1
incr blog:readcount:121

#
 给博客ID为122的评论数增加1
incr blog:comment:122

分布式ID生成

通过Redis原子操作INCRBY批量生成ID:

# 第一次运行
# 生成3000个ID
incrby global_id 3000
# 返回值为3000
# 3000 == 3000,则可以使用[0, 3000]作为ID

#
 再生成3000个ID
incrby global_id 3000
# 返回值为6000
# 则可以使用(6000 - 3000, 6000]作为ID

底层结构和源码分析

Redis底层并没有使用C原生的字符串表示,可以使用了一个结构体(SDS,Simple Dynamic String)用来表示字符串:

/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */

struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

这个结构体有四个属性:

  • len,记录字符串长度,获取字符串长度,相比C的性能更好
  • alloc,已分配内存容量,可以避免缓冲区溢出
  • buf,存储数据的字节数组,内存分配时预分配内存空间,避免扩充频繁申请内存,在缩减字符串时,使用惰性空间释放,即修改alloc的值,让程序访问不到,而不需要立刻重新分配内存回收多出来的字节
  • flags,标志位,根据buf字符串的长度不同,可以使用不同的结构体存储,前三位表示存储类型,后5为用于小字符串时存储长度(一种优化手段)

List

简介

list结构通常用来实现栈和队列,最大长度为

命令

  • LPUSH,链表头部插入
  • RPUSH,链表尾部插入
  • LPOP,弹出链表头部元素
  • RPOP,弹出链表尾部元素
  • LLEN,获取链表长度
  • LMOVE,移动链表元素到另外一个链表
  • LTRIM,截取指定区域的链表
  • BLPOP,阻塞弹出头部元素(当链表为空时,命令阻塞)
  • BLMOVE,阻塞移动(当源链表为空时,命令阻塞)

应用场景

实现栈

通过命令LPUSH(入栈)和LPOP(出栈)可以实现栈操作。

127.0.0.1:6379> lpush stack 1
(integer) 1
127.0.0.1:6379> lpush stack 2
(integer) 2
127.0.0.1:6379> lpush stack 3
(integer) 3
127.0.0.1:6379> lpop stack
"3"
127.0.0.1:6379> lpop stack 2
1) "2"
2) "1"
127.0.0.1:6379> 

实现队列

通过命令LPUSHRPOP实现队列

127.0.0.1:6379> lpush queue 1
(integer) 1
127.0.0.1:6379> lpush queue 2
(integer) 2
127.0.0.1:6379> lpush queue 3
(integer) 3
127.0.0.1:6379> rpop queue
"1"
127.0.0.1:6379> rpop queue 2
1) "2"
2) "3"

阻塞队列

使用命令LPUSHBRPOP实现阻塞队列。

127.0.0.1:6379> lpush block_queue 1
(integer) 1
127.0.0.1:6379> brpop block_queue 5
1) "block_queue"
2) "1"
127.0.0.1:6379> brpop block_queue 5
(nil)
(5.06s)

消息

通过队列可以实现比如公众号图文消息推送

# erpang公众号发送消息,ID为32101
127.0.0.1:6379> lpush msg:erpang 32101
(integer) 1
127.0.0.1:6379> lpush msg:erpang 24123
(integer) 2
127.0.0.1:6379> lpush msg:erpang 3432
(integer) 3
# 查看erpang公众号最新两条消息
127.0.0.1:6379> rpop msg:erpang 2
1) "32101"
2) "24123"

底层结构和源码分析

Redis的list实现和普通Clist的实现方式差不多一致,是一个双向无环链表结构。

/* Node, List, and Iterator are the only data structures used currently. */
// 节点结构体
typedef struct listNode {
    struct listNode *prev; // 前节点指针
    struct listNode *next; // 后节点指针
    void *value; // 数据,void指针可以存放任意数据类型
} listNode;

// 链表
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;

还有一种实现方式ziplist压缩列表,是一种为节约内存而开发的顺序型数据结构。

Set

简介

set是元素无序唯一的集合数据结构,最大元素个数为个。

Redis提供了方便操作集合的命令用于求集合的交集、并集、差集等。

命令

  • SADD,添加元素到集合
  • SREM,从集合移除元素
  • SISMEMBER,判断是否属于集合元素
  • SINTER,求两个集合的交集
  • SINTERSTORE,求两个集合的交集并存入指定集合中
  • SDIFF,求两个集合的差集
  • SDIFFSTORE,求两个集合的差集并存入指定集合中
  • SUNION,求两个集合的并集
  • SUNIONSTORE,求两个集合的并集并存入指定集合中
  • SCARD,获取集合的大小
  • SPOP,移除并获取指定个集合元素数据
  • SRAMDMEMBER,获取指定个集合元素数据
  • SMEMBERS,查看集合元素

应用场景

IP黑白名单

可以在set中放黑白名单的IP,控制访问:

127.0.0.1:6379> sadd block_ips "10.34.21.4" "10.31.25.19" "10.21.42.123"
(integer) 3
127.0.0.1:6379> sismember block_ips "10.34.21.4"
(integer) 1
127.0.0.1:6379> sismember block_ips "10.124.21.105"
(integer) 0

奖池抽奖

可以在set中添加用户ID,通过SRANDMEMBER或者SPOP随机取出N个获奖者:

127.0.0.1:6379> sadd activity:1001 3424 2345 8932 1315
(integer) 4
127.0.0.1:6379> SRANDMEMBER activity:1001 2
1) "2345"
2) "1315"

关注模型

可以通过集合运算实现关注模型:

127.0.0.1:6379> sadd erpang:follow "yezhi" "aquan" "chengyuan"
(integer) 3
127.0.0.1:6379> sadd yezhi:follow "erpang" "shiqi" "aquan"
(integer) 3
127.0.0.1:6379> sadd aquan:follow "yezhi" "chengyuan" "lanjing"
(integer) 3
# erpang 和 yezhi 的共同关注
127.0.0.1:6379> sinter erpang:follow yezhi:follow
1) "aquan"
# erpang 在 yezhi 的关注者中可能认识的人
127.0.0.1:6379> sdiff yezhi:follow erpang:follow
1) "shiqi"
2) "erpang"

底层结构和源码分析

Redis Set底层结构可能为两种,一种是整数集合结构,另一种是哈希表。如果存储的都是整数且元素个数小于set-max-iniset-entries(默认512)使用整数集合结构,否则使用哈希表。

整数集合结构:

typedef struct intset {
    uint32_t encoding; // 数据编码,决定了content中元素的类型
    uint32_t length; // 元素总数
    int8_t contents[]; // 集合存储结构数组
} intset;

intset *intsetNew(void);
intset *intsetAdd(intset *is, int64_t value, uint8_t *success);
intset *intsetRemove(intset *is, int64_t value, int *success);
uint8_t intsetFind(intset *is, int64_t value);
int64_t intsetRandom(intset *is);
uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value);
uint32_t intsetLen(const intset *is);
size_t intsetBlobLen(intset *is);
int intsetValidateIntegrity(const unsigned char *is, size_t size, int deep);

看一下集合添加元素的实现:

/* Insert an integer in the intset */
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
    uint8_t valenc = _intsetValueEncoding(value);
    uint32_t pos;
    if (success) *success = 1;

    /* Upgrade encoding if necessary. If we need to upgrade, we know that
     * this value should be either appended (if > 0) or prepended (if < 0),
     * because it lies outside the range of existing values. */

    if (valenc > intrev32ifbe(is->encoding)) {
        /* This always succeeds, so we don't need to curry *success. */
        return intsetUpgradeAndAdd(is,value);
    } else {
        /* Abort if the value is already present in the set.
         * This call will populate "pos" with the right position to insert
         * the value when it cannot be found. */

        if (intsetSearch(is,value,&pos)) {
            if (success) *success = 0;
            return is;
        }

        is = intsetResize(is,intrev32ifbe(is->length)+1);
        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
    }

    _intsetSet(is,pos,value);
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}

可以看到判断集合中已经有没有这个元素是通过遍历content数组实现的。

同样,因为底层数据结构是数组,当SADD的都是整数时,其实这个集合是有序的。

另外一种实现是哈希表:

typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;     /* Next entry in the same hash bucket. */
    void *metadata[];           /* An arbitrary number of bytes (starting at a
                                 * pointer-aligned address) of size as returned
                                 * by dictType's dictEntryMetadataBytes(). */

} dictEntry;

struct dict {
    dictType *type;

    dictEntry **ht_table[2]; //一个[2][]矩阵,[0][]用于真正的数据存储,[1][]用于rehash时使用
    unsigned long ht_used[2]; // 记录数据数目

    long rehashidx; /* rehashing not in progress if rehashidx == -1 */

    /* Keep small vars at end for optimal (minimal) struct padding */
    int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */
    signed char ht_size_exp[2]; /* exponent of size. (size = 1<<exp) */ 哈希表大小2的幂值
};

typedef struct dictType {
    uint64_t (*hashFunction)(const void *key);
    void *(*keyDup)(dict *d, const void *key);
    void *(*valDup)(dict *d, const void *obj);
    int (*keyCompare)(dict *d, const void *key1, const void *key2);
    void (*keyDestructor)(dict *d, void *key);
    void (*valDestructor)(dict *d, void *obj);
    int (*expandAllowed)(size_t moreMem, double usedRatio);
    /* Allow a dictEntry to carry extra caller-defined metadata.  The
     * extra memory is initialized to 0 when a dictEntry is allocated. */

    size_t (*dictEntryMetadataBytes)(dict *d);
} dictType;

dicEntry哈希表节点保存着一个键值对,next指针指向下一个节点。

dict主要结构**ht_table[2],可以理解为一个两行多列的矩阵,行1是实际存储结构,行2是rehash时辅助用

type属性是针对不同类型,为了创建多态字典而设置的,定义了不同类型的函数指针,比如计算hash的hashFunction

通过计算哈希值,与哈希表的大小运算,计算数据应该存放的位置:

/* Get the index of the new element, or -1 if
     * the element already exists. */

    if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)
        return NULL;

当出现键冲突时,采用头节点插入的拉链法解决。

随着数据量的增多或者减少,哈希表的负载因子不合理后,会进行rehash操作,需要扩容、收缩哈希表。分配ht_table[1]空间,把ht_table[0]的所有KV rehash后放到ht_table[1]上,迁移完成之后,释放ht_table[0],把ht_table[1]设置为ht_table[0],为下一次rehash做准备。

图示:

Redis安装和数据结构

Hash

简介

hash是存储结构化的key-value数据结构,可以用来存储对象数据,每个hash结构可以存储最多个key-value数据。

命令

  • HSET,设置key-value值
  • HGET,获取key的value值
  • HMGET,获取多个key的value值
  • HINCRBY,增加某个key的value值

应用场景

对象缓存

127.0.0.1:6379> HSET user:erpang name "erpang" age 18 gender 1
(integer) 3
127.0.0.1:6379> HGET user:erpang name
"erpang"
127.0.0.1:6379> HMGET user:erpang name age gender
1) "erpang"
2) "18"
3) "1"

底层结构和源码分析

一种实现是哈希表同上,另一种实现是压缩列表。

Sorted set

简介

Redis Sorted set是按照元素权重排序后的结构。

命令

  • ZADD,添加带权重的元素,如果已经存在该元素,则覆盖权重
  • ZRANGE,获取权重为指定区间的元素
  • ZRANK,获取指定元素的权重排名位置,默认升序
  • ZREVRANK,获取指定元素的权重排名位置,默认降序
  • ZUNIONSTORE,合并集合(可以再给每个集合指定权重)并排序

应用场景

排行榜

# 游戏lol a 服务器玩家分数数据
127.0.0.1:6379> ZADD game:lol:server:a 100 erpang 89 yezhi 34 aquan
(integer) 3
# 游戏lol b 服务器玩家分数数据
127.0.0.1:6379> ZADD game:lol:server:b 32 shiqi 98 beibei 10 tingting
(integer) 3
# 查看游戏lol a 服务器 前两名玩家
127.0.0.1:6379> ZREVRANGE game:lol:server:a 0 1 WITHSCORES
1) "erpang"
2) "100"
3) "yezhi"
4) "89"
# 合并游戏lol a服务器和b服务器的玩家到 game:lol:server:a_b 集合(2指定的是后面需要合并的集合数量)
127.0.0.1:6379> ZUNIONSTORE game:lol:server:a_b 2 game:lol:server:a game:lol:server:b
(integer) 6
# 查看lol a和b两个服务器玩家分数排名前五的玩家
127.0.0.1:6379> ZREVRANGE game:lol:server:a_b 0 4 WITHSCORES
 1) "erpang"
 2) "100"
 3) "beibei"
 4) "98"
 5) "yezhi"
 6) "89"
 7) "aquan"
 8) "34"
 9) "shiqi"
10) "32"

底层结构和源码分析

跳跃表(skiplist)是一种有序数据结构,支持平均,最坏复杂度的节点查找,还可以通过顺序操作批量处理节点。这种思想是典型的用空间换时间的思想,建立很多索引,实现近似二分查找的效率。

/* ZSETs use a specialized version of Skiplists */
typedef struct zskiplistNode {
    sds ele; // 数据
    double score; // 权重
    struct zskiplistNode *backward; // 指向前一个节点的指针
    struct zskiplistLevel {
        struct zskiplistNode *forward; // 指向后一个节点的指针
        unsigned long span;  // 跨度
    } level[];
} zskiplistNode;

typedef struct zskiplist {
    struct zskiplistNode *header, *tail; // 头、尾指针
    unsigned long length;  // 元素长度
    int level;  // 节点最大层
} zskiplist;

typedef struct zset {
    dict *dict;     // 
    zskiplist *zsl; // 跳表
} zset;

图示:

Redis安装和数据结构

几个关键点:

  • 创建节点时level是随机生成的,介于1-32之间的数作为level数组的大小,头节点的level大小为32,这样forward指针可以指向任何一层
  • dict结构体的level记录的是所有节点最大的层数,目的是当需要查找值时,从这个level层出发,则一定定位到最高层索引,从这一层出发开始查找
  • 从头节点出发,使用forward指针遍历第一层节点可以正序遍历所有节点
  • 从尾节点出发,使用backword指针可以倒叙遍历所有节点
  • 从头节点出发,使用forward指针查找到某个值时,途径的节点span之和即为数据的正序排名
  • zset结构中的dict存储的是key-score 键值对,用于快速获取key的权重,比如zscore命令

Stream

简介

Redis 的stream数据结构是一种时许数据的存储结构。

命令

  • XADD,添加时序数据,如果不指定ID,由Redis自己生成,格式为时间戳-序列号
  • XREAD,获取指定Key的stream的指定ID以后的数据序列
  • XRANGE,查询指定Key的stream [start, end]区间的数据序列,start和end可以使用-+表示无穷小和无穷大
  • XLEN,查询指定Key的stream的长度

应用场景

监控时序数据

# 服务器127.0.0.1 服务器当前时间监控数据
127.0.0.1:6379> XADD server:127.0.0.1 * cpu 80 mem 20
"1661361382926-0"
127.0.0.1:6379> XADD server:127.0.0.1 * cpu 87 mem 23
"1661361391647-0"
127.0.0.1:6379> XADD server:127.0.0.1 * cpu 32 mem 62
"1661361402220-0"
# 查看1661361382926-0 以后的监控数据
127.0.0.1:6379> XREAD STREAMS server:127.0.0.1 1661361382926-0
1) 1) "server:127.0.0.1"
   2) 1) 1) "1661361391647-0"
         2) 1) "cpu"
            2) "87"
            3) "mem"
            4) "23"
      2) 1) "1661361402220-0"
         2) 1) "cpu"
            2) "32"
            3) "mem"
            4) "62"
# 查看服务器所有监控数据(如果指定开始和结束,则可以查询某个时间段的监控数据)
127.0.0.1:6379> XRANGE server:127.0.0.1 - +
1) 1) "1661361382926-0"
   2) 1) "cpu"
      2) "80"
      3) "mem"
      4) "20"
2) 1) "1661361391647-0"
   2) 1) "cpu"
      2) "87"
      3) "mem"
      4) "23"
3) 1) "1661361402220-0"
   2) 1) "cpu"
      2) "32"
      3) "mem"
      4) "62"

Geospatial

简介

地理位置坐标数据结构,非常适用于给定坐标点和半径、范围查找。

命令

  • GEOADD,添加坐标
  • GEOSEARCH,查找指定半径或边界数据

应用场景

附近的人

# 添加两个人的经纬度
127.0.0.1:6379> GEOADD wechat:position 120.184071 30.216860 erpang 120.202268 30.192678 yezhi
(integer) 2
# 查询erpang 附件10km的数据
127.0.0.1:6379> GEOSEARCH wechat:position FROMMEMBER erpang BYRADIUS 10 km
1) "erpang"
2) "yezhi"
# 查询erpang 附件3km的数据
127.0.0.1:6379> GEOSEARCH wechat:position FROMMEMBER erpang BYRADIUS 3 km
1) "erpang"

HyperLogLog

简介

一种概率性集合基数估算算法(不会存储数据),Redis HyperLogLog实现使用12KB达到估算误差0.81%。最大可以预估 个元素的数据集基数。

命令

  • PFADD,添加元素
  • PFCOUNT,预估基数
  • PFMERGE,合并

应用场景

基数预估

在不要求完全正确的基数预估下以很小的内存空间得到误差很小的基数预估。

Bitmap

简介

Redis Bitmapstring数据结构的一种扩展,可以把string当作位图。

命令

  • SETBIT,设置offset的位置为1
  • GETBIT,获取offset位置的值
  • BITOP,位运算

应用场景

用户签到

# 8.2 号签到
127.0.0.1:6379> SETBIT erpang:202208 2 1
(integer) 0
# 8.15 号签到
127.0.0.1:6379> SETBIT erpang:202208 15 1
(integer) 0
127.0.0.1:6379> SETBIT erpang:202208 3 1
(integer) 0
# 8月份签到多少天
127.0.0.1:6379> BITCOUNT erpang:202208
(integer) 3

统计用户数

# 用户43235313 参与了活动1004
127.0.0.1:6379> SETBIT activity:1004 43235313 1
(integer) 0
# 用户54343234 参与了活动 1004
127.0.0.1:6379> SETBIT activity:1004 54343234 1
(integer) 0
# 统计活动1004的参与用户数
127.0.0.1:6379> BITCOUNT activity:1004
(integer) 2

参考资料

  • Docker教程
  • Redis高级数据结构之stream数据类型(消息队列)
  • 《Redis设计与实现》
  • Redis源码解析-有赞
  • Redis(2)——跳跃表


原文始发于微信公众号(erpang coding):Redis安装和数据结构

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/37389.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!