redis的数据结构
redis存储的是:key,value格式的数据,其中key都是字符串,value有5种不同的数据结构
1、字符串类型 String
2、哈希类型 hash:map格式 (对象-属性)
3、列表类型 List :linkedlist格式(允许重复元素)
4、集合类型 set(不允许重复元素)
5、有序集合类型 sortedset(不允许重复元素,且会自动排序)
value值都是存的字符串类型的值
了解Redis五大数据类型之前我们先来了解一个Redis的键(key)
Redis键(key)
指令 | 含义 |
keys * | 查看当前库中所有的key |
exists key | 判断某个key是否存在,如exists k1 |
type key | 查看某个key是什么类型 |
del key | 删除指定的key 如del k1 |
unlink key | 根据value选择非阻塞删除 仅将keys从keyspace原数据中删除,真正删除会在后续异步操作。 |
expire key 100 | 为给定的key设置过期时间,其中数值是秒单位,过期后获取的数值是nil |
ttl key | 查看还有多少秒过期 -1表示永不过期 -2 表示已过期 其他值就是多少秒后过期 |
select 数值 | 切换数据库,默认是0号数据库 |
dbsize | 查看当前数据库的key的数量 |
flushdb | 清空当前库 |
flushall | 通杀全部库 |
代码操作:
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set k1 Tom
OK
127.0.0.1:6379> set k2 Jason
OK
127.0.0.1:6379> set k3 jerry
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> expire k1 30
(integer) 1
127.0.0.1:6379> get k1
"Tom"
127.0.0.1:6379> get k1
"Tom"
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
127.0.0.1:6379> expire k2 60
(integer) 1
127.0.0.1:6379> ttl k2
(integer) 56
127.0.0.1:6379> exists k2
(integer) 1
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set k4 12
OK
127.0.0.1:6379> keys *
1) "k4"
127.0.0.1:6379> exists k4
(integer) 1
127.0.0.1:6379> del k4
(integer) 1
127.0.0.1:6379> dbsize
(integer) 0
127.0.0.1:6379> set k5 8
OK
127.0.0.1:6379> dbsize
(integer) 1
127.0.0.1:6379>
1、Redis字符串(String)
String是Redis最基本的类型,是一个key对应一个value。key一直都是字符串类型的,这个String类型是指value值的类型。一个Redis中字符串value最多可以是512M。
1.1、常用命令
命令 | 含义解释 | |
增操作 | set key value | 添加键值对,key不存在时直接添加入库,key存在时将之前的value覆盖 |
mset <key1><value1><key2><value2> ..... | 同时设置 一个或多个key-value | |
msetnx key1 value1 key2 value2 key3 value3.... | 同时设置一个或多个key和value,必须保证key不存在,设置中有一个存在就返回0(所有设置不成功)原子性,一个不成功就失败。 | |
查操作 | get key | 查询对应键值,返回value值 |
mget key1 key2 key3 .... | 同时获取一个或多个key的value | |
getrange key start end | 获取key的value值的start到end下标的值,类似Java中substring | |
strlen key | 获取key的value值的长度,返回长度数值 | |
改操作 | append key value | 给指定的key追加到key的原值的末尾,返回追加后的value的长度值 |
setnx key value | 只有在key不存在时设置key的value,返回1表示设置成功,如果key存在就返回0,设置不成功 | |
incr key | 将key中存储的 "数字值" 增1,如果为空,新增值为1 (将字符串值解析成整型,将其加一,最后将结果保存为新的字符串值) 返回增1的数字 (increase增加) | |
incrby key 步长 | 对key中存储的"数字值"自增自定的步长 | |
decr key | 将key中存储的 "数字值" 减1,如果为空,新减值为-1 (将字符串值解析成整型,将其减一,最后将结果保存为新的字符串值) 返回减1的数字 (decrease 减少) | |
decr key 步长 | 对key中存储的"数字值"自减自定的步长 | |
setrange key start value | 用value值覆盖key所存储的字符串值,从start索引开始 | |
setex key <过期时间> value | 设置键的同时设置过期时间,单位是秒。 |
1.2、代码演示
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set k1 123a
OK
127.0.0.1:6379> set k2 12345
OK
127.0.0.1:6379> get k1
"123a"
127.0.0.1:6379> get k2
"12345"
127.0.0.1:6379> append k1 bc
(integer) 6
127.0.0.1:6379> get k1
"123abc"
127.0.0.1:6379> strlen k2
(integer) 5
127.0.0.1:6379> get k2
"12345"
127.0.0.1:6379> setnx k1 111
(integer) 0
127.0.0.1:6379> setnx k3 aaa
(integer) 1
127.0.0.1:6379> get k1
"123abc"
127.0.0.1:6379> get k3
"aaa"
127.0.0.1:6379> get k1
"123abc"
127.0.0.1:6379> incr k1
(error) ERR value is not an integer or out of range
127.0.0.1:6379> incr k2
(integer) 12346
127.0.0.1:6379> get k2
"12346"
127.0.0.1:6379> incr k2 12
(error) ERR wrong number of arguments for 'incr' command
127.0.0.1:6379> incrby k2 12
(integer) 12358
127.0.0.1:6379> get k2
"12358"
127.0.0.1:6379> decr k2
(integer) 12357
127.0.0.1:6379> decrby k2 12
(integer) 12345
127.0.0.1:6379> get k2
"12345"
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
127.0.0.1:6379> mset k4 1234 k5 123456
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k5"
4) "k4"
127.0.0.1:6379> mget k1 k2 k3 k4 k5
1) (nil)
2) "345"
3) "12345"
4) "1234"
5) "123456"
127.0.0.1:6379> setrange k1 0 1123
(integer) 4
127.0.0.1:6379> get k1
"1123"
127.0.0.1:6379> setex k1 100 123
OK
127.0.0.1:6379> ttl k1
(integer) 95
127.0.0.1:6379> ttl k1
(integer) 93
127.0.0.1:6379> ttl k1
(integer) 91
127.0.0.1:6379> ttl k1
(integer) 76
127.0.0.1:6379> ttl k1
(integer) -2
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379>
1.3、 数据结构
String的数据结构为简单动态字符串(Simple Dynamic String 简写SDS)就是可以修改字符串的值,内部结构实现类似Java的ArrayList,采用预分配冗余空间的方式。
如图内部为当前字符串实际分配的空间 capacity一般比实际字符串长度len高。当字符串长度小于1M时,扩容是加倍现有的空间,超过1M时扩容时一次多扩容1M的空间,但是字符串的最大长度为512M。
2、Redis列表(List)
"单键多值",Redis列表是简单的字符串列表,按照插入顺序排序,可以添加一个元素在列表的头或者尾部。
它的底层实际是双向链表
2.1、常用命令
命令 | 含义解释 | |
增 | lpush / rpush key value1 value2 value3 value4.... | 从左边/右边插入一个或者多个值 |
linsert key before value newvalue | 在value的左边插入一个newvalue值 | |
linsert key after value newvalue | 在value的右边插入一个newvalue值 | |
删 | lpop / rpop key | 从左边或者右边吐出一个值 (吐出来就从列表中删除) |
lrem key n value | 从左边删除n个value(从左到右)列表中没有n个就删除列表中有多少(少于n) | |
改 | rpoplpush key1 key2 | 从key1列表中吐一个值,插到key2列表左边 |
lset key index value | 将列表key下标为index的值替换为value | |
查 | lrange key start stop | 从列表key左到右获取元素(下标索引) 其中最右边的下标是-1 (只是获取出来不会从列表中删除) |
lindex key 索引下标 | 按照索引下标获取元素(从左到右) | |
llen key | 获取列表长度 |
2.2、代码演示
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush k1 v1 v2 v3 v4
(integer) 4
127.0.0.1:6379> rpush k1 1 2 3 4
(integer) 8
127.0.0.1:6379> lrange k1 0 -1
1) "v4"
2) "v3"
3) "v2"
4) "v1"
5) "1"
6) "2"
7) "3"
8) "4"
127.0.0.1:6379> linsert k1 before v4 v5
(integer) 9
127.0.0.1:6379> lrange k1 0 -1
1) "v5"
2) "v4"
3) "v3"
4) "v2"
5) "v1"
6) "1"
7) "2"
8) "3"
9) "4"
127.0.0.1:6379> linsert k1 after v4 v_4
(integer) 10
127.0.0.1:6379> lrange k1 0 -1
1) "v5"
2) "v4"
3) "v_4"
4) "v3"
5) "v2"
6) "v1"
7) "1"
8) "2"
9) "3"
10) "4"
127.0.0.1:6379> lpop k1
"v5"
127.0.0.1:6379> lrange k1 0 -1
1) "v4"
2) "v_4"
3) "v3"
4) "v2"
5) "v1"
6) "1"
7) "2"
8) "3"
9) "4"
127.0.0.1:6379> rpop k1
"4"
127.0.0.1:6379> lrange k1 0 -1
1) "v4"
2) "v_4"
3) "v3"
4) "v2"
5) "v1"
6) "1"
7) "2"
8) "3"
127.0.0.1:6379> lrem k1 2 v4
(integer) 1
127.0.0.1:6379> lrange k1 0 -1
1) "v_4"
2) "v3"
3) "v2"
4) "v1"
5) "1"
6) "2"
7) "3"
127.0.0.1:6379> lindex k1 3
"v1"
127.0.0.1:6379> lindex k1 -1
"3"
127.0.0.1:6379> llen k1
(integer) 7
127.0.0.1:6379> lset k1 1 v_3
OK
127.0.0.1:6379> lrange k1 0 -1
1) "v_4"
2) "v_3"
3) "v2"
4) "v1"
5) "1"
6) "2"
7) "3"
2.3、数据结构
List的数据结构为快速链表quickList。在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。
它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成quicklist。
Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用
3、redis集合(set)
Redis set的功能类似List的功能,特殊之处可以自动排重,当需要存储一个列表数据,不可以重复的,可以考虑set,并且set中还可以判断某个成员是否在一个set集合中,List中没有的。
Redis的Set是String类型的无序集合,底层是value是null的哈希表,添加、删除、查找的复杂度都是O(1).
3.1、常用命令
命令 | 含义解释 | |
增 | sadd key1 value1 value2 value3 .... | 将一个或者多个member元素加到集合中,已经存在集合中的不会添加进去,被忽略。 |
删 | srem key value1 value2 value3...... | 删除集合中的某一个或者多个元素 |
spop key 或者 spop key n | 随机从改key集合中吐出一个值或者指定个数(会从集合中删除) | |
移动 | smove key1 key2 value | 把集合key1中的value移到key2中,key1会把value移除,key2会添加上value(如果有就忽略) |
查 | smembers key | 取出集合key中的所有值,不会从集合中删除 |
srandmember key n | 随机从集合key中取出n个值,不会从集合中删除 | |
scard key | 返回集合key中的元素个数 | |
判断 | sismember key value | 判断集合key是否含有value值,有就返回1,没有返回0 |
集合操作 | sinter key1 key2 | 返回集合key1和key2的交集元素 |
sunion key1 key2 | 返回集合key1和key2的并集元素 | |
sdiff key1 key2 | 返回集合key1和集合key2的差集元素(key1中的,但是不包括key2的) |
3.2、代码演示
127.0.0.1:6379> sadd k1 1 2 3 4 5 a b c
(integer) 8
127.0.0.1:6379> sadd k2 1 2 3 adc 12
(integer) 5
127.0.0.1:6379> smembers k1
1) "4"
2) "a"
3) "5"
4) "c"
5) "b"
6) "1"
7) "2"
8) "3"
127.0.0.1:6379> smembers k2
1) "2"
2) "adc"
3) "3"
4) "1"
5) "12"
127.0.0.1:6379> srandmember k1 3
1) "5"
2) "b"
3) "2"
127.0.0.1:6379> scard k2
(integer) 5
127.0.0.1:6379> sismember k2 22
(integer) 0
127.0.0.1:6379> sismember k2 adc
(integer) 1
127.0.0.1:6379> srem k2 1 2 3
(integer) 3
127.0.0.1:6379> spop k1 2
1) "b"
2) "4"
127.0.0.1:6379> smembers k1
1) "a"
2) "5"
3) "c"
4) "1"
5) "2"
6) "3"
127.0.0.1:6379> sinter k1 k2
(empty array)
127.0.0.1:6379> sunion k1 k2
1) "12"
2) "a"
3) "5"
4) "adc"
5) "c"
6) "1"
7) "2"
8) "3"
127.0.0.1:6379> sdiff k1 k2
1) "1"
2) "c"
3) "a"
4) "5"
5) "2"
6) "3"
127.0.0.1:6379>
3.3、数据结构
Set数据结构是dict字典,字典是用哈希表实现的。
Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。
4、Redis哈希(hash)
Redis hash 是一个键值对集合,是一个string类型的field和value的映射表,hash特别适合用于存储对象。类似Java里面的Map<String,Object>
4.1、常用命令
命令 | 含义解释 | |
增 | hset key field value | 给key集合的filed键赋值value |
hmset key field1 value1 filed2 value2 ..... | 批量给key集合的filed1,filed赋值value1,value2... | |
hsetnx key filed value | 将哈希表中的域filed的值设置为value,当且仅当域的filed不存在 | |
查 | hget key field | 从key集合中的field获取出value值 |
hvals key | 列出hash集合中的所有value | |
hexists key field | 查看哈希表key中是否存在field | |
hkeys key | 列出hash集合的所有field | |
改 | hincrby key field 步长 | 为哈希表key中的域field的值加上增量 步长 |
4.2、代码演示
127.0.0.1:6379> hset user id 10001
(integer) 1
127.0.0.1:6379> hget user id
"10001"
127.0.0.1:6379> hset user name lisi
(integer) 1
127.0.0.1:6379> hget user name
"lisi"
127.0.0.1:6379> hmset student id 10001 name zs age 20 classId 182042
OK
127.0.0.1:6379> hget student age
"20"
127.0.0.1:6379> hkeys user
1) "id"
2) "name"
127.0.0.1:6379> clear
127.0.0.1:6379> keys *
1) "user"
2) "student"
127.0.0.1:6379> hkeys user
1) "id"
2) "name"
127.0.0.1:6379> hkeys student
1) "id"
2) "name"
3) "age"
4) "classId"
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hset stu Stno 18204201
(integer) 1
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hset stu:10001 Stno 18204201
(integer) 1
127.0.0.1:6379> hmset stu:10001 Sname ycy age 20 sex 1
OK
127.0.0.1:6379> hsetnx stu:10001 Sname 123
(integer) 0
127.0.0.1:6379> hsetnx stu:10001 classId 182042
(integer) 1
127.0.0.1:6379> hget stu:10001 Sname
"ycy"
127.0.0.1:6379> hval stu:10001
(error) ERR unknown command `hval`, with args beginning with: `stu:10001`,
127.0.0.1:6379> hvals stu:10001
1) "18204201"
2) "ycy"
3) "20"
4) "1"
5) "182042"
127.0.0.1:6379> hkeys stu:10001
1) "Stno"
2) "Sname"
3) "age"
4) "sex"
5) "classId"
127.0.0.1:6379> hincrby stu:10001 age 2
(integer) 22
127.0.0.1:6379> hget stu:10001 age
"22"
127.0.0.1:6379>
4.3、数据结构
Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。
5、Redis有序集合Zset(sorted set)
Redis有序集合Zset与普通集合set非常相似,是一个没有重复元素的字符串的集合。
不同之处:有序集合的每一个成员都关联了一个评分(score),这个评分被用来按照从最低分到最高分的方式排序集合中的成员,集合中的成员是唯一的,但是评分是可以重复的
元素是有序的,可以很快根据评分或者次序来获取一个范围的元素。
5.1、常用命令
命令 | 含义解释 | |
增 | zadd key score1 value1 score2 value2..... | 将一个或者多个member元素及其score值加入到有序集key当中 |
查 | zrange key start end [ withscores ] (小到大排序) | 返回有序集合可以中,下表索引start end之间的元素 带上withscores 可以让分数和值一起返回结果集 (小到大排序) |
zrevrange key start end [ withscores ] (大到小排序) | 返回有序集合可以中,下表索引start end之间的元素 带上withscores 可以让分数和值一起返回结果集 (大到小排序) | |
zrangebyscore key min max [ withscore] [ limit offset count ] | 返回有序集合key中,所有score在min和max之间(包括等于)次序排序(limit 下标 显示个数) (小到大排序) | |
zrevrangebyscore key max min [ withscore ] [ limit offset count ] | 返回有序集合key中,所有score在min和max之间(包括等于)次序排序(limit 下标 显示个数) (大到小排序) | |
zcount key min max | 统计该集合分数区间内的元素个数 | |
zrank key value | 返回该值在集合中的排名,从0开始的 | |
改 | zincrby key 步长 value | 为元素值为value的score加上步长 |
删 | zrem key value | 删除该集合下,指定值的元素 |
5.2、代码演示
127.0.0.1:6379> zadd top 500 java 200 C 300 python 400 C++ 600 php
(integer) 5
127.0.0.1:6379> zrange top 0 -1 withsocres
(error) ERR syntax error
127.0.0.1:6379> zrange top 0 -1 withscores
1) "C"
2) "200"
3) "python"
4) "300"
5) "C++"
6) "400"
7) "java"
8) "500"
9) "php"
10) "600"
127.0.0.1:6379> zrange top 0 -1
1) "C"
2) "python"
3) "C++"
4) "java"
5) "php"
127.0.0.1:6379> zrangebyscore top 200 400 limit offset 4
(error) ERR value is not an integer or out of range
127.0.0.1:6379> zrangebyscore top 200 400 limit offset 2
(error) ERR value is not an integer or out of range
127.0.0.1:6379> zrangebyscore top 200 400
1) "C"
2) "python"
3) "C++"
127.0.0.1:6379> zrangebyscore top 200 400 limit 2
(error) ERR syntax error
127.0.0.1:6379> zrangebyscore top 200 400 limit offset 1
(error) ERR value is not an integer or out of range
127.0.0.1:6379> zrangebyscore top 200 400 limit offset 0
(error) ERR value is not an integer or out of range
127.0.0.1:6379> zrangebyscore top 200 400 limit 1 1
1) "python"
127.0.0.1:6379> zrangebyscore top 200 400 limit 0 1
1) "C"
127.0.0.1:6379> zrangebyscore top 200 400 limit 0 2
1) "C"
2) "python"
127.0.0.1:6379> zrevrange top 0 -1
1) "php"
2) "java"
3) "C++"
4) "python"
5) "C"
127.0.0.1:6379> zrevrange top 200 400
(empty array)
127.0.0.1:6379> zrevrange top 0 -1 limit 0 3
(error) ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX
127.0.0.1:6379> zrevrange top 0 -1 withscores
1) "php"
2) "600"
3) "java"
4) "500"
5) "C++"
6) "400"
7) "python"
8) "300"
9) "C"
10) "200"
127.0.0.1:6379> zrevrange top 0 -1 withscores limit 0 1
(error) ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX
127.0.0.1:6379> zrangebyscore top 100 400 limit 0 1
1) "C"
127.0.0.1:6379> zrangebyscore top 100 600 limit 0 3
1) "C"
2) "python"
3) "C++"
127.0.0.1:6379> zrevrangebyscore top 600 400 limit 0 2
1) "php"
2) "java"
127.0.0.1:6379> zincrby top 100 java
"600"
127.0.0.1:6379> zrange top 0 -1 withscores
1) "C"
2) "200"
3) "python"
4) "300"
5) "C++"
6) "400"
7) "java"
8) "600"
9) "php"
10) "600"
127.0.0.1:6379> zrank top java
(integer) 3
案例:如何利用zset实现一个文章访问量的排行榜?
5.3、数据结构
它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,
另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。
zset底层使用了两个数据结构
(1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
(2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。