目录
应用场景 - 列表最多可存储 2的32次方 - 1 元素 (4294967295, 每个列表可存储40多亿)。
应用场景 - 集合中最大的成员数为 2的32次方 - 1 (4294967295, 每个集合可存储40多亿个成员)。
应用场景 - 每个 hash 可以存储 2的32次方 - 1 键值对(40多亿)。
redis基本数据类型,底层原理,应用场景
String**
基本用法
SET存储字符串值。
SETNX仅在键不存在的情况下存储字符串值。用于实现锁。
GET检索字符串值。
MGET在单个操作中检索多个字符串值。
key - value键值对,底层数据结构是SDS简单动态字符。
struct SDS<T> {
T capacity; // 数组容量 使用泛型表示的
T len; // 数组长度 使用泛型表示的
byte flags; // 特殊标识位,不理睬它
byte[] content; // 数组内容 字节数组
}
buf数组存储实际的字符串。len字段存储了buf的长度。这使得获得Redis字符串的长度是一个0(1)操作。free=capacity-len 存储可供使用的额外字节数。
详细来说的话,底层有三种编码方式,int,embstr,raw
其中int存储的是long型数据,其他两个都用sds,不过embstr编码的sds是最结构效率最快的。
应用场景。??
sds预分配,扩容机制?
字符串实际分配的空间 capacity 一般要高于实际字符串长度 len.
1、获取当前可用空间长度avail,若大于等于新增长度addlen,说明无需扩容,直接返回
2、若avail小于addlen,s的长度加上addlen小于1M(代码中的SDS_MAX_PREALLOC就是1M),那么按新长度的2倍扩容
3、若avail小于addlen,s的长度加上addlen大于1M,那么按新长度加1M
4、根据新长度选择sds类型,若sds类型和原类型相同,则调用s_realloc_usable,扩大柔性数组
5、如果sds类型和原类型不同,则调用s_malloc_usable,重新申请内存,把原buf内容移动到新位置
6、对新串的属性进行赋值,返回。
sds _sdsMakeRoomFor(sds s, size_t addlen, int greedy) {
void *sh, *newsh;
size_t avail = sdsavail(s);
size_t len, newlen, reqlen;
char type, oldtype = s[-1] & SDS_TYPE_MASK;
int hdrlen;
size_t usable;
/* Return ASAP if there is enough space left. */
if (avail >= addlen) return s;
len = sdslen(s);
sh = (char*)s-sdsHdrSize(oldtype);
reqlen = newlen = (len+addlen);
assert(newlen > len); /* Catch size_t overflow */
if (greedy == 1) {
if (newlen < SDS_MAX_PREALLOC)
newlen *= 2;
else
newlen += SDS_MAX_PREALLOC;
}
type = sdsReqType(newlen);
/* Don't use type 5: the user is appending to the string and type 5 is
* not able to remember empty space, so sdsMakeRoomFor() must be called
* at every appending operation. */
if (type == SDS_TYPE_5) type = SDS_TYPE_8;
hdrlen = sdsHdrSize(type);
assert(hdrlen + newlen + 1 > reqlen); /* Catch size_t overflow */
if (oldtype==type) {
newsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable);
if (newsh == NULL) return NULL;
s = (char*)newsh+hdrlen;
} else {
/* Since the header size changes, need to move the string forward,
* and can't use realloc */
newsh = s_malloc_usable(hdrlen+newlen+1, &usable);
if (newsh == NULL) return NULL;
memcpy((char*)newsh+hdrlen, s, len+1);
s_free(sh);
s = (char*)newsh+hdrlen;
s[-1] = type;
sdssetlen(s, len);
}
usable = usable-hdrlen-1;
if (usable > sdsTypeMaxSize(type))
usable = sdsTypeMaxSize(type);
sdssetalloc(s, usable);
return s;
什么时候用哪个编码?
数据类型 | 编码格式 |
---|---|
19位数字 | int |
大于19位数字 | embstr |
大于44位数字 | raw |
浮点数,普通字符串 | embstr |
大于44位浮点数,普通字符串 | raw |
如果长度小于44字节,则按embstr方式编码,否则按raw方式编码
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
robj *createStringObject(const char *ptr, size_t len) {
if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
return createEmbeddedStringObject(ptr,len);
else
return createRawStringObject(ptr,len);
}
特殊情况
embstr仅是可读的,如果修改了之后,就会变成raw格式编码。
编码格式 | 特点 |
---|---|
int | 仅包含redisObject数据结构,直接将数据赋值给ptr |
embstr | 包含redisObject以及sds数据结构,通过ptr指针连续存储在一起 |
raw | 包含redisObject以及sds数据结构,通过ptr指针链接,分开存储 |
struct RedisObject {
int4 type; // 4bits
int4 encoding; // 4bits
int24 lru; // 24bits
int32 refcount; // 4bytes
void *ptr; // 8bytes,64-bit system
} robj;
不同的对象具有不同的类型 type(4bit),
同一个类型的 type 会有不同的存储形式encoding(4bit),
为了记录对象的 LRU 信息,使用了 24 个 bit 来记录 LRU 信息。
每个对象都有个引用计数,当引用计数为零时,对象就会被销毁,内存被回收。
ptr 指针将指向对象内容 (body) 的具体存储位置。
应用场景 - 一个键最大能存储512MB
分布式锁
完整写法 - set lock xxx nx ex 5
redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
计数器
redis> SET mykey "10"
"OK"
redis> INCRBY mykey 5
(integer) 15
存储token
redis> SET mykey "Hello"
"OK"
redis> GET mykey
"Hello"
redis> SET anotherkey "will expire in a minute" EX 60
"OK"
java jedis用法
package io.redis.examples;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.params.SetParams;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class StringExample {
public void run() {
try (UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379")) {
String res1 = jedis.set("bike:1", "Deimos");
System.out.println(res1); // OK
String res2 = jedis.get("bike:1");
System.out.println(res2); // Deimos
Long res3 = jedis.setnx("bike:1", "bike");
System.out.println(res3); // 0 (because key already exists)
System.out.println(jedis.get("bike:1")); // Deimos (value is unchanged)
String res4 = jedis.set("bike:1", "bike", SetParams.setParams().xx()); // set the value to "bike" if it
// already
// exists
System.out.println(res4); // OK
String res5 = jedis.mset("bike:1", "Deimos", "bike:2", "Ares", "bike:3", "Vanth");
System.out.println(res5); // OK
List<String> res6 = jedis.mget("bike:1", "bike:2", "bike:3");
System.out.println(res6); // [Deimos, Ares, Vanth]
jedis.set("total_crashes", "0");
Long res7 = jedis.incr("total_crashes");
System.out.println(res7); // 1
Long res8 = jedis.incrBy("total_crashes", 10);
System.out.println(res8); // 11
}
}
}
redis数据结构源码分析——string_redis string 源码-CSDN博客
Redis底层数据结构之String_redis string 底层数据结构-CSDN博客
Redis进阶-string底层数据结构精讲-腾讯云开发者社区-腾讯云
List**
基本用法
LPUSH在列表的头部添加一个新元素;RPUSH添加到尾部。
LPOP从列表的头部删除并返回一个元素;RPOP做同样的事情,但是是从列表的尾部。
LLEN返回列表的长度。
LMOVE自动地将元素从一个列表移动到另一个列表。
LTRIM将列表缩减为指定的元素范围。列表支持多个阻塞命令。例如:
BLPOP从列表的头部删除并返回一个元素。如果列表为空,则命令阻塞,直到元素变为可用或达到指定的超时。
BLMOVE自动地将元素从源列表移动到目标列表。如果源列表为空,则该命令将阻塞,直到有新的元素可用。
底层结构
底层有两种数据结构,双端链表和压缩链表
双向链表
无环,双向,头尾指针
typedef struct listNode {
//前置节点
struct listNode *prev;
//后置节点
struct listNode *next;
//节点的值
void *value;
} listNode;
在此基础上他还设计了list结构
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.无环 2.查找速度快
缺点:1.内存不连续,无法很好的利用cpu缓存 2.内存开销大
压缩链表
一种紧凑型数据结构,压缩链表字节数zlbytes,从首到尾字节数zltail,节点个数zllength,entries节点列表,zlend压缩列表结束符。entries包括前一个指针,编码方式int或string,数据data。
优点:
- 一种内存紧凑型的数据结构,占用一块连续的内存空间
缺点:
- 不能保存过多的元素,否则查询效率就会降低;
- 新增或修改某个元素时,压缩列表占用的内存空间需要重新分配,甚至可能引发连锁更新的问题。
什么时候用哪个底层结构?**
当列表对象同时满足以下两个条件时,列表对象使用ziplist进行存储,否则用linkedlist存储。
- 列表对象保存的所有字符串元素的长度小于64字节
- 列表对象保存的元素数量小于512个。
应用场景 - 列表最多可存储 2的32次方 - 1 元素 (4294967295, 每个列表可存储40多亿)。
消息队列
> LPUSH msg_queue msg1 msg2 msg3
(integer) 3
这边往 key 为 msg_queue 的队列中插入了三个消息 msg1、msg2、msg3。
> RPOP msg_queue
"msg1"
> RPOP msg_queue
"msg2"
> RPOP msg_queue
"msg3"
> RPOP msg_queue
(nil)
Redis 提供了 BLPOP、BRPOP ,无数据的时候自动阻塞读取的命令,有新消息进入的时候,恢复消息取数
Redis使用List实现消息队列_redis list消息队列-CSDN博客
java redis用法
package io.redis.examples;
import org.junit.Test;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.args.ListDirection;
import java.util.List;
import static org.junit.Assert.*;
public class ListExample {
public void run() {
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
long res1 = jedis.lpush("bikes:repairs", "bike:1");
System.out.println(res1); // >>> 1
long res2 = jedis.lpush("bikes:repairs", "bike:2");
System.out.println(res2); // >>> 2
String res3 = jedis.rpop("bikes:repairs");
System.out.println(res3); // >>> bike:1
String res4 = jedis.rpop("bikes:repairs");
System.out.println(res4); // >>> bike:2
long res5 = jedis.lpush("bikes:repairs", "bike:1");
System.out.println(res5); // >>> 1
long res6 = jedis.lpush("bikes:repairs", "bike:2");
System.out.println(res6); // >>> 2
String res7 = jedis.lpop("bikes:repairs");
System.out.println(res7); // >>> bike:2
String res8 = jedis.lpop("bikes:repairs");
System.out.println(res8); // >>> bike:1
long res9 = jedis.llen("bikes:repairs");
System.out.println(res9); // >>> 0
long res10 = jedis.lpush("bikes:repairs", "bike:1");
System.out.println(res10); // >>> 1
long res11 = jedis.lpush("bikes:repairs", "bike:2");
System.out.println(res11); // >>> 2
String res12 = jedis.lmove("bikes:repairs", "bikes:finished", ListDirection.LEFT, ListDirection.LEFT);
System.out.println(res12); // >>> bike:2
List<String> res13 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res13); // >>> [bike:1]
List<String> res14 = jedis.lrange("bikes:finished", 0, -1);
System.out.println(res14); // >>> [bike:2]
long res15 = jedis.rpush("bikes:repairs", "bike:1");
System.out.println(res15); // >>> 1
long res16 = jedis.rpush("bikes:repairs", "bike:2");
System.out.println(res16); // >>> 2
long res17 = jedis.lpush("bikes:repairs", "bike:important_bike");
System.out.println(res17); // >>> 3
List<String> res18 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res18); // >>> [bike:important_bike, bike:1, bike:2]
long res19 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3");
System.out.println(res19); // >>> 3
long res20 = jedis.lpush("bikes:repairs", "bike:important_bike", "bike:very_important_bike");
System.out.println(res20); // >>> 5
List<String> res21 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res21); // >>> [bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3]
long res22 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3");
System.out.println(res22); // >>> 3
String res23 = jedis.rpop("bikes:repairs");
System.out.println(res23); // >>> bike:3
String res24 = jedis.lpop("bikes:repairs");
System.out.println(res24); // >>> bike:1
String res25 = jedis.rpop("bikes:repairs");
System.out.println(res25); // >>> bike:2
String res26 = jedis.rpop("bikes:repairs");
System.out.println(res26); // >>> null
long res27 = jedis.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5");
System.out.println(res27); // >>> 5
String res28 = jedis.ltrim("bikes:repairs", 0, 2);
System.out.println(res28); // >>> OK
List<String> res29 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res29); // >>> [bike:5, bike:4, bike:3]
res27 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5");
System.out.println(res27); // >>> 5
res28 = jedis.ltrim("bikes:repairs", -3, -1);
System.out.println(res2); // >>> OK
res29 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res29); // >>> [bike:3, bike:4, bike:5]
long res31 = jedis.rpush("bikes:repairs", "bike:1", "bike:2");
System.out.println(res31); // >>> 2
List<String> res32 = jedis.brpop(1, "bikes:repairs");
System.out.println(res32); // >>> (bikes:repairs, bike:2)
List<String> res33 = jedis.brpop(1,"bikes:repairs");
System.out.println(res33); // >>> (bikes:repairs, bike:1)
List<String> res34 = jedis.brpop(1,"bikes:repairs");
System.out.println(res34); // >>> null
long res35 = jedis.del("new_bikes");
System.out.println(res35); // >>> 0
long res36 = jedis.lpush("new_bikes", "bike:1", "bike:2", "bike:3");
System.out.println(res36); // >>> 3
String res37 = jedis.set("new_bikes", "bike:1");
System.out.println(res37); // >>> OK
String res38 = jedis.type("new_bikes");
System.out.println(res38); // >>> string
try {
long res39 = jedis.lpush("new_bikes", "bike:2", "bike:3");
} catch (Exception e) {
e.printStackTrace();
// >>> redis.clients.jedis.exceptions.JedisDataException:
// >>> WRONGTYPE Operation against a key holding the wrong kind of value
}
jedis.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3");
System.out.println(res36); // >>> 3
boolean res40 = jedis.exists("bikes:repairs");
System.out.println(res40); // >>> true
String res41 = jedis.lpop("bikes:repairs");
System.out.println(res41); // >>> bike:3
String res42 = jedis.lpop("bikes:repairs");
System.out.println(res42); // >>> bike:2
String res43 = jedis.lpop("bikes:repairs");
System.out.println(res43); // >>> bike:1
boolean res44 = jedis.exists("bikes:repairs");
System.out.println(res44); // >>> false
long res45 = jedis.del("bikes:repairs");
System.out.println(res45); // >>> 0
long res46 = jedis.llen("bikes:repairs");
System.out.println(res46); // >>> 0
String res47 = jedis.lpop("bikes:repairs");
System.out.println(res47); // >>> null
long res48 = jedis.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5");
System.out.println(res48); // >>> 5
String res49 = jedis.ltrim("bikes:repairs", 0, 2);
System.out.println(res49); // >>> OK
List<String> res50 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res50); // >>> [bike:5, bike:4, bike:3]
}
}
redis list底层数据结构_redis底层是如何存储list列表的? csdn-CSDN博客
Set**
基本用法
SADD向集合添加一个新成员。
SREM从集合中删除指定的成员。
SISMEMBER测试字符串的集合成员关系。
SINTER返回两个或多个集合共有的成员的集合(即交集)。
SCARD返回集合的大小(即基数)。
无序不重复集合 ,
底层结构
int数组
typedef struct intset {
//编码方式
uint32_t encoding;//int16_t 2字节、int32_t 4字节和int64_t类型 8字节
//集合中包含的元素数量
uint32_t length;
//保存元素的数组
int8_t contents[];
} intset;
哈希表
typedef struct dictht {
dictEntry **table;//哈希表数组
unsigned long size;//哈希表大小
unsigned long sizemask;//掩码大小,用于计算索引值,总是等于size-1
unsigned long used;//哈希表中的已有节点数
} dictht;
注:table 是一个数组,其每个元素都是一个 dictEntry 对象。
什么时候用哪个数据结构?
set-max-intset-entries的默认值是512,表示当Set对象的键值对数量大于该值时使用HashTable数据结构。
- 当Set对象的值出现了非数字时,也会使用HashTable数据结构。
- 当插入一个非数字时,数据结构从IntList转变为HashTable。
- 当元素数量超过512时,数据结构从IntList转变为HashTable。
应用场景 - 集合中最大的成员数为 2的32次方 - 1 (4294967295, 每个集合可存储40多亿个成员)。
共同粉丝,共同好友(交集)
SINTER set1 set2
java jedis用法
package io.redis.examples;
import redis.clients.jedis.UnifiedJedis;
import org.junit.Test;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
public class SetsExample {
public void run() {
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
long res1 = jedis.sadd("bikes:racing:france", "bike:1");
System.out.println(res1); // >>> 1
long res2 = jedis.sadd("bikes:racing:france", "bike:1");
System.out.println(res2); // >>> 0
long res3 = jedis.sadd("bikes:racing:france", "bike:2", "bike:3");
System.out.println(res3); // >>> 2
long res4 = jedis.sadd("bikes:racing:usa", "bike:1", "bike:4");
System.out.println(res4); // >>> 2
jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3");
jedis.sadd("bikes:racing:usa", "bike:1", "bike:4");
boolean res5 = jedis.sismember("bikes:racing:usa", "bike:1");
System.out.println(res5); // >>> true
boolean res6 = jedis.sismember("bikes:racing:usa", "bike:2");
System.out.println(res6); // >>> false
jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3");
jedis.sadd("bikes:racing:usa", "bike:1", "bike:4");
Set<String> res7 = jedis.sinter("bikes:racing:france", "bikes:racing:usa");
System.out.println(res7); // >>> [bike:1]
jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3");
long res8 = jedis.scard("bikes:racing:france");
System.out.println(res8); // >>> 3
long res9 = jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3");
System.out.println(res9); // >>> 3
Set<String> res10 = jedis.smembers("bikes:racing:france");
System.out.println(res10); // >>> [bike:1, bike:2, bike:3]
boolean res11 = jedis.sismember("bikes:racing:france", "bike:1");
System.out.println(res11); // >>> true
List<Boolean> res12 = jedis.smismember("bikes:racing:france", "bike:2", "bike:3", "bike:4");
System.out.println(res12); // >>> [true,true,false]
jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3");
jedis.sadd("bikes:racing:usa", "bike:1", "bike:4");
Set<String> res13 = jedis.sdiff("bikes:racing:france", "bikes:racing:usa");
System.out.println(res13); // >>> [bike:2, bike:3]
jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3");
jedis.sadd("bikes:racing:usa", "bike:1", "bike:4");
jedis.sadd("bikes:racing:italy", "bike:1", "bike:2", "bike:3", "bike:4");
Set<String> res14 = jedis.sinter("bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy");
System.out.println(res14); // >>> [bike:1]
Set<String> res15 = jedis.sunion("bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy");
System.out.println(res15); // >>> [bike:1, bike:2, bike:3, bike:4]
Set<String> res16 = jedis.sdiff("bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy");
System.out.println(res16); // >>> []
Set<String> res17 = jedis.sdiff("bikes:racing:usa", "bikes:racing:france");
System.out.println(res17); // >>> [bike:4]
Set<String> res18 = jedis.sdiff("bikes:racing:france", "bikes:racing:usa");
System.out.println(res18); // >>> [bike:2, bike:3]
jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5");
long res19 = jedis.srem("bikes:racing:france", "bike:1");
System.out.println(res18); // >>> 1
String res20 = jedis.spop("bikes:racing:france");
System.out.println(res20); // >>> bike:3
Set<String> res21 = jedis.smembers("bikes:racing:france");
System.out.println(res21); // >>> [bike:2, bike:4, bike:5]
String res22 = jedis.srandmember("bikes:racing:france");
System.out.println(res22); // >>> bike:4
jedis.close();
}
}
Redis数据结构:Set类型全面解析-腾讯云开发者社区-腾讯云
Redis高级之底层源码4——Set数据结构底层源码分析-CSDN博客
Zset**
有序不重复集合,底层跳表或压缩列表
数据结构
压缩链表
跟list压缩链表一致
跳表
typedef struct zskiplistNode {
robj *obj;
double score;
struct zskiplistNode *backward;
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned int span;
} level[];
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;
- zskiplistNode 结构体表示跳跃表中的一个节点。包含元素对象(obj)、分数(score)、指向前一个节点的指针(backward)和一个包含多个层的数组(level)。每一层都包含一个指向下一个节点的指针(forward)和一个表示当前节点到下一个节点的跨度(span)
- zskiplist 结构体表示一个跳跃表,包含头节点(header)、尾节点(tail)、跳跃表中的节点数量(length)和当前跳跃表的最大层数(level)
什么时候用跳表,什么时候用跳表?
这主要取决于两个配置参数:zset-max-ziplist-entries (默认值为128 单位:个) 和 zset-max-ziplist-value (默认值为64,单位:字节)。
[value,score]键值对数量少于128个;每个元素的长度小于64字节。
应用场景
排行榜
# 查询score评分在某个范围内的数据,从小到大排序,min 和 max 可以是 -inf 和 +inf来表示无穷小和无穷大,withscores加上他,连着评分一起查出
zrangebyscore <key> <min> <max> [withscores] [limit offset count]
使用 Redis Zset 有序集合实现排行榜功能(SpringBoot环境)_redis实时排行榜-CSDN博客
java jedis用法
package io.redis.examples;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.resps.Tuple;
public class SortedSetsExample {
public void run() {
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
long res1 = jedis.zadd("racer_scores", 10d, "Norem");
System.out.println(res1); // >>> 1
long res2 = jedis.zadd("racer_scores", 12d, "Castilla");
System.out.println(res2); // >>> 1
long res3 = jedis.zadd("racer_scores", new HashMap<String,Double>() {{
put("Sam-Bodden", 8d);
put("Royce", 10d);
put("Ford", 6d);
put("Prickett", 14d);
put("Castilla", 12d);
}});
System.out.println(res3); // >>> 4
List<String> res4 = jedis.zrange("racer_scores", 0, -1);
System.out.println(res4); // >>> [Ford, Sam-Bodden, Norem, Royce, Castil, Castilla, Prickett]
List<String> res5 = jedis.zrevrange("racer_scores", 0, -1);
System.out.println(res5); // >>> [Prickett, Castilla, Castil, Royce, Norem, Sam-Bodden, Ford]
List<Tuple> res6 = jedis.zrangeWithScores("racer_scores", 0, -1);
System.out.println(res6); // >>> [[Ford,6.0], [Sam-Bodden,8.0], [Norem,10.0], [Royce,10.0], [Castil,12.0], [Castilla,12.0], [Prickett,14.0]]
List<String> res7 = jedis.zrangeByScore("racer_scores", Double.MIN_VALUE, 10d);
System.out.println(res7); // >>> [Ford, Sam-Bodden, Norem, Royce]
long res8 = jedis.zrem("racer_scores", "Castilla");
System.out.println(res8); // >>> 1
long res9 = jedis.zremrangeByScore("racer_scores", Double.MIN_VALUE, 9d);
System.out.println(res9); // >>> 2
List<String> res10 = jedis.zrange("racer_scores", 0, -1);
System.out.println(res10); // >>> [Norem, Royce, Prickett]
long res11 = jedis.zrank("racer_scores", "Norem");
System.out.println(res11); // >>> 0
long res12 = jedis.zrevrank("racer_scores", "Norem");
System.out.println(res12); // >>> 2
long res13 = jedis.zadd("racer_scores", new HashMap<String,Double>() {{
put("Norem", 0d);
put("Sam-Bodden", 0d);
put("Royce", 0d);
put("Ford", 0d);
put("Prickett", 0d);
put("Castilla", 0d);
}});
System.out.println(res13); // >>> 3
List<String> res14 = jedis.zrange("racer_scores", 0, -1);
System.out.println(res14); // >>> [Castilla, Ford, Norem, Prickett, Royce, Sam-Bodden]
List<String> res15 = jedis.zrangeByLex("racer_scores", "[A", "[L");
System.out.println(res15); // >>> [Castilla, Ford]
long res16 = jedis.zadd("racer_scores", 100d, "Wood");
System.out.println(res16); // >>> 1
long res17 = jedis.zadd("racer_scores", 100d, "Henshaw");
System.out.println(res17); // >>> 1
long res18 = jedis.zadd("racer_scores", 100d, "Henshaw");
System.out.println(res18); // >>> 0
double res19 = jedis.zincrby("racer_scores", 50d, "Wood");
System.out.println(res19); // >>> 150.0
double res20 = jedis.zincrby("racer_scores", 50d, "Henshaw");
System.out.println(res20); // >>> 200.0
}
}
https://www.cnblogs.com/hld123/p/18074778
redis的基础底层篇 zset的详解_zset底层结构-CSDN博客
Hash**
key-value结构 底层哈希表或压缩链表
数据结构
哈希表
与上面一致
压缩链表
与上面一致
什么时候用哈希表,什么时候用压缩链表?
在redis6中哈希对象报错的键值对个数要小于512,所有的键值对的键和值的字符串长度都小于64个字节时用ziplist否则用hashtable。
注意:ziplist可以升级为hashtable,但hashtable不能降级为ziplist,在节省内存空间方面哈希表是没有压缩列表高效的!
应用场景 - 每个 hash 可以存储 2的32次方 - 1 键值对(40多亿)。
存储对象
redis 127.0.0.1:6379> HMSET user:1 username redis.net.cn password redis.net.cn points 200
OK
redis 127.0.0.1:6379> HGETALL user:1
1) "username"
2) "redis.net.cn"
3) "password"
4) "redis.net.cn"
5) "points"
6) "200"
java jedis用法
package io.redis.examples;
import redis.clients.jedis.UnifiedJedis;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HashExample {
public void run() {
try (UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379")) {
Map<String, String> bike1 = new HashMap<>();
bike1.put("model", "Deimos");
bike1.put("brand", "Ergonom");
bike1.put("type", "Enduro bikes");
bike1.put("price", "4972");
Long res1 = jedis.hset("bike:1", bike1);
System.out.println(res1); // 4
String res2 = jedis.hget("bike:1", "model");
System.out.println(res2); // Deimos
String res3 = jedis.hget("bike:1", "price");
System.out.println(res3); // 4972
Map<String, String> res4 = jedis.hgetAll("bike:1");
System.out.println(res4); // {type=Enduro bikes, brand=Ergonom, price=4972, model=Deimos}
List<String> res5 = jedis.hmget("bike:1", "model", "price");
System.out.println(res5); // [Deimos, 4972]
Long res6 = jedis.hincrBy("bike:1", "price", 100);
System.out.println(res6); // 5072
Long res7 = jedis.hincrBy("bike:1", "price", -100);
System.out.println(res7); // 4972
Long res8 = jedis.hincrBy("bike:1:stats", "rides", 1);
System.out.println(res8); // 1
Long res9 = jedis.hincrBy("bike:1:stats", "rides", 1);
System.out.println(res9); // 2
Long res10 = jedis.hincrBy("bike:1:stats", "rides", 1);
System.out.println(res10); // 3
Long res11 = jedis.hincrBy("bike:1:stats", "crashes", 1);
System.out.println(res11); // 1
Long res12 = jedis.hincrBy("bike:1:stats", "owners", 1);
System.out.println(res12); // 1
String res13 = jedis.hget("bike:1:stats", "rides");
System.out.println(res13); // 3
List<String> res14 = jedis.hmget("bike:1:stats", "crashes", "owners");
System.out.println(res14); // [1, 1]
}
}
}
Redis底层数据结构之Hash_redis hash底层结构-CSDN博客
bitmap*
位图不是一种实际的数据类型,而是在String类型上定义的一组面向位的操作,它被视为位向量。由于字符串是二进制安全的blob,其最大长度为512 MB,因此它们适合设置最多2^32个不同的位。
基本命令
SETBIT将给定偏移量处的位设置为0或1。
GETBIT返回给定偏移量处的位的值。
java用法
package io.redis.examples;
import org.junit.Assert;
import org.junit.Test;
import redis.clients.jedis.UnifiedJedis;
public class BitMapsExample {
public void run() {
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
boolean res1 = jedis.setbit("pings:2024-01-01-00:00", 123, true);
System.out.println(res1); // >>> false
boolean res2 = jedis.getbit("pings:2024-01-01-00:00", 123);
System.out.println(res2); // >>> true
boolean res3 = jedis.getbit("pings:2024-01-01-00:00", 456);
System.out.println(res3); // >>> false
long res4 = jedis.bitcount("pings:2024-01-01-00:00");
System.out.println(res4); // >>> 1
}
}
hyperloglog*
HyperLogLog是一种概率数据结构,用于估计集合的基数。
HyperLogLog是一种概率数据结构,用于估计集合的基数。作为一种概率数据结构,HyperLogLog以完美的准确性换取有效的空间利用。
Redis HyperLogLog实现最多使用12 KB,标准误差为0.81%。
基本命令
PFADD向HyperLogLog添加一个项目。
PFCOUNT返回集合中项目数量的估计值。
PFMERGE将两个或多个hyperloglog合并为一个。
java用法
package io.redis.examples;
import org.junit.Assert;
import org.junit.Test;
import redis.clients.jedis.UnifiedJedis;
public class HyperLogLogExample {
public void run() {
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
long res1 = jedis.pfadd("bikes", "Hyperion", "Deimos", "Phoebe", "Quaoar");
System.out.println(res1); // >>> 1
long res2 = jedis.pfcount("bikes");
System.out.println(res2); // >>> 4
long res3 = jedis.pfadd("commuter_bikes", "Salacia", "Mimas", "Quaoar");
System.out.println(res3); // >>> 1
String res4 = jedis.pfmerge("all_bikes", "bikes", "commuter_bikes");
System.out.println(res4); // >>> OK
long res5 = jedis.pfcount("all_bikes");
System.out.println(res5); // >>> 6
}
}
geospatial*
Redis地理空间索引允许您存储坐标并搜索它们。这种数据结构对于在给定半径或边界框内查找附近的点非常有用。
基本用法
GEOADD将位置添加到给定的地理空间索引(注意,使用此命令时经度在纬度之前)。
GEOSEARCH返回具有给定半径或边界框的位置。
java 用法
package io.redis.examples;
import redis.clients.jedis.GeoCoordinate;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.args.GeoUnit;
import redis.clients.jedis.resps.GeoRadiusResponse;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
public class GeoExample {
public void run() {
try (UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379")) {
long res1 = jedis.geoadd("bikes:rentable", -122.27652, 37.805186, "station:1");
System.out.println(res1); // 1
long res2 = jedis.geoadd("bikes:rentable", -122.2674626, 37.8062344, "station:2");
System.out.println(res2); // 1
long res3 = jedis.geoadd("bikes:rentable", -122.2469854, 37.8104049, "station:3");
System.out.println(res2); // 1
List<GeoRadiusResponse> res4 = jedis.geosearch(
"bikes:rentable",
new GeoCoordinate(-122.27652, 37.805186),
5,
GeoUnit.KM
);
List<String> members = res4.stream() //
.map(GeoRadiusResponse::getMemberByString) //
.collect(Collectors.toList());
System.out.println(members); // [station:1, station:2, station:3]
}
}
}
Streams
Redis流是一种数据结构,它的行为类似于仅追加日志,但也实现了一些操作来克服典型的仅追加日志的一些限制。其中包括O(1)时间内的随机访问和复杂的消费策略,例如消费者群体。您可以使用流来实时记录和同时联合事件。Redis流用例包括:
- 事件溯源(例如,跟踪用户操作、点击等)
- 传感器监控(例如,现场设备的读数)
- 通知(例如,将每个用户的通知记录存储在单独的流中)
基本用法
XADD向流添加一个新条目。
XREAD读取一个或多个条目,从给定位置开始,并在时间上向前移动。
XRANGE返回两个提供的条目id之间的条目范围。
XLEN返回流的长度。
java jedis用法
package io.redis.examples;
import redis.clients.jedis.StreamEntryID;
import redis.clients.jedis.UnifiedJedis;
public class StreamsExample {
public void run(){
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
StreamEntryID res1 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Castilla");put("speed","30.2");put("position","1");put("location_id","1");}} , XAddParams.xAddParams());
System.out.println(res1); // >>> 1701760582225-0
StreamEntryID res2 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Norem");put("speed","28.8");put("position","3");put("location_id","1");}} , XAddParams.xAddParams());
System.out.println(res2); // >>> 1701760582225-1
StreamEntryID res3 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Prickett");put("speed","29.7");put("position","2");put("location_id","1");}} , XAddParams.xAddParams());
System.out.println(res3); // >>> 1701760582226-0
List<StreamEntry> res4 = jedis.xrange("race:france","1701760582225-0","+",2);
System.out.println(res4); // >>> [1701760841292-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701760841292-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
List<Map.Entry<String, List<StreamEntry>>> res5= jedis.xread(XReadParams.xReadParams().block(300).count(100),new HashMap<String,StreamEntryID>(){{put("race:france",new StreamEntryID());}});
System.out.println(
res5
); // >>> [race:france=[1701761996660-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701761996661-0 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701761996661-1 {rider=Prickett, speed=29.7, location_id=1, position=2}]]
StreamEntryID res6 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Castilla");put("speed","29.9");put("position","2");put("location_id","1");}} , XAddParams.xAddParams());
System.out.println(res6); // >>> 1701762285679-0
long res7 = jedis.xlen("race:france");
System.out.println(res7); // >>> 4
StreamEntryID res8 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Castilla");}},XAddParams.xAddParams().id("0-1"));
System.out.println(res8); // >>> 0-1
StreamEntryID res9 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-2"));
System.out.println(res9); // >>> 0-2
try {
StreamEntryID res10 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Prickett");}},XAddParams.xAddParams().id("0-1"));
System.out.println(res10); // >>> 0-1
}
catch (JedisDataException e){
System.out.println(e); // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
}
StreamEntryID res11 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-*"));
System.out.println(res11);
List<StreamEntry> res12 = jedis.xrange("race:france","-","+");
System.out.println(
res12
); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
List<StreamEntry> res13 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000),String.valueOf(System.currentTimeMillis()+1000));
System.out.println(
res13
); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
List<StreamEntry> res14 = jedis.xrange("race:france","-","+",2);
System.out.println(res14); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
List<StreamEntry> res15 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000)+"-0","+",2);
System.out.println(res15); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
List<StreamEntry> res16 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()+1000)+"-0","+",2);
System.out.println(res16); // >>> []
List<StreamEntry> res17 = jedis.xrevrange("race:france","+","-",1);
System.out.println(res17); // >>> [1701765218592-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
List<Map.Entry<String, List<StreamEntry>>> res18= jedis.xread(XReadParams.xReadParams().count(2),new HashMap<String,StreamEntryID>(){{put("race:france",new StreamEntryID());}});
System.out.println(
res18
); // >>> [race:france=[1701765384638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701765384638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]]
String res19 = jedis.xgroupCreate("race:france","france_riders",StreamEntryID.LAST_ENTRY,false);
System.out.println(res19); // >>> OK
String res20 = jedis.xgroupCreate("race:italy","italy_riders",StreamEntryID.LAST_ENTRY,true);
System.out.println(res20); // >>> OK
StreamEntryID id1 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Castilaa");}},XAddParams.xAddParams());
StreamEntryID id2 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Royce");}},XAddParams.xAddParams());
StreamEntryID id3 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Sam-Bodden");}},XAddParams.xAddParams());
StreamEntryID id4 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Prickett");}},XAddParams.xAddParams());
StreamEntryID id5 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Norem");}},XAddParams.xAddParams());
List<Map.Entry<String, List<StreamEntry>>> res21 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}});
System.out.println(res21); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]
List<Map.Entry<String, List<StreamEntry>>> res22 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",new StreamEntryID());}});
System.out.println(res22); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]
long res23 = jedis.xack("race:italy","italy_riders",id1);
System.out.println(res23); // >>> 1
List<Map.Entry<String, List<StreamEntry>>> res24 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",new StreamEntryID());}});
System.out.println(res24); // >>> [race:italy=[]]
List<Map.Entry<String, List<StreamEntry>>> res25 = jedis.xreadGroup("italy_riders","Bob", XReadGroupParams.xReadGroupParams().count(2),new HashMap<String,StreamEntryID>(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}});
System.out.println(res25); // >>> [race:italy=[1701767632261-1 {rider=Royce}, 1701767632262-0 {rider=Sam-Bodden}]]
StreamPendingSummary res26 = jedis.xpending("race:italy","italy_riders");
System.out.println(res26.getConsumerMessageCount()); // >>> {Bob=2}
List<StreamPendingEntry> res27 = jedis.xpending("race:italy","italy_riders",XPendingParams.xPendingParams().start(StreamEntryID.MINIMUM_ID).end(StreamEntryID.MAXIMUM_ID).count(10));
System.out.println(res27); // >>> [1701768567412-1 Bob idle:0 times:1, 1701768567412-2 Bob idle:0 times:1]
List<StreamEntry> res28 = jedis.xrange("race:italy",id2.toString(),id2.toString());
System.out.println(res28); // >>> [1701768744819-1 {rider=Royce}]
List<StreamEntry> res29 = jedis.xclaim("race:italy","italy_riders","Alice", 0L, XClaimParams.xClaimParams().time(60000),id2);
System.out.println(res29); // >>> [1701769004195-1 {rider=Royce}]
Map.Entry<StreamEntryID, List<StreamEntry>> res30 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID("0-0"),XAutoClaimParams.xAutoClaimParams().count(1));
System.out.println(res30); // >>> [1701769266831-2=[1701769266831-1 {rider=Royce}]
Map.Entry<StreamEntryID, List<StreamEntry>> res31 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID(id2.toString()),XAutoClaimParams.xAutoClaimParams().count(1));
System.out.println(res31); // >>> [0-0=[1701769605847-2 {rider=Sam-Bodden}]
StreamInfo res32 = jedis.xinfoStream("race:italy");
System.out.println(
res32.getStreamInfo()
); // >>> {radix-tree-keys=1, radix-tree-nodes=2, entries-added=5, length=5, groups=1, max-deleted-entry-id=0-0, first-entry=1701769637612-0 {rider=Castilaa}, last-generated-id=1701769637612-4, last-entry=1701769637612-4 {rider=Norem}, recorded-first-entry-id=1701769637612-0}
List<StreamGroupInfo> res33 = jedis.xinfoGroups("race:italy");
for (StreamGroupInfo a : res33){
System.out.println(
a.getGroupInfo()
); // >>> {last-delivered-id=1701770253659-0, lag=2, pending=2, name=italy_riders, consumers=2, entries-read=3}
}
List<StreamConsumersInfo> res34 = jedis.xinfoConsumers("race:italy","italy_riders");
for (StreamConsumerInfo a : res34){
System.out.println(
a.getConsumerInfo()
); // {inactive=1, idle=1, pending=1, name=Alice} , {inactive=3, idle=3, pending=1, name=Bob}
}
jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Jones");}},XAddParams.xAddParams().maxLen(10));
jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Wood");}},XAddParams.xAddParams().maxLen(10));
jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Henshaw");}},XAddParams.xAddParams().maxLen(10));
long res35 = jedis.xlen("race:italy");
System.out.println(res35); // >>> 8
List<StreamEntry> res36 = jedis.xrange("race:italy","-","+");
System.out.println(res36); // >>> [1701771219852-0 {rider=Castilaa}, 1701771219852-1 {rider=Royce}, 1701771219853-0 {rider=Sam-Bodden}, 1701771219853-1 {rider=Prickett}, 1701771219853-2 {rider=Norem}, 1701771219858-0 {rider=Jones}, 1701771219858-1 {rider=Wood}, 1701771219859-0 {rider=Henshaw}]
StreamEntryID id6 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Smith");}},XAddParams.xAddParams().maxLen(2));
List<StreamEntry> res37 = jedis.xrange("race:italy","-","+");
System.out.println(res37); // >>> [1701771067332-1 {rider=Henshaw}, 1701771067332-2 {rider=Smith}]
long res38 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10).exactTrimming());
System.out.println(res38); /// >>> 0
long res39 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10));
System.out.println(res39); /// >>> 0
List<StreamEntry> res40 = jedis.xrange("race:italy","-","+");
System.out.println(res40); // >>> [1701771356428-2 {rider=Henshaw}, 1701771356429-0 {rider=Smith}]
long res41 = jedis.xdel("race:italy",id6);
System.out.println(res41); // >>> 1
List<StreamEntry> res42 = jedis.xrange("race:italy","-","+");
System.out.println(res42); // >>> [1701771517639-1 {rider=Henshaw}]
}
}
Bitfields
Redis位域允许您设置、递增和获取任意位长度的整数值。例如,您可以操作从无符号1位整数到有符号63位整数的任何整数。
这些值使用二进制编码的Redis字符串存储。位字段支持原子读、写和自增操作,这使得它们成为管理计数器和类似数值的好选择。
基本命令
BITFIELD自动设置、递增和读取一个或多个值。
BITFIELD_RO是BITFIELD的只读变体。