redis数据结构

redis数据结构

redis全名(Remote Dictionary Server),即远程字典服务

redis的值的数据结构类型有String、List、Set、Hash、zset(sorted set,有序集合)、Bitmaps(位图)、HyperLogLogs

注意:我使用的版本是6.0.10,不同版本可能略有差别

redis中key的最大长度为512M

对象

server.h

任何value对象都会被包装成一个redisObject,redisObject能指定value类型,编码方式等数据属性

typedef struct redisObject {
   // 对象的类型 字符串、列表、集合、哈希表
    unsigned type:4;
   // 编码方式,也就是存储方式,提供多种方式来保存一个数据
    unsigned encoding:4;
   // 记录最后一次被访问的时间
    unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
                            * LFU data (least significant 8 bits frequency
    // 引用计数                        * and most significant 16 bits access time). */

    int refcount;
   // 指向底层实现的指针
    void *ptr;
} robj;
类型总览

redis中的数据类型,即为redisObject的type属性

// string
#define OBJ_STRING 0    /* String object. */
// list
#define OBJ_LIST 1      /* List object. */
// set
#define OBJ_SET 2       /* Set object. */
//zset
#define OBJ_ZSET 3      /* Sorted set object. */
//hash
#define OBJ_HASH 4      /* Hash object. */
// module
#define OBJ_MODULE 5    /* Module object. */
// stream
#define OBJ_STREAM 6    /* Stream object. */
#可以使用type命令来查看该键所对应值的类型
type key
redis的编码分类

对应于redisObject的ecoding属性,encoding是决定对象底层实现数据结构的

// 简单动态字符串  raw
#define OBJ_ENCODING_RAW 0     /* Raw representation */
// long类型的整数 int
#define OBJ_ENCODING_INT 1     /* Encoded as integer */
// 哈希表  hashtable
#define OBJ_ENCODING_HT 2      /* Encoded as hash table */
// 压缩  不再使用
#define OBJ_ENCODING_ZIPMAP 3  /* Encoded as zipmap */
// 双向链表  不再使用该结构
#define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */
// 压缩列表  ziplist
#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
// 整数集合  intset
#define OBJ_ENCODING_INTSET 6  /* Encoded as intset */
// 跳表  skiplist
#define OBJ_ENCODING_SKIPLIST 7  /* Encoded as skiplist */
// embstr编码的简单动态字符串  embstr
#define OBJ_ENCODING_EMBSTR 8  /* Embedded sds string encoding */
// 快表  quicklist
#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
// 流  stream
#define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */

可以使用命令来查看存储方式

object encoding key

来查看该key使用的是哪种编码

127.0.0.1:6379set test_int 1
OK
127.0.0.1:6379> object encoding test_int
"int"
127.0.0.1:6379set test_raw zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
OK
127.0.0.1:6379strlen test_raw
(integer) 44
127.0.0.1:6379> object encoding test_raw
"embstr"
127.0.0.1:6379> set test_raw zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
OK
127.0.0.1:6379> object encoding test_raw
"raw"
127.0.0.1:6379> strlen test_raw
(integer) 45
# 查看key的内部结构和编码等信息   
>debug object k4
  Value at:0x7ffc97c044f0 refcount:1 encoding:embstr serializedlength:3 lru:11351195 lru_seconds_idle:5363

String类型

string类型属于单值单value,是一个可变的字节数组,就像StringBuilder似的,可以追加、获取长度、截取等操作

扩容:当字符串长度小于1M时,扩容是现有空间进行加倍;当字符串长度超过1M,扩容只会多扩1M的空间。且字符串最长为512M

源码结构

sds.h其内部使用的是SDS简单动态字符串,保存了长度信息,获取字符串长度只需要O(1)

struct __attribute__ ((__packed__)) sdshdr5 {
   // 存储了字符串的类型和长度,3位存类型,5位存长度
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
// 暂时应该是没有用到5的,只用到了8、16、32、64
struct __attribute__ ((__packed__)) sdshdr8 {
  // header中进行存储len、alloc、flags
  // 当前使用空间大小,即字符串的长度
    uint8_t len; /* used */
  // 去掉header和结尾空字符后的最大空间
    uint8_t alloc; /* excluding the header and null terminator */
  // 8位的标记,3位存类型,5位没有使用
    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[];
};
编码格式
编码对象
OBJ_ENCODING_INT使用整数值实现的字符串对象
OBJ_ENCODING_EMBSTR使用embstr编码的简单动态字符串实现的字符串对象
OBJ_ENCODING_RAW使用简单动态字符串实现的字符串对象

string有三种编码格式

  • int 整数值

  • embstr 用于存储短字符串的编码方式,字符串长度小于等于44字节使用,数据保存在一块连续的内存里

  • raw 字符串值长度大于44字节使用

embstr和raw两种编码的区别

embstr编码用于保存短字符串,与raw编码一样,都是使用redisObject结构和sdshdr结构来表示字符串对象,但是raw编码会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的内存空间,空间中依次包含redisObject和sdshdr两个结构

操作命令

值拼接


#
给key对应的value进行拼接,返回的是拼接之后的字符长度
append k1 qwe
(integer) 5

值长度

strlen k1

数字运算操作

#加一
INCR k2
#减一
DECR k2
#加3,第二个参数是步长
INCRBY k2 3
#减2,第二个参数是步长
DECRBY k2 2

注意:这几个命令只能对数字进行操作,范围是Long.MIN~Long.MAX

获取值

get k1

#
只获取该key所对应value的部分字符
getrange k1 0 2

判断值是否存在

# 返回0则为不存在
exists test

设置值

# set key value
set test 123

#
 setrange覆盖部分值  其中0为从下标为0的字符开始替换
setrange k1 0 zx
(integer) 5

#
设置值时设置过期时间
#setex key seconds value
setex k3 20 b

#
如果key不存在设置值,防止覆盖(set if not exists)
#setnx key value
setnx k1 1

#
 设置key的值,并返回key的旧值
getset k1 vv1

多值操作

#同时set多个键值
#mset key value [key value ...]
mset k4 v4 k5 v5
#同时获取多个键的值
#mget key  [key ...]
mget k4 k5 
#同时set多个键值(全部不存在时才会设置值)
#msetnx key value [key value ...]
msetnx k6 v6 k7 k8

List类型

List属于单值多value,链表用的是双向链表结构,list支持pop、push来操作头部和尾部,既可以用做栈也可以用做队列

在3.2版本之前使用的是ziplist和linkedlist,在3.2版本之后使用的是quicklist

源码结构
quicklist结构
typedef struct quicklist {
    quicklistNode *head;
    quicklistNode *tail;
    unsigned long count;        /* total count of all entries in all ziplists */
    unsigned long len;          /* number of quicklistNodes */
    int fill : QL_FILL_BITS;              /* fill factor for individual nodes */
    unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */
    unsigned int bookmark_count: QL_BM_BITS;
    quicklistBookmark bookmarks[];
} quicklist;

typedef struct quicklistNode {
    struct quicklistNode *prev;
    struct quicklistNode *next;
    unsigned char *zl; // 对应的是ziplist
    unsigned int sz;             /* ziplist size in bytes */
    unsigned int count : 16;     /* count of items in ziplist */
    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */
    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */
    unsigned int recompress : 1/* was this node previous compressed? */
    unsigned int attempted_compress : 1/* node can't compress; too small */
    unsigned int extra : 10/* more bits to steal for future usage */
} quicklistNode;
ziplist结构

压缩的双链表,以连续的空间来表示双链表,节省前驱和后继指针的空间,在小的list上,压缩效率明显,因为在普通的双链表中,前驱和后继指针在64位机器上分别占用8B,在ziplist.h和ziplist.c中定义

typedef struct zlentry {
    unsigned int prevrawlensize; /* Bytes used to encode the previous entry len*/
    unsigned int prevrawlen;     /* Previous entry len. */
    unsigned int lensize;        /* Bytes used to encode this entry type/len.
                                    For example strings have a 1, 2 or 5 bytes
                                    header. Integers always use a single byte.*/

    unsigned int len;            /* Bytes used to represent the actual entry.
                                    For strings this is just the string length
                                    while for integers it is 1, 2, 3, 4, 8 or
                                    0 (for 4 bit immediate) depending on the
                                    number range. */

    unsigned int headersize;     /* prevrawlensize + lensize. */
    unsigned char encoding;      /* Set to ZIP_STR_* or ZIP_INT_* depending on
                                    the entry encoding. However for 4 bits
                                    immediate integers this can assume a range
                                    of values and must be range-checked. */

    unsigned char *p;            /* Pointer to the very start of the entry, that
                                    is, this points to prev-entry-len field. */

} zlentry;

typedef struct {
    /* When string is used, it is provided with the length (slen). */
    unsigned char *sval;
    unsigned int slen;
    /* When integer is used, 'sval' is NULL, and lval holds the value. */
    long long lval;
} ziplistEntry;

而对于quicklist编码的定义是

OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
操作命令

设置值

#lpush(指从左边进,头部,先进后出,像栈)
#lpush key element [element ...]
lpush list1 1 2 3 4 5

#
rpush(指从右边进,尾部,先进先出,像队列)
#rpush key element [element ...]
rpush list2 1 2 3 4 5

#
给某个索引位置赋值
#lset key index element
lset list1 0 q

#
在某个元素之前或之后插入一个元素
#linsert key before|after pivot element
linsert list1 before q zz

#
 获取指定索引的元素,如果使用负数,则从右边开始计算
lindex list1 0

取值

#弹出栈顶元素(从左边开始弹出,弹出之后,list中就不存在该元素了)
#lpop key 
lpop list1
#弹出栈顶元素(从右边开始弹出,弹出之后,list中就不存在该元素了)
#rpop key 
rpop list1

#
按照索引位置取值
#lindex key index
lindex list1 0

#
取值,从头部到尾部
#lrange key start end   (负下标表示的是从后往前,如-1表示倒数第一)
lrange list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"

#
 blpop和brpop是阻塞版本
# blpop key timeout
# timeout表示 列表为空时会等3秒后返回,如果timeout=0,则会一直阻塞下去,直到有数据为止
# 如果多个客户端对同一个键执行blpop,那么先执行的blpop命令的客户端可以获取到弹出的值
blpop list1 3

列表长度

llen list1

删除值

#删除几个某个元素  count>0时,从左边开始删除;count=0时,删除所有值为element的元素;count<0时,从右边开始删除
#lrem key count element
lrem list1 1 "hi"

#
截取指定范围的值赋给key(范围是索引)
#ltrim key start stop
ltrim list1 0 0

#
 从头部删除元素  出栈
lpop list1
# 从尾部删除元素
rpop list1

出栈入栈

#从源列表右出栈压入左目标列表,将一个列表移至另一个列表
#rpoplpush source dest
rpoplpush list2 list1

Set类型

set类型是单值多value,与List的区别在于不可重复,且没有顺序,类似于HashSet,通过hash table实现的

set除了基本的增删改查操作之外,还可以使用集合来取并集、交集、差集,可以用来实现好友推荐等功能

编码格式
编码对象
OBJ_ENCODING_INTSET使用整数集合实现的集合对象
OBJ_ENCODING_HT使用的hash table

set有两种编码格式

  • intset 保存的元素全都是整数,且元素数量不超过512
  • hash table 保存的不是整数
#根据该配置项来进行编码转换的
set-max-intset-entries 512
源码结构
intset结构

整数集合

typedef struct intset {
  // 每个整数的类型
    uint32_t encoding;
  // intset的长度
    uint32_t length;
  // 整数数组
    int8_t contents[];
} intset;
操作命令

设置值

#设置值
#sadd key member [member ...]
sadd set1 vv vc vb vv

取值

#取值,集合中的所有元素
#smembers key
smembers set1
1) "vc"
2) "vb"
3) "vv"

#
判断该元素是否在set
#sismember key member
sismember set1 vv

#
获取随机的元素,不会删除元素(count表示获取的数量,默认为1)
#srandmember key [count]
srandmember set1 2

查看集合元素个数

#scard key
scard set1

删除

#srem key member [member ...]
srem set1 vb

#
随机出栈(默认数量为1)
#spop key [count]
spop set1

随机抽取

#随机从集合中抽取2个(默认1个)
#srandmember key [count]
srandmember set1 2
#将源集合中的值移至目标集合
#smove source dest member
smove set1 set2 test

差集 交集 并集

 #差集 返回的是其他key与第一个key的差集,属于A且不属于B的(A - B)
 #sdiff key [key ...]
 sdiff set1 set2
 
 #
交集  属于A且属于B的(A ∩ B)
 #sinter key [key ...]
 sinter set1 set2
 #并集  属于A或属于B的(A ∪ B)
 #sunion key [key ...]
 sunion set1 set2

Hash类型

value是键值对,相当于HashMap,对于hash碰撞也是采用的HashMap的处理方式,数组+链表

更适合存储对象,将一个对象存储在hash类型中会占用更少的内存,且可以更方便的存取整个对象

编码格式
编码对象
OBJ_ENCODING_ZIPLIST使用ziplist
OBJ_ENCODING_HT使用的hash table

hash有两种编码格式,当键值对数据量比较小时,使用紧凑的数组格式来节省内存空间

  • ziplist 一开始存储使用的ziplist,但是当满足一定条件时会转换为hash table
  • hash table
#根据该配置项来进行编码转换的
# 当hash对象的键值对数量大于该值时使用OBJ_ENCODING_HT编码 
hash-max-ziplist-entries 512
# 当hash对象中的键值对存在键或值的长度大于该值时使用OBJ_ENCODING_HT编码 
hash-max-ziplist-value 64
源码结构

dict.h

// 哈希表结构
typedef struct dictht {
   // 哈希表数组
    dictEntry **table;
   // 哈希表大小
    unsigned long size;
   // 哈希表大小掩码,用于计算索引值
    unsigned long sizemask;
   // 哈希表已有节点数量
    unsigned long used;
} dictht;
// 哈希节点
typedef struct dictEntry {
   // 键
    void *key;
   // 值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
   // 下一个哈希节点,链表,可以将多个哈希值相同的键值对进行连接,解决hash冲突的问题
    struct dictEntry *next;
} dictEntry;
// 字典结构
typedef struct dict {
   // 类型
    dictType *type;
   // 私有数据
    void *privdata;
   // 哈希表
   // ht[0]:用来存放真实的数据
   // ht[1]:用于扩容
    dictht ht[2];
   //rehash 如果为-1表示没有进行rehash
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */
    int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */
} dict;


// 处处不同的数据结构,使用不同的hash算法,不同的键值比较算法,不同的析构函数
typedef struct dictType {
   // hash函数
    uint64_t (*hashFunction)(const void *key);
    void *(*keyDup)(void *privdata, const void *key);
    void *(*valDup)(void *privdata, const void *obj);
   // 比较函数
    int (*keyCompare)(void *privdata, const void *key1, const void *key2);
  // 键值析构函数
    void (*keyDestructor)(void *privdata, void *key);
    void (*valDestructor)(void *privdata, void *obj);
    int (*expandAllowed)(size_t moreMem, double usedRatio);
} dictType;
哈希冲突

redis哈希冲突是使用链地址法进行解决的,将新节点添加在链表的表头

扩容

redis的字典源码中的dictht是一个容量为2的数组,其作用就是用于进行扩容的

 // 哈希表
   // ht[0]:用来存放真实的数据
   // ht[1]:用于扩容
    dictht ht[2];

最初始的数据是存放在ht[0]中的,当要进行扩展或收缩时,ht[1]会根据ht[0]的容量来计算容量,ht[1]扩展的大小是比当前ht[0].used值的两倍大的第一个2的整数幂;收缩的大小是ht[0].used的第一个大于等于的2的整数幂。然后将ht[0]的所有键值重新散列到ht[1]中,重新计算所有的数组下标,当数据迁移完后ht[0]就会释放,然后将ht[1]改为ht[0],为下一次扩展或收缩做准备

由于有时候redis中的数据比较多,不能瞬间完成扩容操作,会将rehash操作进行分步进行,其字段rehashidx就用于标识rehash过程,如果是-1表示当前没有进行rehash操作,当进行rehash操作时会将该值改为0,在进行更新、删除、查询操作时会在ht[0]和ht[1]中都进行,先更新ht[0],在更新ht[1],而进行新增操作就直接新增到ht[1]中,保证ht[0]只减不增,直到rehash完成

操作命令

设置值

#Redis4.0之后可以设置多个值,与hmset功能相同
#hset key field value [field value ...]
# 不区分插入和更新,插入时返回1,更新时返回0
hset user id 123

#
设置多个字段
#hmset key field value [field value ...]
hmset user name zhangsan age 18

#
不存在则设置值
#hsetnx key field value
hsetnx user id 12

获取值

#hget key field
hget user id
#获取多个字段的值
#hmget key field [field ...]
hmget user id name sex age

#
可以获取该key所对应的map
#hgetall key
hgetall user

#
获取key对应的map有几个字段
#hlen key
hlen user

#
判断该key的某个字段是否存在
#hexists key field
hexists user id

#
获取该key下所有的字段
#hkeys key
hkeys user

#
获取该key下所有的字段值
#hvals key 
hvals user

删除字段值

#支持删除多个字段
#hdel key field [field ...]
hdel user age

数值操作

# 加2
# hincrby key field increment
hincrby user id 2

#
 减2
# hdecrby key field increment
hdecrby user id 2

Zset类型

zset是sorted set,即有序的集合,在set的基础上加了一个score,类似于TreeSet,使用score来进行排序,也可以根据score的范围来获取元素的列表

原本set值为k1 v1 k2 v2,而zset是k1 score1 v1 k2 score2 v2

底层使用的是dict哈希表和skiplist跳表,dict用来关联value和score,保证value的唯一性,同时通过value可以获取到score。skiplist的作用在于给value排序以及根据score的范围来获取元素列表

编码格式
编码对象
OBJ_ENCODING_ZIPLIST使用ziplist
OBJ_ENCODING_SKIPLIST使用的skiplist

zset有两种编码格式

  • ziplist 满足条件使用ziplist,否则使用skiplist
  • skiplist
#根据该配置项来进行编码转换的
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
源码结构

跳表skiplist相比一般的链表,有更高的查找效率,效率可以比拟二叉查找树

跳表搜索
跳表搜索

在存储时会预先间隔地保存了有序链表的节点,从而在查找时能达到类似于二分搜索的效果,可以节省查找时间,但是浪费了空间,数据只存储了一次,只是指针多次存储了

  • 由多层结构组成
  • 每一层都是一个有序链表
  • 最底层的链表包含了所有元素
  • 如果一个元素出现在leveli的链表中,则它在leveli之下的链表也都会出现
  • 每个节点包含两个指针,一个指向同链表中的下一个元素,一个指向下面一层的元素
typedef struct zset {
   // 哈希表
    dict *dict;
  // 跳表
    zskiplist *zsl;
} zset;

typedef struct zskiplistNode {
  // 节点数据
    sds ele;
  // 分数
    double score;
  // 后继指针
    struct zskiplistNode *backward;
  // 前驱指针
    struct zskiplistLevel {
        struct zskiplistNode *forward;
      // 调到下一个数据项需要走几步,计算rank时有用
        unsigned long span;
    } level[];
} zskiplistNode;

typedef struct zskiplist {
  // 跳表头尾指针
    struct zskiplistNode *header, *tail;
  // 跳表长度
    unsigned long length;
  // 跳表高度
    int level;
} zskiplist;
操作命令

设置值

# 向zset中添加元素,并且会根据score进行排序,如果元素存在,会更新score
#zadd key score member [score member ...]
zadd zset1 1 q 2 w

取值

#WITHSCORES表示查询结果是否带着score,score从小到大
#zrange key start stop [WITHSCORES]
zrange zset1 0 -1

#
WITHSCORES表示查询结果是否带着score,score从大到小
#zrevrange key start stop [WITHSCORES]
zrevrange zset1 0 -1

#
根据score范围查询
#zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
zrangebyscore zset2 80 100

#
根据score的范围来计数
#zcount key min max
 zcount zset2 0 10
 
 #
根据值来获取下标(根据score从小到大来排的序)
 #zrank key member
 zrank zset2 d
 
 #
根据值来获取下标(根据score从大到小来排的序)
 #zrevrank key member
 zrevrank zset2 d
 
 #
获取元素的score
 #zscore key member
 zscore zset2 c
 

查看集合元素个数

#该key的元素个数
zcard zset2

删除

#zrem key member
zrem zset2 s

#
 根据下标区间来删除(根据score从小到大来排的序)
#zremrangebyrank key start stop
zremrangebyrank zset1 5 8

#
 根据score区间来删除
# zremrangebyscore key min max
zremrangebyscore zset1 7 10

数值操作

# 如果key中存在该元素,则该元素的score增加increment,否则新增该元素
# zincrby key increment member
zincrby zset1 2 b

Bitmap类型

位图不是一个真实的数据类型,是定义在字符串类型之上的面向位操作的集合,每个单元只能存储0和1。位图的最大优势在于节省空间

127.0.0.1:6379> type bittest
string

设置值

#setbit key offset value
# value只能是0或者1
setbit bittest 10 1

取值

#getbit key offset
# 如果offset超出范围的话返回0
getbit bittest 10

#
bitcount key [start] [end]
#获取指定范围为1的个数
bitcount bittest 0 1000

bitmap适用于大量数据,少量数据的话会导致大部分位都是0,占用的内存数比集合还多

HyperLogLog类型

HyperLogLog是一种基于字符串类型的基数算法。通过HyperLogLog可以利用极小的内存空间完成独立总数的统计

127.0.0.1:6379> type hpl
string
# 添加
# pfadd key element [element ...]
pfadd hpl 123

#
 统计
# pfcount key [key ...]
pfcount hpl

#
合并
# pfmerge destkey sourcekey [sourcekey ...]
pfmerge destkey sourcekey1 sourcekey2

HyperLogLog非常的节省空间,但是HyperLogLog统计的数据是不准确的

GEO类型

地理信息定位功能,支持存储地理位置信息。其底层是zset

127.0.0.1:6379> type key
zset
# 增加地理位置信息
# longitude经度
# latitude纬度
# member成员
# geoadd key longitude latitude member [longitude latitude member ...]
geoadd key 116.28 39.55 bj

#
 获取地理位置信息 返回经纬度
#geopos key member [member ...]
geopos key bj

#
 获取两个地理位置的距离 unit为单位 包含 m 米、km 千米、mi 英里、ft 尺
# geodist key member1 member2 [unit]
geodist key bj sjz km

#
 获取指定位置范围内的地理信息集合
# georadius key longitude latitude radius m|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key]
# longitude latitude也可以使用成员来替换,此时使用georadiusbymember key member
# radius m|km|ft|mi 表示半径+单位
# withcoord 返回结果中包含经纬度
# withdist 返回结果中包含里节点位置的距离
# withhash 返回结果中包含geohash
# COUNT count 指定返回的数量
# asc|desc 按照距离做升序或者降序
# store key 将返回结果的地理位置信息保存到指定键
# storedist key 将返回结果距离节点距离保存到指定键
georadiusbymember key bj 250 km

#
 删除地理位置信息
# zrem key member

各个类型的场景

  • String:

    • 缓存 set key value
    • 分布式锁 setnx key value
    • 限流/计数器 INCR count DECR count
    • 分布式id 使用INCR来生成id INCR id
    • pv/uv统计 可以使用incr来统计文章的访问量,key设置为post:文章ID:view
    • 分布式session
  • List:

    • 消息队列 LPUSH list item + brpop list来实现阻塞队列
    • 评论
    • 热点列表 如微博话题 LRANGE list 0 10

    lpush+lpop = 栈

    lpush+rpop = 队列

    lpush+ltrim = 有限集合

    lpush+brpop = 消息队列

  • Set: 点赞、标签

    • 标签 文章标签很多,每个人喜好也很多,推送的时候使用交集 SINTER set1 set2 set3
  • Hash: 存储对象

  • Zset: 排行榜

    • ZADD 添加元素
    • ZRANGE 按分数从低到高排列,返回区间内的元素
    • ZREVRANGE 按分数从高到低排列,返回区间内的元素
    • ZRANK 返回某个元素的排名
  • Bitmap

    • 可以用来做布隆过滤器
    • 可以用来统计每天的用户数
  • HyperLogLog

    • 可以用来计算独立总数,但是不可以获取用户id(而且数据存在一定的误差)
  • GEO

    • 可以用来做附近位置等依赖于地理位置的功能

https://zhhll.icu/2021/数据库/非关系型数据库/redis/基础/7.redis数据结构/

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/153412.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv8-seg改进:重新思考轻量化视觉Transformer中的局部感知CloFormer,提升上下文感知权重来增强局部特征 |2023清华

🚀🚀🚀本文改进:CloFormertAttention利用共享权重和上下文感知权重有效地提取高频局部特征表示 🚀🚀🚀SEAM、MultiSEAM分割物与物相互遮挡、分割小目标性能 🚀🚀🚀YOLOv8-seg创新专栏:http://t.csdnimg.cn/KLSdv 学姐带你学习YOLOv8,从入门到创新,轻轻…

Java Enumeration 接口

Java Enumeration 接口 这段代码展示了如何使用 Enumeration 接口来遍历 Properties 对象的键。在这里&#xff0c;foo.getProp() 返回一个 Properties 对象&#xff0c;而 propertyNames() 方法返回一个 Enumeration 对象&#xff0c;它包含了 Properties 对象中所有键的枚举。…

synchronized锁膨胀过程

轻量级锁&#xff1a; 使用场景&#xff1a;如果一个对象虽然有多线程要加锁&#xff0c;但加锁的时间是错开的&#xff08;也就是没有竞争&#xff09;&#xff0c;那么可以 使用轻量级锁来优化。 轻量级锁原理 1.创建锁记录&#xff08;Lock Record&#xff09;对象&#…

光谱图像超分辨率综述

光谱图像超分辨率综述 简介 ​ 论文链接&#xff1a;A Review of Hyperspectral Image Super-Resolution Based on Deep Learning UpSample网络框架 1.Front-end Upsampling ​ 在Front-end上采样中&#xff0c;是首先扩大LR图像&#xff0c;然后通过卷积网络对放大图像进行…

竞赛 题目:基于深度学习的中文汉字识别 - 深度学习 卷积神经网络 机器视觉 OCR

文章目录 0 简介1 数据集合2 网络构建3 模型训练4 模型性能评估5 文字预测6 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的中文汉字识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &a…

什么是媒体见证?媒体宣传有哪些好处?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 一&#xff0c;什么是媒体见证&#xff1f; 媒体见证是指企业举办活动&#xff0c;发布会&#xff0c;邀请媒体现场采访的一种宣传方式&#xff0c;媒体到场后&#xff0c;对其进行记录…

lenovo联想笔记本ThinkPad P1 Gen5/X1 Extreme Gen5原装出厂Windows11预装OEM系统

链接&#xff1a;https://pan.baidu.com/s/13E97Nwc-0-N7ffPjEeeeOw?pwdep4l 提取码&#xff1a;ep41 原装出厂系统自带所有驱动、出厂主题壁纸、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;32G或以上的U盘 文件格式&#xff1a;ISO 文件大小&#xff…

Java实现俄罗斯方块游戏

俄罗斯方块游戏本身的逻辑&#xff1a; 俄罗斯方块游戏的逻辑是比较简单的。它就类似于堆砌房子一样&#xff0c;各种各样的方地形状是不同的。但是&#xff0c;俄罗斯方块游戏的界面被等均的分为若干行和若干列&#xff0c;因此方块的本质就是占用了多少个单元。 首先来考虑…

解决 Python requests 库中 SSL 错误转换为 Timeouts 问题

解决 Python requests 库中 SSL 错误转换为 Timeouts 问题&#xff1a;理解和处理 SSL 错误的关键 在使用Python的requests库进行HTTPS请求时&#xff0c;可能会遇到SSL错误&#xff0c;这些错误包括但不限于证书不匹配、SSL层出现问题等。如果在requests库中设置verifyFalse&…

《向量数据库指南》——Range Search 使用方法和参数检查

Range Search 使用方法 如需使用 Range Search,只需要修改搜索请求中的搜索参数。接下来我会讲一下的详细使用指南,在指南的最后还提供了 Python 示例代码。 开始前 请确保已安装并运行 Milvus Cloud。请确保已创建 1 个 Collection,并为该 Collection 创建索引。 Ra…

【LeetCode:2216. 美化数组的最少删除数 | 贪心】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

掌握源码,轻松搭建:一站式建站系统源码 附完整搭建步骤与教程

随着互联网的快速发展&#xff0c;网站已成为人们生活中不可或缺的一部分。然而&#xff0c;对于许多初学者或中小企业来说&#xff0c;搭建一个完整的网站系统并非易事。这涉及到前端和后端的开发、数据库管理等多个环节。为了解决这一痛点&#xff0c;我们推出了一站式建站系…

sortablejs拖拽后新增和删除行时顺序错乱

问题描述&#xff1a;如下图所示&#xff0c;使用sortablejs拖拽后&#xff0c;在序号2后新增行会出现新增行跑到第一行的错误顺序。 解决&#xff1a;在进行拖拽后&#xff0c;对表格数据进行清空重新赋值。

一种可度量的测试体系-精准测试

行业现状 软件行业长期存在一个痛点&#xff0c;即测试效果无法度量。通常依赖于测试人员的能力和经验&#xff0c;测试结果往往不可控&#xff0c;极端情况下同一个业务功能&#xff0c;即使是同一个人员在不同的时间段&#xff0c;测试场景和过程也可能不一致&#xff0c;从而…

抖音电商双11官方数据最全汇总!

11月13日&#xff0c;抖音电商数据发布“抖音商城双11好物节”数据报告&#xff0c;展现双11期间平台全域经营情况及大众消费趋势。 报告显示&#xff0c;10月20日至11月11日&#xff0c;抖音电商里的直播间累计直播时长达到5827万小时&#xff0c;挂购物车的短视频播放了1697亿…

第十一篇 基于JSP 技术的网上购书系统——产品类别管理、评论/留言管理、注册用户管理、新闻管理功能实现(网上商城、仿淘宝、当当、亚马逊)

目录 1.产品类别管理 1.1功能说明 1.2界面设计 1.3处理流程 1.4数据来源和算法 1.4.1数据来源 1.4.2 查询条件 1.4.3相关sql实例 2. 评论/留言管理 2.1功能说明 2.2 界面设计 2.3处理流程 2.4数据来源和算法 2.4.1数据来源 2.4.2 查询条件 2.4.3相关sql实例…

vue3 使用simplebar【滚动条】

1.下载simplebar-vue npm install simplebar-vue --save2.引入注册 import simplebar from "simplebar-vue"; import simplebar-vue/dist/simplebar.min.css import simplebar-vue/dist/simplebar-vue.jsvue2的版本基础上 【引入注册】 import simplebar from &qu…

IDEA 搭建 SpringCloud 项目【超详细步骤】

文章目录 一、前言二、项目搭建1. 数据库准备2. 创建父工程3. 创建注册中心4. 服务注册5. 编写业务代码6. 服务拉取 一、前言 所谓微服务&#xff0c;就是要把整个业务模块拆分成多个各司其职的小模块&#xff0c;做到单一职责原则&#xff0c;不会重复开发相同的业务代码&…

数据预处理pandas pd.json_normalize占用内存过大优化

问题描述 从ES下载数据&#xff0c;数据格式为json&#xff0c;然后由pandas进行解析&#xff0c;json中的嵌套字段会进行展开作为列名(由于维度初期无法预测&#xff0c;所以根据数据有啥列就使用啥列&#xff0c;这是最方便的点)&#xff0c;变成表格&#xff0c;方面了后续…