第三章 Redis基本操作与数据类型

一、Redis基本操作

1.连接redis

root@3f706a745e74:/data# redis-cli 
127.0.0.1:6379>

2.查看所有数据

127.0.0.1:6379> keys *
1) "a"

#查看时注意不要轻易使用,如想查看数据,先查看数据量DBSIZE
127.0.0.1:6379> DBSIZE
(integer) 1

3.添加数据

127.0.0.1:6379> set k1 v1
OK

4.查看数据

127.0.0.1:6379> keys *
1) "k1"

127.0.0.1:6379> get k1
"v1"

5.删除数据

127.0.0.1:6379> keys *
1) "k1"

127.0.0.1:6379> del k1
(integer) 1

127.0.0.1:6379> keys *
(empty array)

#根据value选择非阻塞删除仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作。
127.0.0.1:6379> UNLINK ke
(integer) 1   

6.修改数据

127.0.0.1:6379> set k1  v1
OK
127.0.0.1:6379> set k1 v11111
OK
127.0.0.1:6379> get k1
"v11111"

7.追加数据

127.0.0.1:6379> APPEND k1 ggg
(integer) 9
127.0.0.1:6379> get k1
"v11111ggg"

8.切换库

#1.切换库[1-15]
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> SELECT 2
OK
127.0.0.1:6379[2]> SELECT 3
OK
127.0.0.1:6379[3]> SELECT 16
(error) ERR DB index is out of range

#2.清空当前库
127.0.0.1:6379> FLUSHDB 
OK

#3.通杀全部库
127.0.0.1:6379> FLUSHALL
OK

二、密码的设置

1.配置文件配置密码

#1.修改配置文件
[root@db01 redis]# vim redis.conf 
requirepass DataXgroup@9H
 
#2.使用密码连接
[root@db01 redis]# redis-cli -a DataXgroup@9H

#3.登陆后输入密码
[root@db01 redis]# redis-cli
127.0.0.1:6379> AUTH DataXgroup@9H
OK
127.0.0.1:6379> DBSIZE
(integer) 2018041

#4.redis连接后获取密码
127.0.0.1:6379[3]> CONFIG GET requirepass
1) "requirepass"
2) "DataXgroup@9H"

2.redis连接后修改密码

#1.修改redis密码为DataXgroup@9G
127.0.0.1:6379[3]> CONFIG SET requirepass DataXgroup@9G
OK
127.0.0.1:6379[3]> exit

#2.测试登录
root@3f706a745e74:/data# redis-cli 
127.0.0.1:6379> DBSIZE
(error) NOAUTH Authentication required.

127.0.0.1:6379> AUTH DataXgroup@9H
(error) WRONGPASS invalid username-password pair or user is disabled.

127.0.0.1:6379> auth DataXgroup@9G
OK

三、Redis键(key)

1.查看当前库所有key

#1.查看当前库所有key
127.0.0.1:6379> keys *
1) "k1"
2) "ke"

#2.匹配键k1
127.0.0.1:6379> keys *k1
1) "k1"

2.判断key是否存在

#存在则返回1
127.0.0.1:6379> EXISTS k1
(integer) 1
127.0.0.1:6379> EXISTS ke
(integer) 1

#不存在则返回0
127.0.0.1:6379> EXISTS k2
(integer) 0

3.修改key的名字

127.0.0.1:6379> RENAME ke k100
OK

127.0.0.1:6379> keys *
1) "k1"
2) "k100"

4.查看数据类型

127.0.0.1:6379> TYPE k100
string

5.设置生存时间

#以秒为单位
127.0.0.1:6379> set jh gg
OK
127.0.0.1:6379> EXPIRE jh 10
(integer) 1

#以毫秒为单位
127.0.0.1:6379> pexpire k100 10000
(integer) 1

6.查看生存时间

#正整数生存时间倒计时
127.0.0.1:6379> TTL k100
(integer) 1					

#-1代表没有设置生存时间
127.0.0.1:6379> TTL k100
(integer) -1				

#代表设置过生存时间已删除,已过期
127.0.0.1:6379> TTL k100
(integer) -2			

#1.以秒为单位
ttl key
127.0.0.1:6379> ttl mysets
-1	# -1代表永不过期

#2.以毫秒为单位
Pttl key
127.0.0.1:6379> PTTL a
-1	# -1代表永不过期

7.取消生存时间

127.0.0.1:6379> set jh nnng
OK

127.0.0.1:6379> EXPIRE jh 200
(integer) 1

127.0.0.1:6379> ttl jh
(integer) 196

127.0.0.1:6379> PERSIST jh
(integer) 1

127.0.0.1:6379> ttl jh
(integer) -1

8. 随机返回一个key

127.0.0.1:6379> RANDOMKEY
myset_sdiff
127.0.0.1:6379> RANDOMKEY
new_myset
127.0.0.1:6379> RANDOMKEY
myset

四、Redis数据结构之字符串(String)

String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。

String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。

String类型可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。

1.命令行模式

进入命令行模式:
#a 输入密码
redis-cli -a password_value
 
#raw 避免中文显示乱码
redis-cli -a password_value --raw

2. 查看命令帮助

127.0.0.1:6379> help @string

  APPEND key value
  summary: Append a value to a key
  since: 2.0.0

  BITCOUNT key [start end]
  summary: Count set bits in a string
  since: 2.6.0

  BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]

3.Exists

#查看key是否存在,返回true / false(0/1)

127.0.0.1:6379> EXISTS a
1
127.0.0.1:6379> EXISTS b
0

4.TTL

#查看数据过期时间

127.0.0.1:6379> ttl a
-1 		# 永不过期

127.0.0.1:6379> ttl b
(integer) 2 	# 过期时间还有2个单位

5.增加数据

增加数据的命令有以下几个:set,setex,psetex,setnx。
#1.常用命令
set key value [EX seconds|PX milliseconds|EXAT timestamp|PXAT milliseconds-timestamp|KEEPTTL] [NX|XX] [GET]

1.*NX:当数据库中key不存在时,可以将key-value添加数据库
2.*XX:当数据库中key存在时,可以将key-value添加数据库,与NX参数互斥
3.*EX:key的超时秒数
4.*PX:key的超时毫秒数,与EX互斥

#2.使用set命令新增一个键值对及其EX,PX用法
--1.设置
127.0.0.1:6379> set name jh
OK
127.0.0.1:6379> get name 
"jh"

--2.设置过期时间为秒
127.0.0.1:6379> set name jh ex 10 
OK
127.0.0.1:6379> ttl name
(integer) 6
127.0.0.1:6379> ttl name
(integer) -2

--3.set 和 ex合并成setex
127.0.0.1:6379> SETEX name 10 jh
OK
127.0.0.1:6379> ttl name 
(integer) 6
127.0.0.1:6379> ttl name 
(integer) -2

--4.设置过期时间为毫秒
127.0.0.1:6379> set name jh px 10
OK
127.0.0.1:6379> ttl name
(integer) -2

--5.set 和 px 合并使用
127.0.0.1:6379> PSETEX name 100 jh
OK
127.0.0.1:6379> ttl name
(integer) -2

#3.使用set命令新增一个键值对及其NX,XX用法
Nx:当key不存在时创建,已存在则忽略。
Xx:当key存在则更新,不存在则忽略。

# nx
127.0.0.1:6379> set name jh nx
OK
127.0.0.1:6379> get name
"jh"
127.0.0.1:6379> set name jindada
OK
127.0.0.1:6379> get name
"jindada"
127.0.0.1:6379> set name jh nx
(nil)
127.0.0.1:6379> get name
"jindada"

# set 与 nx 合并使用
127.0.0.1:6379> SETNX name jh
(integer) 0
127.0.0.1:6379> get name
"jindada"

#4.批量增加
同时定义多个键值对,mset也可以和nx一起用。
mset key1 value1 [key2 value2...] #批量创建kv,已存在的会被更新
msetnx key1 value1 [key2 value2...] #批量创建kv,所有key不存在才会创建

127.0.0.1:6379> mset k1 a k2 b k3 c
OK
127.0.0.1:6379> MGET k1 k2 k3
1) "a"
2) "b"
3) "c"

6. 删除数据

DDL	#删除数据

使用del命令删除数据,可批量可单个删除。

#格式
del key1 [key2 key3...]

#案例
127.0.0.1:6379> del k1 k2 k3
(integer) 3
127.0.0.1:6379> del name_xx
(integer) 1
127.0.0.1:6379> mget k1 k2 k3 name_xx name_nx
1) (nil)
2) (nil)
3) (nil)
4) (nil)
5) "wyk"

7.更改数据

#1.使用set命令修改数据
127.0.0.1:6379> get name
"jindada"
127.0.0.1:6379> set name jh
OK
127.0.0.1:6379> get name
"jh"

#2.使用getset命令修改数据
等同于get+set,执行此命令返回get的结果,然后将该key更新为新的value,下次再get的时候查到的就是新的value。
127.0.0.1:6379> GETSET name jindada
"jh"
127.0.0.1:6379> get name
"jindada"

#3.使用setrange按下标更新数据
对指定下标的字符串进行更新,下标从0开始算起。
127.0.0.1:6379> SETRANGE name 5 eidei
(integer) 10
127.0.0.1:6379> get name
"jindaeidei"

8.查找数据

#1.查看所有的key
查看所有的Key,在数据量大的Redis中慎用,容易卡死。
127.0.0.1:6379> KEYS *
1) "k1"
2) "name"
3) "k2"
4) "k3"

#2.根据key获取数据
使用get获得指定key的value。
127.0.0.1:6379> get name
"jindaeidei"

#3.批量获得指定keys的values
mget key1 [key2 key3...]
127.0.0.1:6379> mget k1 k2 k3
1) "a"
2) "b"
3) "c"

#4.从字符串的指定开始结束下标截取字符串
getrange key start end
127.0.0.1:6379> GETRANGE name 0 4
"jinda"

9.计数

#1.指定key递增
对指定的key的value加1,递增步长默认1,如果key不存在,其初始值为0,在incr之后其值为1,如果value的值不能转为整型,如hello,该操作将执行失败并返回相应的错误信息。
127.0.0.1:6379> INCR num
1
127.0.0.1:6379> INCR num
2

#2.指定key递减
对指定的key的value减1,递减步长默认1,如果key不存在,其初始值为0,在decr之后其值为-1,如果value的值不能转为整型,如hello,该操作将执行失败并返回相应的错误信息。

127.0.0.1:6379> DECR num
1
127.0.0.1:6379> DECR num
0

#3.递增指定长度
对指定的key的value加指定的步长,递增步长可指定,如果key不存在,其初始值为0,在incrby之后其值为步长,如果value的值不能转为整型,如hello,该操作将执行失败并返回相应的错误信息。

$步长increment为整数,当为负数时效果等于递减
incrby key increment

#4.递减指定长度
对指定的key的value减指定的步长,递减步长可指定,如果key不存在,其初始值为0,在decrby之后其值为步长的负数,如果value的值不能转为整型,如hello,该操作将执行失败并返回相应的错误信息。

$步长increment为整数,当为负数时效果等于递増

decrby key increment

redis> SET count 100
OK

redis> DECRBY count 20
(integer) 80

10.追加

#在字符串最后面追加子串
append key value

# 对不存在的 key 执行 APPEND
## 确保 myphone 不存在
redis> EXISTS myphone
(integer) 0

# 对不存在的 key 进行 APPEND ,等同于 SET myphone "nokia"
## 字符长度
redis> APPEND myphone "nokia" 
(integer) 5

# 对已存在的字符串进行 APPEND
## 长度从 5 个字符增加到 12 个字符
redis> APPEND myphone " - 1110" 
(integer) 12

redis> GET myphone
"nokia - 1110"

11.长度

#返回字符的长度

strlen key

12.数据结构

String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配.

如图中所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M。

五、Redis数据结构之列表(List)

单键多值
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。

1.查看命令帮助

help @list

  BLPOP key [key ...] timeout
  summary: Remove and get the first element in a list, or block until one is available
  since: 2.0.0

  BRPOP key [key ...] timeout
  summary: Remove and get the last element in a list, or block until one is available
  since: 2.0.0

2.从左边插入元素

从左边插入元素,从左边依次追加进栈,先进后出,后进先出
LPUSH key element [element ...]
 
127.0.0.1:6379> LPUSH mylist jh 18
2
127.0.0.1:6379> LPUSH mylist shanghai
3
127.0.0.1:6379> LRANGE mylist 0 -1
1) "shanghai"
3) "18"
4) "jh"

3.从右边插入元素

从右边插入元素,从右边依次追加进队列,先进先出,后进后出。
RPUSH key element [element ...]
 
127.0.0.1:6379> RPUSH mylist python
(integer) 4
127.0.0.1:6379> RPUSH mylist linux
(integer) 5
127.0.0.1:6379> LRANGE mylist 0 -1
1) "shanghai"
3) "18"
4) "jh"
5) "python"
6) "linux"

4. list存在时才会从左边依次追加元素

#与sting类型中的nx类似,只有当list存在时才会从左边依次追加元素。
LPUSHX key element [element ...]

127.0.0.1:6379> LPUSHX mylist Shanghai7
(integer) 6

127.0.0.1:6379> LRANGE mylist 0 -1
1) "Shanghai7"
2) "shanghai"
3) "18"
4) "jh"
5) "python"
6) "linux"

127.0.0.1:6379> LPUSHX testlist Shanghai0
(integer) 0

127.0.0.1:6379> LRANGE testlist
(error) ERR wrong number of arguments for 'lrange' command

5.当list存在时才会从右边依次追加元素

#与lpushx类似,只有当list存在时才会从右边依次追加元素。
RPUSHX key element [element ...]

127.0.0.1:6379> RPUSH testlist jh
(integer) 1

127.0.0.1:6379> LRANGE testlist 0 -1
1) "jh"

127.0.0.1:6379> RPUSHX testlist 18
(integer) 2

127.0.0.1:6379> LRANGE testlist 0 -1
1) "jh"
2) "18"

6. 从list中指定的元素的前/后 插入一个新元素

LINSERT key BEFORE|AFTER pivot element

127.0.0.1:6379> LRANGE testlist 0 -1
1) "jh"
2) "18"

# 在某个KEY之前插入某个值
127.0.0.1:6379> LINSERT mylist before 18 179

127.0.0.1:6379> LRANGE mylist 0 -1
Shanghai上海1718alvin上海市linuxpythonQingPu

# 在某个值后面添加一个值
127.0.0.1:6379> LINSERT mylist after 18 1610
127.0.0.1:6379> LRANGE mylist 0 -1
Shanghai上海171816alvin上海市linuxpythonQingPu
127.0.0.1:6379>

7.删除数据

#1.从列表左侧开始移除LREM key count element
127.0.0.1:6379> 127.0.0.1:6379> LINSERT mylist after 18 1614
(integer) 8
127.0.0.1:6379> LINSERT mylist after 18 1615
(integer) 9
127.0.0.1:6379> LINSERT mylist after 18 1616
(integer) 10
127.0.0.1:6379> LRANGE mylist 0 -1
 1) "Shanghai7"
 2) "shanghai"
 4) "18"
 5) "1616"
 6) "1615"
 7) "1614"
 8) "jh"
 9) "python"
10) "linux

127.0.0.1:6379> LREM mylist 2 1614
127.0.0.1:6379> LRANGE mylist 0 -1
1) "Shanghai7"
2) "shanghai"
3) "18"
4) "1616"
5) "1615"
6) "jh"
7) "python"
8) "linux"

8. 修改数据

#1.修改元素内容
LSET key index element

127.0.0.1:6379> LRANGE mylist 0 -1
1) "Shanghai7"
2) "shanghai"
3) "18"
4) "1616"
5) "1615"
6) "jh"
7) "python"
8) "linux"

127.0.0.1:6379> LSET mylist 2 19
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "Shanghai7"
2) "shanghai"
3) "19"
4) "1616"
5) "1615"
6) "jh"
7) "python"
8) "linux"

#2.截取为从下标start到下标stop闭区间的列表
将原列表截取为从下标start到下标stop闭区间的列表,即原列表变为一个第start+1个到第stop+1个元素的列表。
LTRIM key start stop

127.0.0.1:6379> LTRIM mylist 2 8
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "19"
2) "1616"
3) "1615"
4) "jh"
5) "python"
6) "linux"

9. 查询数据

#1.查看列表中元素的个数
LLEN key

127.0.0.1:6379> LLEN mylist
(integer) 6

#2.根据起止下标查询列表元素
LRANGE key start stop
 
127.0.0.1:6379> lrange mylist 0 -1 #表示查看全部元素
1) "19"
2) "1616"
3) "1615"
4) "jh"
5) "python"
6) "linux"

127.0.0.1:6379> lrange mylist -1 -1  #表示查看最右边的元素
1) "linux"

#3.根据指定的index下标查看列表中的元素
LINDEX key index

127.0.0.1:6379> LINDEX mylist 3
"jh"

#4.从左边消费列表中的元素
从左边消费列表中的元素,消费完之后从列表中删除。
LPOP key

127.0.0.1:6379> LRANGE mylist 0 -1
1) "19"
2) "1616"
3) "1615"
4) "jh"
5) "python"
6) "linux"
127.0.0.1:6379> LPOP mylist
"19"
127.0.0.1:6379> LRANGE mylist 0 -1
1) "1616"
2) "1615"
3) "jh"
4) "python"
5) "linux"

#5.从右边消费列表中的元素
从右边消费列表中的元素,消费完之后从列表中删除。
RPOP key

127.0.0.1:6379> LRANGE mylist 0 -1
1) "1616"
2) "1615"
3) "jh"
4) "python"
5) "linux"
127.0.0.1:6379> RPOP mylist
"linux"
127.0.0.1:6379> LRANGE mylist 0 -1
1) "1616"
2) "1615"
3) "jh"
4) "python"

#6.消费列表最右边的元素并返回
消费列表A的最右边的元素返回,然后追加到列表B的最左边。
RPOPLPUSH source destination
 
RPOPLPUSH List_A List_B

127.0.0.1:6379> LRANGE mylist 0 -1
1) "1616"
2) "1615"
3) "jh"
4) "python"
127.0.0.1:6379> RPOPLPUSH mylist mylist
"python"
127.0.0.1:6379> LRANGE mylist 0 -1
1) "python"
2) "1616"
3) "1615"
4) "jh"

#7.从列表中左侧查询元素
从列表中左侧查询元素,返回列表的key和左侧第一个元素,若所有查询的列表中都没有元素,则会阻塞等待至设置的timeout秒之后返回空,若在这期间,这些列表新增了元素,则会立刻消费并返回该元素。
BLPOP key [key ...] timeout

127.0.0.1:6379> BLPOP mylist 10
1) "mylist"
2) "1616"
127.0.0.1:6379> BLPOP mylist 10
1) "mylist"
2) "1615"
127.0.0.1:6379> BLPOP mylist 10
1) "mylist"
2) "jh"

10.数据结构

List的数据结构为快速链表quickList。

首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成quicklist。因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。

Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。

六、Redis数据结构之无序集合(Set)

Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。

Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变。集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。

1. 查看帮助命令

#使用Redis命令手册查看
127.0.0.1:6379> help @set

2.增加数据

#1.给集合内新增成员,若集合不存在则创建集合并新增成员。
SADD key member [member ...]

#2.示例
127.0.0.1:6379> SADD myset 1
(integer) 1
127.0.0.1:6379> SADD myset 2
(integer) 1
127.0.0.1:6379> SADD myset 3
(integer) 1
127.0.0.1:6379> SADD myset 4
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "1"
2) "2"
3) "3"
4) "4"

3. 删除数据

#1.从集合中删除指定的成员,返回删除的个数。
SREM key member [member ...]

#2.示例
127.0.0.1:6379> SREM myset 2 4
(integer) 2
127.0.0.1:6379> SMEMBERS myset
1) "1"
2) "3"

4. 修改数据

#1.移动数据
SMOVE source destination member

#2.示例
127.0.0.1:6379> SADD myset1 1 2 3 4 5
(integer) 5

127.0.0.1:6379> SADD myset2 one two three
(integer) 3

127.0.0.1:6379> SMOVE myset1 myset2 4
(integer) 1

127.0.0.1:6379> SMEMBERS myset1
1) "1"
2) "2"
3) "3"
4) "5"

127.0.0.1:6379> SMEMBERS myset2
1) "three"
2) "two"
3) "4"
4) "one"

5.查看数据

#1.查看集合中所有的成员
SMEMBERS key

127.0.0.1:6379> SMEMBERS myset1
1) "1"
2) "2"
3) "3"
4) "5"

#2.返回集合中成员的个数
SCARD key

127.0.0.1:6379> SCARD myset1
(integer) 4

#3.从集合中随机返回指定个数的成员

SRANDMEMBER key [count]

127.0.0.1:6379> SRANDMEMBER myset1 2
1) "3"
2) "2"

127.0.0.1:6379> SRANDMEMBER myset1 2
1) "5"
2) "3"


#4.判断对象是否是集合中的成员,1表示true,0表示false
SISMEMBER key member

127.0.0.1:6379> SISMEMBER myset1 6
(integer) 0
127.0.0.1:6379> SISMEMBER myset1 2
(integer) 1

#5.随机返回一个成员,从集合中随机弹出一个成员,返回该成员并从集合中删除该成员。

SPOP key

127.0.0.1:6379> SPOP myset2
"one"
127.0.0.1:6379> SPOP myset2
"two"
127.0.0.1:6379> SPOP myset2
"three"
127.0.0.1:6379> SMEMBERS myset2
1) "4"

6.交集

#1.取多个集合的交集,返回这些集合中共同拥有的成员。

SINTER key [key ...]

127.0.0.1:6379> sadd myseta 1 2 3 4 5
(integer) 5
127.0.0.1:6379> sadd mysetb 4 5 6 7 8
(integer) 5
127.0.0.1:6379> sadd mysetc 5 6 7 8 9
(integer) 5
127.0.0.1:6379> SINTER myseta mysetb mysetc
1) "5"

#2.将多个集合的交集的结果保存为一个新的集合destination,返回新集合的成员个数。
SINTERSTORE destination key [key ...]

127.0.0.1:6379> SINTERSTORE mysetc mysetb myseta
(integer) 2

127.0.0.1:6379> SMEMBERS mysetc
1) "4"
2) "5"

7. 并集

#1.取多个集合的并集,相同的成员会被去重。
SUNION key [key ...]

127.0.0.1:6379> SMEMBERS mysetb
1) "4"
2) "5"
3) "6"
4) "7"
5) "8"
127.0.0.1:6379> SMEMBERS myseta
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> SUNION myseta mysetb
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"

#2.将多个集合的并集的结果保存为一个新的集合
SUNIONSTORE destination key [key ...]

127.0.0.1:6379> SUNIONSTORE mysetc myseta mysetb
(integer) 8
127.0.0.1:6379> SMEMBERS mysetc
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"

8. 差集

#1.取多个集合的差集,以最左边的为主集合,返回左集合中有而其他集合没有的成员。
127.0.0.1:6379> SDIFF myseta mysetb
1) "1"
2) "2"
3) "3"

9. 将多个集合的差集的结果保存为一个新的集合

#将多个集合的差集的结果保存为一个新的集合 ,返回新集合的成员个数 。
SDIFFSTORE destination key [key ...]

127.0.0.1:6379> SDIFFSTORE mysetc myseta mysetb
(integer) 3
127.0.0.1:6379> SMEMBERS mysetc
1) "1"
2) "2"
3) "3"

10.数据结构

Set数据结构是dict字典,字典是用哈希表实现的。

Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。

七、Redis数据结构之哈希(Hash)

Redis hash 是一个键值对集合。

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。类似于python中的dict和java中的map集合。用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储

主要有以下2种存储方式:

每次修改用户的某个属性需要,先反序列化改好后再序列化回去。开销较大。

用户ID数据冗余

通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题

1.查看命令帮助

not connected> help @hash

  HDEL key field [field ...]
  summary: Delete one or more hash fields
  since: 2.0.0

  HEXISTS key field
  summary: Determine if a hash field exists
  since: 2.0.0

2. 设置hash类型的单个field或一次性设置多个field

HSET key field value [field value ...]
 
127.0.0.1:6379> hset csdn id 1 name jh company 上海
(integer) 3

3.查看hash类型的key中指定的field是否存在

hexists key field
127.0.0.1:6379> HEXISTS csdn name
1

127.0.0.1:6379> HEXISTS csdn company
1

4.只有value中不存在的field才会被创建

HSETNX key field value

5. 删除数据

#使用hdel命令删除hash类型的value中的fields,可批量可单个删除
HDEL key field1 [field2 ...]

127.0.0.1:6379> HDEL csdn name
1
127.0.0.1:6379> HEXISTS csdn name
0

6.更改数据

#对hash类型中相同的field进行set操作会更新该field的值
HSET key field value [field value ...]

127.0.0.1:6379> HSET csdn name jindada
0

7.查找数据

#1.使用hget获取指定hash类型的field对应的值
HGET key field

127.0.0.1:6379> Hget csdn name
jindada

#2.使用hmget获取指定hash类型的多个field对应的值
HMGET key field [field ...]

127.0.0.1:6379> hmget csdn  name company
jindada
上海

#3.使用hgetall获得指定hash类型对象的全部field和对应的value值
HGETALL key

127.0.0.1:6379> HGETALL csdn
id
1
company
上海
name
jindada

#4.使用hkeys获得指定hash类型对象的全部field
HKEYS key

127.0.0.1:6379> HKEYS csdn
id
company
name

#5.使用hvals获得指定hash类型对象的全部field的values值
HVALS key

127.0.0.1:6379> HVALS csdn
1
上海
jindada

8. 计数

#1.hincrby可以对hash对象的指定的field的value做递增操作
与string类型中的incrby类似,hincrby可以对hash对象的指定的field的value做递增操作,increment必须是整数(hash类型中没有hdecrby方法,当increment为负数时为递减操作),value必须是integer类型,否则会报对应的错误。
HINCRBY key field increment

ps:filed对应的value必须是integer类型,increment必须是整数,可以为负

127.0.0.1:6379> HINCRBY csdn  id 10
11

#2.hincrbyfloat可以对hash对象的指定的field的value做递增操作
与string类型中的incrbyfloat类似,hincrbyfloat可以对hash对象的指定的field的value做递增操作,increment可以是整数或浮点数(hash类型中没有hdecrbyfloat方法,当increment为负数时为递减操作),value必须是数字类型,否则会报对应的错误。
HINCRBYFLOAT key field increment

ps:filed对应的value必须是数字类型,increment可以是整数或浮点,可以为负

127.0.0.1:6379> HINCRBYFLOAT csdn id 10.9
21.9

9. 长度

#1.hlen返回hash类型中field的数量
HLEN key

127.0.0.1:6379> HLEN csdn
3

#2.hstrlen返回hash类型中指定filed对应的value的字符长度
HSTRLEN key field

127.0.0.1:6379> HSTRLEN csdn id
4

127.0.0.1:6379> HSTRLEN csdn company
6

10.过期

#我们可以看到hash类型没有hsetex hpsetex一类的方法,想对hash对象做过期策略可以使用全局函数expire。
expire key seconds

127.0.0.1:6379> EXPIRE csdn 1000
1
127.0.0.1:6379> ttl csdn
993

11.数据结构

Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。

八、Redis数据结构之有序集合Zset(sorted set)

Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。

因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。

1.查看命令帮助

127.0.0.1:6379> help @sorted_set

2.增加数据

#1.往有序集合中新增成员,需要指定该成员的分数,分数可以是整形或浮点型,当分数相同时候,索引下标按照字典排序。
ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]

#2.示例
127.0.0.1:6379>  ZADD myzset 1 jh
1
127.0.0.1:6379> ZADD myzset 2 shanghai 3 beijing
2
127.0.0.1:6379> ZRANGE myzset 0 -1
jh
shanghai
beijing

3.查询数据

#1.获取有序集合的成员数
ZCARD key

127.0.0.1:6379> ZCARD myzset
3

#2.获取指定分数区间内的成员数
从有序集合内获取指定分数区间内的成员数。
ZCOUNT key min max

127.0.0.1:6379> ZRANGE myzset 0 -1
jh
shanghai
beijing

127.0.0.1:6379> ZCOUNT myzset 1 2
2

#3.字典排序
根据字典排序返回min ,max之间的数据量.
ZLEXCOUNT key min max

127.0.0.1:6379> ZRANGE myzset 0 -1
jh
shanghai
beijing

127.0.0.1:6379> ZLEXCOUNT myzset [sh [zzz
0

#4.获取成员的分数值
返回有序集中,成员的分数值,不存在的成员返回空。

ZSCORE key member

127.0.0.1:6379> ZRANGE myzset 0 -1
jh
shanghai
beijing
127.0.0.1:6379> ZSCORE myzset shanghai
2

#5.迭代有序集合中的元素(包括元素成员和元素分值)
127.0.0.1:6379> ZSCAN myzset 0 match "sh*"
0
shanghai
2
127.0.0.1:6379> ZSCAN myzset 0 match "j*"
0
jh
1

5.删除数据

#1.移除指定的成员
ZREM key member [member ...]

127.0.0.1:6379> ZRANGE myzset 0 -1
jh
shanghai
beijing
127.0.0.1:6379> ZREM myzset shanghai
1
127.0.0.1:6379> ZRANGE myzset 0 -1
jh
beijing

6.数据结构

SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。

zset底层使用了两个数据结构:
1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。

7.跳跃表(跳表)

1、简介
	有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等。对于有序集合的底层实现,可以用数组、平衡树、链表等。数组不便元素的插入、删除;平衡树或红黑树虽然效率高但结构复杂;链表查询需要遍历所有效率低。Redis采用的是跳跃表。跳跃表效率堪比红黑树,实现远比红黑树简单。

2、实例
	对比有序链表和跳跃表,从链表中查询出51
(1)	有序链表
要查找值为51的元素,需要从第一个元素开始依次查找、比较才能找到。共需要6次比较。

(2)	跳跃表
从第2层开始,1节点比51节点小,向后比较。
21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层
在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下
在第0层,51节点为要查找的节点,节点被找到,共查找4次。

从此可以看出跳跃表比有序链表效率要高

posted @ 2022-09-19 10:22  高压锅炖主播  阅读(66)  评论(0编辑  收藏  举报