Redis6之(二)常用的五大数据类型
- 一、Redis字符串类型(String)
- 1.1 简介
- 1.2 常用命令
- 1.2.1 设置键值对
- 1.2.2 查询对应键值对
- 1.2.3 获取值的长度
- 1.2.4 将值追加到原值的末尾
- 1.2.5 增值操作
- 1.2.6 减值操作
- 1.2.7 设置过期时间
- 1.3 数据结构
- 1.4 Redis-Jedis测试
- 二、Redis列表类型(List)
- 2.1 简介
- 2.2 常用命令
- 2.2.1 向列表中插入值
- 2.2.2 获取列表长度
- 2.2.3 根据索引下标获取值
- 2.2.4 根据索引下标替换值
- 2.2.5 只保留列表指定片段
- 2.2.6 删除值
- 2.2.7 插入值
- 2.2.8 将元素从一个列表转到另一个列表
- 2.3 数据结构
- 2.4 Redis-Jedis测试
- 三、Redis集合类型(Set)
- 3.1 简介
- 3.2 常用命令
- 3.2.1 向集合中添加元素
- 3.2.2 取出集合中的值
- 3.2.3 删除集合中的值
- 3.2.4 判断集合中是否含有某元素
- 3.2.5 返回集合的元素个数
- 3.2.6 把集合中的一个值移动到另一个集合
- 3.2.7 返回两个集合的交集、并集、差集元素
- 3.3 数据结构
- 3.4 Redis-Jedis测试
- 四、Redis散列类型(Hash)
- 4.1 简介
- 4.2 常用命令
- 4.2.1 向集合中添加值
- 4.2.2 从集合中取值
- 4.2.3 列出集合中所有的field、value及字段的数量
- 4.2.4 为hash表key对应的field值加上增量
- 4.2.5 查看hash表key对应的field字段是否存在
- 4.2.6 删除字段
- 4.3 数据结构
- 4.4 Redis-Jedis测试
- 五、Redis有序集合类型(Zset)
- 5.1 简介
- 5.2 常用命令
- 5.2.1 向集合中添加值
- 5.2.2 从集合中取出值
- 5.2.3 从集合中取值并将其排序
- 5.2.4 获得集合中元素的数量
- 5.2.5 为集合中指定元素的score加上增量
- 5.2.6 统计集合指定分数区间内的元素个数
- 5.2.7 返回某值在集合中的排名
- 5.2.8 删除集合中的值
- 5.3 数据结构
- 5.3.1 Zset 使用的数据结构
- 5.3.2 跳跃表(跳表)
- 5.4 Redis-Jedis测试
一、Redis字符串类型(String)
1.1 简介
String 是 Redis 最基本的数据类型,也可以理解为与 Memcached 一模一样的类型:一个 key 对应一个 value
。
String 类型是二进制安全
的,这也意味着 Redis 的 String 可以包含任何数据。
String 类型是 Redis 最基本的数据类型,一个 Redis 中字符串 value 最多可以使 512 M。
小贴士:
二进制安全:内容能用字符串表示,也就是说都可以存到redis中。例如:图片用字节形式表示,可以做成字符串放到文件中,当然这个字符串也可以转换成图片。
1.2 常用命令
1.2.1 设置键值对
- 设置单个键值对
# 设置键值对,当key存在时,将原值覆盖掉
set key value
# 只有当key不存在时,设置key的值
setnx key value
- 设置多个键值对
同时设置多个key-value对,当且仅当所有给定的key都不存在时成立,也就是保证原子性
。
原子性:即有一个失败则都失败。
# 当键存在时,将原值覆盖掉
mset key1 value1 key2 value2...
# 同时设置多个key-value对,当且仅当所有给定的key都不存在
msetnx key1 value1 key2 value2...
- 按照范围设值
# 用value覆写key所存储的字符串值,从'起始位置'开始
setrange key 起始位置 value
- 以新换旧
设置新值同时获取旧值。
getset key value
1.2.2 查询对应键值对
- 单个键值对查询
# 根据键获取对应的值
get key
- 多个键值对查询
# 同时获取一个或多个value
mget key1 key2 key3...
- 按照范围查询
# 获取值的范围,类似于java中的substring,‘前后闭’
getrange key 起始位置 结束位置
1.2.3 获取值的长度
# 通过键获取值的长度
strlen key
1.2.4 将值追加到原值的末尾
将给定的值追加到原值的末尾。
append key value
1.2.5 增值操作
- 固定步长
对存储在指定key的数值执行原子的加1操作。只能对数字值操作,如果为空,新增值为1。
incr key
- 自定义步长(整数和浮点数步长)
将key增值相应的步长。
# 增加指定整数步长
incrby key 步长
# 增加指定浮点数步长
incrbyfloat key increment
小贴士:
所谓原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行直到结束,中间不会有任何线程的切换。
①在单线程中,能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间;
②在多线程中,不能被其他进程(线程)打断的操作就叫原子操作。
1.2.6 减值操作
- 固定步长
对存储在指定key的数值执行原子的减1操作。只能对数字值操作,如果为空,新增值为-1。
decr key
- 自定义步长
将key减值相应的步长。
decr by key 步长
1.2.7 设置过期时间
设置键值的同时,设置过期时间,单位为秒。
setex key 过期时间 value
1.3 数据结构
String 的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。
是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。
内部为当前字符串实际分配的空间 capacity 一般要高于实际字符串长度len。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M的空间。需要注意的是字符串最大长度为 512M。
1.4 Redis-Jedis测试
public class jedisTestString {
public static void main(String[] args) {
Jedis jedis = new Jedis("139.224.229.89", 6379);
// 设置键值对(多个)
jedis.mset("key1", "zhangsan", "key2", "lisi", "key3", "wangwu", "key4", "10");
// 获取对应的键值对(单个)
String key1 = jedis.get("key1");
System.out.println("key1:" + key1);
// 获取键值对(多个)
List<String> stringList = jedis.mget("key1", "key2", "key3");
for (String s : stringList){
System.out.println("======" + s);
}
// 获取值的长度
int length = jedis.get("key1").length();
System.out.println("key1.length:" + length);
// 将值追加到原值的末尾
jedis.append("key1", "APPEND").toString();
String appendValue = jedis.get("key1");
System.out.println("key1.append:" + appendValue);
// 增值操作(固定步长)
jedis.incr("key4");
System.out.println("key4+1:" + jedis.get("key4"));
// 增值操作(自定义步长)
jedis.incrBy("key4", 10);
System.out.println("key4+n:" + jedis.get("key4"));
// 减值操作(固定步长)
jedis.decr("key4");
System.out.println("key4-1:" + jedis.get("key4"));
// 减值操作(自定义步长)
jedis.decrBy("key4", 10);
System.out.println("key4-1:" + jedis.get("key4"));
// 设置过期时间 --- -2代表过期
jedis.setex("key5", 20, "zhaoliu");
System.out.println("key5:" + jedis.get("key5"));
System.out.println(jedis.ttl("key5"));
}
}
二、Redis列表类型(List)
2.1 简介
单键多值
Redis 列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。
它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
2.2 常用命令
2.2.1 向列表中插入值
- 从左边插入一个或多个值
lpush key value1 value2...
- 从右边插入一个或多个值
rpush key value1 value2 value3...
2.2.2 获取列表长度
llen key
2.2.3 根据索引下标获取值
- 获取指定下标对应的值
lindex key index
- 获取指定下标范围内的值(从左至右)
lrange key start stop
2.2.4 根据索引下标替换值
将下标为 index对应的值替换成 value。
lset key index value
2.2.5 只保留列表指定片段
删除指定索引范围之外的所有元素。
ltrim key start end
小贴士:
ltrim 命令常和 lpush命令一起使用来限制列表中元素的数量。
2.2.6 删除值
- 每次吐出一个值
值在键在,值光键亡。
# 从左边吐出一个值
lpop key
# 从右边吐出一个值
rpop key
- 从左边删除指定个数的指定值
lrem key n value
小贴士:
- 当 n>0 时,lrem命令会从列表左边开始删除前 n个值为 value的元素;
- 当 n<0时,lrem命令会从列表右边开始删除前 |n|个值为 value的元素;
- 当 n=0时,lrem命令会删除所有值为 value的元素。
2.2.7 插入值
在第一个指定值之前/后插入。
- 在指定值之前插入
在第一个 value值的前面插入 newvalue值。
linsert key before value newvalue
- 在指定值之后插入
在第一个 value值的后面插入 newvalue值。
linsert key after value newvalue
2.2.8 将元素从一个列表转到另一个列表
先执行rpop命令再指定lpush命令,从 fromKey的右边弹出一个元素,然后将其加入到 toKey的左边,并返回这个元素的值,整个过程是原子的。
rpoplpush fromKey toKey
2.3 数据结构
List 的数据结构为快速链表 quickList。
首先会在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist,也即是压缩列表。
它将所有的元素紧挨着一起存储,分配的是一块连续的内存。
当数据量比较多的时候才会改成 quicklist。
因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针 prev和 next。
Redis 将链表和 ziplist结合起来组成了 quicklist。也就是将多个 ziplist使用双向指针串起来使用。这样既满足了快速的插入删除功能,又不会出现太大的空间冗余。
2.4 Redis-Jedis测试
public class jedisTestList {
public static void main(String[] args) {
Jedis jedis = new Jedis("139.224.229.89", 6379);
// 从左边插入一个或多个值
jedis.lpush("lkey", "zhangsan", "lisi", "wangwu");
// 从右边插入一个或多个值
jedis.rpush("rkey", "zhangsansan", "lisisi", "wangwuwu");
// 获取列表长度
System.out.println("lkey.len:" + jedis.llen("lkey"));
System.out.println("rkey.len:" + jedis.llen("rkey"));
// 根据索引下标获取值
String lkeyValue = jedis.lindex("lkey", 1);
System.out.println("lkeyValue:" + lkeyValue);
String rkeyValue = jedis.lindex("rkey", 2);
System.out.println("rkeyValue:" + rkeyValue);
// 获取指定下标范围内的值
List<String> lkeyValues = jedis.lrange("lkey", 0, 1);
for (String s : lkeyValues){
System.out.println("======lkeyValues:" + s);
}
// 根据索引下标替换值
jedis.lset("lkey", 1, "我不是lisi");
System.out.println("lsetValue:" + jedis.lindex("lkey", 1));
// 在指定值之前插入值
jedis.linsert("lkey", ListPosition.BEFORE, "zhangsan", "我不是zhangsan");
System.out.println("lkey.beforeInsert:" + jedis.lrange("lkey", 0, 3));
// 在指定值之后插入值
jedis.linsert("rkey", ListPosition.AFTER, "lisisi", "我不是lisisi");
System.out.println("rkey.afterInsert:" + jedis.lrange("rkey", 0, 3));
// 每次吐出一个值
jedis.lpop("lkey");
System.out.println("lkey.lpop:" + jedis.lrange("lkey", 0, 3));
// 从左边删除指定个数的指定值
jedis.lrem("rkey", 2, "zhangsan");
System.out.println("rkey.lrem:" + jedis.lrange("rkey", 0, 3));
}
}
三、Redis集合类型(Set)
3.1 简介
Redis Set对外提供的功能与 list类似是一个列表的功能,特殊之处在于 set是可以自动排重的
,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且 set提供了判断某个成员是否在一个 set集合内的重要接口,这个也是 list所不能提供的。
Redis的 Set是 String类型的无序集合。
它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是 O(1)
。
一个算法,随着数据的增加,执行时间的长短,如果是 O(1),数据增加,查找数据的时间不变。
3.2 常用命令
3.2.1 向集合中添加元素
sadd key value1 value2 value3...
3.2.2 取出集合中的值
- 取出集合里的所有值
smembers key
- 随机取出集合中的n个值
随机取出集合中的n个值,不会从集合中删除。
srandmember key n
3.2.3 删除集合中的值
- 随机从集合中吐出一个值
spop key
- 删除集合中的某个元素
srem key value1 value2...
3.2.4 判断集合中是否含有某元素
判断集合 key中是否含有 value元素,有1,没有0。
sismember key value
3.2.5 返回集合的元素个数
scard key
3.2.6 把集合中的一个值移动到另一个集合
把集合中的一个值从一个集合移动到另一个集合。
smove fromKey toKey value
3.2.7 返回两个集合的交集、并集、差集元素
- 返回两个集合的交集元素
k1和k2共有的元素。
sinter key1 key2
- 返回两个集合的并集元素
k1和k2不重复的所有元素。
sunion key1 key2
- 返回两个集合的差集元素
k1中的不包含k2的元素。
sdiff key1 key2
3.3 数据结构
Set 数据结构是 dict字典,字典是用哈希表实现的。
Java 中的 HashSet的内部实现使用的是 HashMap,只不过所有的value都指向同一个对象。
Redis 的 Set结构也是一样,它的内部也使用 hash结构,所有的 value都指向同一个内部值。
3.4 Redis-Jedis测试
public class jedisTestSet {
public static void main(String[] args) {
Jedis jedis = new Jedis("139.224.229.89", 6379);
// 向集合中添加元素
jedis.sadd("key1", "v1", "v2", "v3");
jedis.sadd("key2", "vv1", "vv2", "vv3");
// 取出该集合中的值
Set<String> stringSet = jedis.smembers("key1");
for (String s : stringSet){
System.out.println("key1.add=====" + s);
}
// 判断集合中是否含有某元素
System.out.println(jedis.sismember("key1", "v2"));
// 返回集合的元素个数
System.out.println(jedis.scard("key1"));
// 把集合中的一个值移动到另一集合
jedis.smove("key1", "key2", "v2");
// 取出该集合中的值
Set<String> stringSet1 = jedis.smembers("key1");
for (String s : stringSet1){
System.out.println("key1=====" + s);
}
Set<String> stringSet2 = jedis.smembers("key2");
for (String s : stringSet2){
System.out.println("key2=====" + s);
}
// 随机从集合中吐出一个值
jedis.spop("key1");
// 删除集合中的某个元素
jedis.srem("key2", "vv1", "v2");
// 返回两个集合的交集元素
jedis.sinter("key1", "key2");
// 返回两个集合的并集元素
jedis.sunion("key1", "key2");
// 返回两个集合的差集元素
jedis.sdiff("key1", "key2");
}
}
四、Redis散列类型(Hash)
4.1 简介
Redis hash是一个键值对集合。
Redis hash是一个 String类型的 field和 value的映射表,hash 特别适合用于存储对象,类似于 Java里面的 Map<String, Object>。
用户 ID为查找的 key,存储的 value用户对象包含姓名,年龄,生日等信息,如果用普通的 key/value结构来存储,主要有以下两种存储方式:
引入 Hash后,通过 key + field就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题:
4.2 常用命令
4.2.1 向集合中添加值
- 单个值设置
# 单个设值
hset key field value
小贴士:
hset 命令的方便之处在于不区分插入和更新操作,这意味着修改数据时不用事先判断字段是否存在来决定要执行的是插入操作(insert)还是更新操作(update)。当执行的是插入操作时(即之前字段不存在),hset命令会返回1;当执行的是更新操作时(即之前字段已经存在),hset命令会返回0.更进一步,当键本身不存在时,hset命令还是会自动建立它。
- 批量设置值
# 批量设值
hmset key field1 value1 field2 value2...
- 给不存在的field设置value
将hash表中的 field设值为 value,当且仅当 field不存在时。
hsetnx key field value
4.2.2 从集合中取值
- 根据 key+field 获取值
hget key field
- 根据 key获取值
hgetall key
4.2.3 列出集合中所有的field、value及字段的数量
- 列出hash集合中所有的field
hkeys key
- 列出hash集合中所有的value
hvals key
- 获得字段的数量
hlen key
4.2.4 为hash表key对应的field值加上增量
hincrby key field increment
小贴士:
如果之前 user:1001 不存在,hincrby命令会自动建立该键并默认 age字段在执行命令前的值为"0",命令的返回值时增值后的字段值。
4.2.5 查看hash表key对应的field字段是否存在
存在返回1,不存在返回0。
hexists key field
4.2.6 删除字段
hdel 命令可以删除一个或多个字段,返回值是被删除的字段的个数。
hdel key field
4.3 数据结构
Hash 类型对应的数据结构是两种:ziplist 和 hashtable。当 field-value 长度较短且个数较少时,使用 ziplist,否则使用 hashtable。
4.4 Redis-Jedis测试
public class jedsiTestHash {
public static void main(String[] args) {
Jedis jedis = new Jedis("139.224.229.89", 6379);
// 向集合中添加值(单个值)
jedis.hset("user:1001", "id", "1");
jedis.hset("user:1001", "name", "zhangsan");
jedis.hset("user:1001", "age", "23");
jedis.hset("user:1001", "gender", "1");
// 向集合中添加值(多个值)
Map<String, String> map = new HashMap<>();
map.put("id", "1");
map.put("name", "lisi");
map.put("age", "34");
map.put("gender", "1");
jedis.hmset("user:1002", map);
// 从集合中取值
String name = jedis.hget("user:1001", "name");
System.out.println("user:1001.name:" + name);
// 列出集合中所有的field
Set<String> stringSet = jedis.hkeys("user:1001");
for (String s : stringSet){
System.out.println("field======" + s);
}
// 列出集合中所有的value
List<String> stringList = jedis.hvals("user:1002");
for (String s : stringList){
System.out.println("value======" + s);
}
// 查看hash表key对应的field是否存在
System.out.println(jedis.hexists("user:1001", "gender"));
System.out.println(jedis.hexists("user:1002", "pwd"));
// 为hash表key对应的field值上加上增量
jedis.hincrBy("user:1001", "age", 10);
System.out.println(jedis.hget("user:1001", "age"));
}
}
五、Redis有序集合类型(Zset)
5.1 简介
Redis 有序集合 Zset与普通集合 set非常相似,是一个没有重复元素的字符串集合。
不同之处是有序集合的每个成员都关联了一个评分(score)
,这个评分被用来按照从最低分到最高分的方式排序集合中的成员。集合中的成员是唯一的,但是评分是可以重复的。
因为元素是有序的,所以也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
访问有序集合的中间元素也是非常快的,因此能够使用有序集合作为一个没有重复成员的智能列表。
5.2 常用命令
5.2.1 向集合中添加值
zadd key score1 value1 score2 value2...
5.2.2 从集合中取出值
- 取出指定下标间的元素
# 不带分数返回
zrange key start stop
# 带分数返回
zrange key start stop withscores
- 取出指定分数之间的元素
返回有序集合 key中,所有score值介于 min和 max之间(包括等于 min或 max)的元素,有序集合元素按照 score值递增的次序排列。
# 不带分数返回
zrangebyscore key min max
# 带分数返回
zrangescore key min max withscores
- 获得元素的分数
zscore key value
5.2.3 从集合中取值并将其排序
- 升序排列
获取指定分数之间的元素默认按照升序排序。
# 不带分数返回
zrangebyscore key min max
# 带分数返回
zrangebyscore key min max withscores
- 降序排列
# 不带分数返回
zrevrangebyscore key max min
# 带分数返回
zrevrangebyscore key max min withscores
5.2.4 获得集合中元素的数量
zcard key
5.2.5 为集合中指定元素的score加上增量
# 为value的score加上increment
zincrby key increment value
5.2.6 统计集合指定分数区间内的元素个数
zcount key min max
5.2.7 返回某值在集合中的排名
zrank key value
5.2.8 删除集合中的值
- 删除一个或多个值
# 删除一个值
zrem key value
# 删除多个值
zrem key value1 value2...
- 按照排名范围删除元素
按照元素分数大小从小到大的顺序删除处在指定排名范围内的所有元素(左右均闭合),并返回删除的元素数量。
zremrangebyrank key start stop
- 按照分数范围删除元素
删除指定分数范围内的所有元素(左右均闭合),返回值是删除的元素数量。
zremrangebyscore key min max
5.3 数据结构
5.3.1 Zset 使用的数据结构
SortedSet(Zset)是 Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构 Map<String, Double>,可以给每一个元素 value赋予一个权重 score,另一方面它又类似于 TreeSet,内部的元素会按照权重 score进行排序,可以得到每个元素的名次,还可以通过 score的范围来获取元素的列表。
Zset 底层使用了两个数据结构:
- hash,hash的作用就是关联元素 value和权重 score,保障元素 value的唯一性,可以通过元素 value找到相应的 score值。
- 跳跃表,跳跃表的目的在于给元素 value排序,根据 score的范围获取元素列表。
5.3.2 跳跃表(跳表)
有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等等。对于有序集合的底层实现,可以使用数组、平衡树、链表等等。数组不便于元素的插入和删除;平衡树或红黑树虽然效率高但是结构复杂;链表查询需要遍历所有效率低。Redis 采用的是跳跃表。
跳跃表效率堪比红黑树,实现远比红黑树简单。
对比有序链表和跳跃表,从链表中查询出51:
- 有序链表
要查找值为51的元素,需要从第一个元素开始依次查找、比较才能找到。共需要6次比较。 - 跳跃表
从第2层开始,1节点比51节点小,向后比较;
21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层;
在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下;
在第0层,51节点为要查找的节点,节点被找到,共查找4次。
从此可以看出跳跃表比有序链表效率要高。
5.4 Redis-Jedis测试
public class jedsiTestZset {
public static void main(String[] args) {
Jedis jedis = new Jedis("139.224.229.89", 6379);
// 向集合中添加值
jedis.zadd("book", 400d, "java");
jedis.zadd("book", 100d, "c#");
jedis.zadd("book", 200d, "php");
jedis.zadd("book", 500d, "html");
jedis.zadd("book", 300d, "js");
// 取出指定下标间的元素
Set<String> bookListByIndex = jedis.zrange("book", 0, -1);
for (String s : bookListByIndex){
System.out.println("bookListByIndex======" + s);
}
// 取出指定分数之间的元素
Set<String> bookListByScore = jedis.zrangeByScore("book", 200d, 400d);
for (String s : bookListByScore){
System.out.println("bookListByScore======" + s);
}
// 删除集合中的指定值
jedis.zrem("book", "java", "php");
// 为集合中指定元素的score加上增量
jedis.zincrby("book", 10, "html");
// 统计集合指定分数区间内的元素个数
Long nums = jedis.zcount("book", 100, 300);
System.out.println("指定分数区间内的元素个数:" + nums);
// 返回某值在集合中的排名
Long zrank = jedis.zrank("book", "js");
System.out.println("js在集合中的排名:" +zrank);
}
}