02_API的理解和使用

一、预备

Redis提供了5种数据结构,理解每种数据结构的特点对于Redis开发运维非常重要,同时掌握Redis的单线程命令处理机制,会使数据结构和命令的选择事半功倍。
预备知识:几个简单的全局命令、数据结构和内部编码,单线程命令处理机制分析。
5种数据结构的特点、命令使用、应用场景。
键管理、遍历键、数据库管理。
在正式介绍5种数据结构之前,了解一下redis的一些全局命令,数据结构和内部编码,单线程命令处理机制是十分有必要的,他们能为后面内容学习打下一个好的基础,主要体现在两个方面:第一、redis的命令有上百个,如果纯靠死记硬背比较困难,但是如果理解redis的一些机制,会发现这些命令很强的通用性。第二、redis不是万能的,有些数据结构和命令必须在特定场景下使用,一旦使用不当可能对redis本身或应用本身造成致命伤害。

1 全局命令

redis有5种数据结构,它们是键值对中的值,对于键来说有一些通用命令。
1、keys * 查看所有的键

192.168.1.71:6379> keys *
(empty list or set)
192.168.1.71:6379> set a b
OK
192.168.1.71:6379> get a
"b"
192.168.1.71:6379> keys *
1) "a"

2、通过dbsize统计键的总和

192.168.1.71:6379> rpush mylist a s d f g h
(integer) 6
192.168.1.71:6379> dbsize 
(integer) 2

3、检查a键是否存在,存在返回1,不存在返回0

192.168.1.71:6379> exists a
(integer) 1

4、删除aa键,什么数据类型都可以删除,1=成功,0=没有

192.168.1.71:6379> set aa 666
OK
192.168.1.71:6379> get aa
"666"
192.168.1.71:6379> del aa 
(integer) 1

//删除多个在del后面跟多个键即可

5、键过期
指定键的过期时间,当超过过期时间后,会自动删除键
使用ttl可以查看键的过期剩余时间
-1:键没有设置过期时间
-2:键不存在

192.168.1.71:6379> set hello world
OK
192.168.1.71:6379> expire hello 10
(integer) 1
192.168.1.71:6379> get hello
"world"
192.168.1.71:6379> ttl hello
(integer) -2

6、查看字符串类型

192.168.1.71:6379> type a
string
192.168.1.71:6379> type mylist
list

2 数据结构和内部编码

type命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合),但这些只是redis对外的数据结构。
实际上每种数据结构都会有自己底层的内部编码实现,而且是多种实现,这样redis会在合适的场景选择合适的内部编码。
可以通过object encoding命令查询内部编码:

  • string(字符串)3种 raw int embstr
  • hash(哈希)2种 hashtable ziplist
  • list(列表)2种 linkedlist ziplist
  • set(集合)2种 hashtable intest
  • zset(有序集合)2 种 skiplist ziplist

object encoding hello查看hello的编码

192.168.1.71:6379> object encoding hello
"embstr"

内部编码好处
1、可以改进内部编码,而对外的数据结构和命令没有影响,这样在开发出更优秀的内部编码,不需要改变外部数据结构和命令

2、多种内部编码实现不同场景下发挥各自的优势

3 单线程架构

redis使用了单线程架构I\O多录复用模型来实现高性能的内存数据库服务。
为什么单线程还能这么快?
1、redis将所有的数据存放在内存中,内存的相应时长大约为100纳秒,这是redis达到每秒万级别访问的重要基础。
2、非阻塞型I\O线程,redis使用epoll作为I\O多路复用技术的实现,再加上redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I\O上浪费过多的时间。
3、单线程避免了因线程切换和竟态产生的消耗。

单线程的好处
1、单线程可以简化数据结构和算法的实现
2、单线程避免了线程切换和竟态产生的消耗。

单线程的问题
当某一条命令执行时间过长,其他命令等待时间过长,造成阻塞。

二、数据类型

1. 字符串

字符串类型是redis最基础的数据结构。首先键都是字符串类型,而且其他几种数据结构都是在字符串类型基础上构建的,所以字符串类型能为其他四种结构的学习奠定基础。字符串的值实际可以是字符串(简单、复杂的字符串),也可以数字、甚至是二进制(图片、音频、视频),但是最大值不能超多512M

命令格式:

 set key value [expiration EX seconds|PX milliseconds] [NX|XX]
  • [EX seconds]设置秒级别的过期时间
  • [PX milliseconds]设置毫秒级别的过期时间
  • [NX]键必须不存在,才可以设置成功,用于添加
  • [XX]键必须存在,才可以设置成功,用于更新

//检测键是否存在

127.0.0.1:6379> exists hello
(integer) 0
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> setnx hello world
(integer) 0
127.0.0.1:6379> set hello redis xx
OK
127.0.0.1:6379> get hello
"redis"

获取值

语法:get key
127.0.0.1:6379> get hello

批量设置值

语法:mset key1 value1 key2 value2 ...
mget key1  key2 ...
127.0.0.1:6379> mset a 1 b 2 c 3
OK
127.0.0.1:6379> mget a b c
1) "1"
2) "2"
3) "3"

计数

语法:incr key

incr只能对数字自增
值不是整数的时候,返回错误
值=整数,返回自增后的结果
键不存在,按0开始自增

127.0.0.1:6379> get a
"1"
127.0.0.1:6379> incr a 
(integer) 2
127.0.0.1:6379> incr a 
(integer) 3
127.0.0.1:6379> incr a 
(integer) 4
127.0.0.1:6379> get a
"4"

decr key(自减)

127.0.0.1:6379> get a
"4"
127.0.0.1:6379> decr a
(integer) 3
127.0.0.1:6379> decr a
(integer) 2
127.0.0.1:6379> get a
"2"

lncrby key 数字(自增指定数字)

127.0.0.1:6379> get a
"2"
127.0.0.1:6379> incrby a 4
(integer) 6
127.0.0.1:6379> incrby a 4
(integer) 10
127.0.0.1:6379> get a
"10"

decrby key 数字(自减指定数字)

127.0.0.1:6379> get a
"10"
127.0.0.1:6379> decrby a 2
(integer) 8
127.0.0.1:6379> decrby a 2
(integer) 6
127.0.0.1:6379> get a
"6"

lncrbyfloat key 带小数点数字(自增浮点数)

127.0.0.1:6379> get a
"6"
127.0.0.1:6379> incrbyfloat a 0.5
"6.5"
127.0.0.1:6379> get a
"6.5"

//使用负号(-)可以实现自减浮点数

127.0.0.1:6379> get a
"6.5"
127.0.0.1:6379> incrbyfloat a -0.5
"6"

append key value 追加值:相当于把追加的值合并在原值后面
设置key的值redis
追加key的值world
结果是合并

127.0.0.1:6379> set key redis
OK
127.0.0.1:6379> append key world
(integer) 10
127.0.0.1:6379> get key
"redisworld"

strlen key 字符串长度

127.0.0.1:6379> get key
"redisworld"
127.0.0.1:6379> strlen key
(integer) 10

//每个中文占3个字节

127.0.0.1:6379> set hello "时间"
OK
127.0.0.1:6379> strlen hello
(integer) 6

getset key value 设置并返回原值

127.0.0.1:6379> set hello 10
OK
127.0.0.1:6379> getset hello 20
"10"
127.0.0.1:6379> getset hello 30
"20"
127.0.0.1:6379> get hello
"30"

setrange key 数字位置value (设置指定位置的字符)
pest第一个字母是0,把位置0的p改成b,结果是best

127.0.0.1:6379> set redis pest
OK
127.0.0.1:6379> setrange redis 0 b
(integer) 4
127.0.0.1:6379> get redis
"best"

getrange key 起始数字(0开始)结束数字
获取部分字符串(0开始)

127.0.0.1:6379> get redis
"best"
127.0.0.1:6379> getrange  redis 0 1
"be"

字符串的内部编码
根据我们输入键对应的值,redis自行选择编码
lnt 8字节的长度整形
embstr小于等于39个字节的字符串
raw 大于39个字节的字符串

127.0.0.1:6379> set key 88888888
OK
127.0.0.1:6379> object encoding key
"int"

扩展:
开发代码:
在这里插入图片描述
使用场景

  • 缓存功能
  • 计数
  • 共享session
  • 限速

2. 哈希

几乎所有的编程语言都提供哈希(hash)类型,它们的叫法可能是哈希、字典、关联数组。在redis中,哈希类型是指键值本身又是一种键值结构,列如value=field1,value1…fieldN,valueN。哈希类型中的映射关系叫做field-value,这里的value是指field对应的值,不是键对应的值。

1、命令
hset key field value
成功1,不成功0

127.0.0.1:6379> hset user:1 name tom
(integer) 1

2、获取值hget key field

127.0.0.1:6379> hget user:1 name
"tom"
127.0.0.1:6379> hget user:2 name
(nil)
127.0.0.1:6379> hget user:1 age
(nil)

3、删除field

127.0.0.1:6379> hdel user:1 name
(integer) 1
127.0.0.1:6379> hdel user:1 name
(integer) 0

4、计数field个数
hlen key

127.0.0.1:6379> hset user:1 name tom
(integer) 1
127.0.0.1:6379> hset user:1 age 23
(integer) 1
127.0.0.1:6379> hset user:1 city tianjin
(integer) 1
127.0.0.1:6379> hlen user:1
(integer) 3

5、批量设置获取field value
hmset key field value [field…]
hmget key field [field value…]

127.0.0.1:6379> hmset user:2 name zhangsan age 18 city beijing
OK
127.0.0.1:6379> hmget user:2 name age city
1) "zhangsan"
2) "18"
3) "beijing"

6、判断是否存在
hexists key field

127.0.0.1:6379> hexists user:2 name
(integer) 1

7、获取所有的field
hkeys key

127.0.0.1:6379> hkeys user:2
1) "name"
2) "age"
3) "city"

8、获取所有的value
hvals key

127.0.0.1:6379> hvals user:2
1) "zhangsan"
2) "18"
3) "beijing"

9、获取所有的field的value的值(元素过多容易造成阻塞)
hgetall key

127.0.0.1:6379> hgetall user:2
1) "name"
2) "zhangsan"
3) "age"
4) "18"
5) "city"
6) "beijing"

10、hincrby key field 自增数字

127.0.0.1:6379> hincrby user:1 age 1
(integer) 24
127.0.0.1:6379> hincrby user:1 age 1
(integer) 25

11、 计算value的字符串长度3.2版本以上
hstrlen key field

127.0.0.1:6379> hstrlen user:1 name
(integer) 3

2、哈希的内部编码
ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)、同时所有的值都小于hash-max-ziplist-value配置(默认64字节)时,redis会使用ziplist作为哈希内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。
hashtable(哈希表):反之用这个

127.0.0.1:6379> hmset hashkey f1 v1 f2 v2
OK
127.0.0.1:6379> object encoding hashkey
"ziplist"

3、使用场景
用哈希表记录数据

3. list列表

列表(list)类型是用来存储多个有序的字符串、比如a、b、c、d、e五个元素从左到右组成了一个有序的列表,列表中的每一个字符串称为元素(element),一个列表最多可以存储2的32次方-1个元素。

redis中。可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表,获取指定索引下标的元素等。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。

列表类型结构有两个特点
1、列表中的元素是有序的,这就意味着可以通过索引下标获取某个元素或者某个范围内的元素列表。
2、列表中的元素可以是重复的。

这两个特点在后面介绍集合和有序集合,会显得更加突出。因此在考虑是否使用该数据结构前,首先需要弄清楚列表数据结构的特点。

1、命令操作

1.添加、查看操作
rpush key value [value…](右边添加)
lpush key value [value…](左边添加)

127.0.0.1:6379> rpush listkey c b a
(integer) 3

//可以重复添加重复值

127.0.0.1:6379> lpush listkey a c s k n l 
(integer) 9

查看list元素
//0表示从第一个开始查看 -1表示查看到最后一个

127.0.0.1:6379> lrange listkey 0 -1
1) "l"
2) "n"
3) "k"
4) "s"
5) "c"
6) "a"
7) "c"
8) "b"
9) "a"

//获取列表指定索引下标的元素

127.0.0.1:6379> lrange listkey 1 3 
1) "n"
2) "k"
3) "s"
127.0.0.1:6379> lindex listkey -1
"a"

向某个元素前或后插入元素
//向元素b前面添加java
linsert listkey before

127.0.0.1:6379> linsert listkey before b java
(integer) 10

127.0.0.1:6379> lrange listkey 0 -1
 1) "l"
 2) "n"
 3) "k"
 4) "s"
 5) "c"
 6) "a"
 7) "c"
 8) "java"
 9) "b"
10) "a"

//向某个元素后面添加c++
linsert listkey after

127.0.0.1:6379> linsert listkey after s c++
(integer) 11
127.0.0.1:6379> lrange listkey 0 -1
 1) "l"
 2) "n"
 3) "k"
 4) "s"
 5) "c++"
 6) "c"
 7) "a"
 8) "c"
 9) "java"
10) "b"
11) "a"

获取列表的长度
llen key

127.0.0.1:6379> llen listkey
(integer) 11

2.删除操作
删除lpop rpop ltrim

lpop key 从左删(上面)

127.0.0.1:6379> lpop listkey
"l"

rpop key 右侧删(下面)

127.0.0.1:6379> rpop listkey
"a"

删除指定元素
lrem key count(数字)value(值)
count>0从左往右删,删多少看count的数,如果几个位置内没有那个值,则不删
count<0从右往左删,删多少看count的数,如果几个位置内没有那个值,则不删
count=0删除所有

127.0.0.1:6379> lrem listkey 1 c 
(integer) 1

3.修改操作
lset kye index(数字,第几个,从0开始)newvalue

127.0.0.1:6379> lrange listkey 0 -1
1) "n"
2) "k"
3) "s"
4) "c++"
5) "c"
6) "java"
7) "b"
127.0.0.1:6379> lset listkey 1 bbb
OK
127.0.0.1:6379> lrange listkey 0 -1
1) "n"
2) "bbb"
3) "s"
4) "c++"
5) "c"
6) "java"
7) "b"

4.阻塞操作
blpop brpop 并发量太大,阻塞,缓解压力
blpop key [key…]timeout(阻塞多少秒)从左开始
brpop key [key…]timeout(阻塞多少秒)从右开始
timeout=0一直阻塞下去(不要用)

127.0.0.1:6379> brpop list:test 3
(nil)
(3.09s)

2、内部编码
列表类型的内部编码右两种。
ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512),同时列表中每个元素的值都小于list-max-ziplist-calue配置时(默认64字节),redis会选用ziplist来作为表的内部实现来减少内存的使用。

linkedlist(链表):当列表类型无法满足ziplist的条件时,redis会使用linkedlist作为列表的内部实现。

3、使用场景
1.消息队列
2.文章列表

4.集合

集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素并且集合中的元素是无序的,不能通过索引下标获取元素。一个集合最多可以存储2的32次方-1个元素,redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。

1、命令
1.添加sadd
插入重复值,出错

127.0.0.1:6379> exists myset
(integer) 0
127.0.0.1:6379> sadd myset a b c 
(integer) 3
127.0.0.1:6379> sadd myset a b 
(integer) 0

2.查看集合内所有元素smembers

127.0.0.1:6379> smembers myset
1) "b"
2) "a"
3) "c"

3.删除元素srem

127.0.0.1:6379> srem myset a
(integer) 1
127.0.0.1:6379> smembers myset
1) "b"
2) "c"

4.计数元素个数scard

127.0.0.1:6379> scard myset
(integer) 2

5.判断元素是否在集合中sismember

127.0.0.1:6379> sismember myset b
(integer) 1

6.随机从集合中返回指定个数的元素srandmember
//默认返回1 ,可指定随机返回数量

127.0.0.1:6379> srandmember myset 
"b"
127.0.0.1:6379> srandmember myset 2
1) "b"
2) "c"

7.从集合中随机删除元素spop
//默认随机删除1个元素,可指定删除多少元素

127.0.0.1:6379> spop myset
"b"
127.0.0.1:6379> spop myset 2
1) "e"
2) "y"

2、集合间操作

127.0.0.1:6379> sadd user:1 it music his sports
(integer) 4
127.0.0.1:6379> sadd user:2 it news eat sports
(integer) 4

1.交集sinter

127.0.0.1:6379> sinter user:1 user:2
1) "it"
2) "sports"

2.差集sdiff

127.0.0.1:6379> sdiff user:1 user:2
1) "music"
2) "his"

3.并集sunion

127.0.0.1:6379> sunion user:1 user:2
1) "his"
2) "music"
3) "it"
4) "eat"
5) "news"
6) "sports"

4.差集并集交集的结果保存
sinterstore destination
sunionstore destination
sdiffstore destination

如:将user:1和user:2的交集存在新的集合中(user:11)

127.0.0.1:6379> sinterstore user:11 user:1 user:2
(integer) 2
127.0.0.1:6379> smembers user:11
1) "it"
2) "sports"

3、集合的内部编码
集合类型的内部编码有两种:
intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intest-entries配置(默认512)时,redis会选用intset来1作为集合的内部实现,从而减少内存的使用。

hashtable(哈希表):当集合类型无法满足intset的条件时,redis会使用hashtable作为集合的内部实现

127.0.0.1:6379> sadd setkey 1 2 3 4
(integer) 4
127.0.0.1:6379> object encoding setkey
"intset"
127.0.0.1:6379> sadd setkey a
(integer) 1
127.0.0.1:6379> object encoding setkey
"hashtable"

4、使用场景
集合类型比较典型的使用场景时标签(tag)。比如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻、比较感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验以及增强用户黏度比较重要。例如一个电子商务的网站会对不同的标签的用户做不同类型的推荐,比如对数码产品比较感兴趣的人,在各个页面或者通过邮件的形式给他们推荐最新的数据产品,通常会为网站带来较多的利益。

5.有序集合

有序集合相对于哈希、列表、集合来说会有一点点陌生,但既然叫做有序集合,那么它和集合必然有着联系,它保留了集合不能重复成员的特性,但不同的是,有序集合中元素可以排序。但是它和列表使用索引下标作为排序依据不同的是,它给每一个元素设置一个分数(score)作为排序的依据。合理的利用有序集合,能帮助我们在实际开发中解决很多问题。

数据结构 是否允许重复元素 是否有序 有序实现方式 应用场景
列表 索引下标 时间轴、消息队列等
集合 标签、社交等
有序集合 分值 排行榜系统、社交等

1、命令
1.添加成员zadd

127.0.0.1:6379> zadd user:ranking 251 tom
(integer) 1
127.0.0.1:6379> zadd user:ranking 1 kris 91 mike 200 frank 220 time 250 marti
(integer) 5

2.计算成员个数zcard

127.0.0.1:6379> zcard user:ranking
(integer) 6

3.求某个成员的分数zscore

127.0.0.1:6379> zscore user:ranking tom
"251"

4.求成员排名
zrank低到高成员排名
zrevrank高到低成员排名

127.0.0.1:6379> zrank user:ranking tom
(integer) 5
127.0.0.1:6379> zrevrank user:ranking tom
(integer) 0

5.删除成员zrem

127.0.0.1:6379> zrem user:ranking mike
(integer) 1

6.增加某个成员分数zincrby

127.0.0.1:6379> zincrby user:ranking 9 time 
"229"
127.0.0.1:6379> zincrby user:ranking 9 tim
"9"

7.指定排名范围的成员
zrange低到高

127.0.0.1:6379> zrange user:ranking 0 2 withscores
1) "kris"
2) "1"
3) "tim"
4) "18"
5) "frank"
6) "200"

zrevrange高到低

127.0.0.1:6379> zrevrange user:ranking 0 2 withscores
1) "tom"
2) "351"
3) "marti"
4) "250"
5) "time"
6) "230"

8.求指定分数范围内成员
zrangebyscore低到高
//求200到221分数成员(包括200)

127.0.0.1:6379> zrangebyscore user:ranking 200 221 withscores

zrevrangebyscore高到低

127.0.0.1:6379> zrevrangebyscore user:ranking 221 200 withscores
1) "frank"
2) "200"

200以后的,通过设置-inf(无限小)和+inf(无限大)
//查找大于200的(不包括200)

127.0.0.1:6379> zrangebyscore user:ranking (200 +inf withscores
1) "time"
2) "230"
3) "marti"
4) "250"
5) "tom"
6) "351"

9.求指定分数范围查看成员的个数zcount

127.0.0.1:6379> zcount user:ranking 200 221
(integer) 1

10.删除指定排名内的升序元素zremrangebyrank

127.0.0.1:6379> zremrangebyrank user:ranking 0 2
(integer) 3

11.删除指定分数范围成员zremrangebyscore
//删除大于250的分数

127.0.0.1:6379> zremrangebyscore user:ranking (250 +inf
(integer) 0

2、有序集合间操作
先创建2个集合

127.0.0.1:6379> zadd user:t1 1 kris 91 mike 200 frank 220 tim 250 marrin
(integer) 5
127.0.0.1:6379> zadd user:t2 8 james 77 mike 652 martin 88 tom
(integer) 4

1.交集

语法:zinterstore destination numkeys key [key …] [WEIGHTS weight] [AGGREGATE SUM|MIN|MA
numkeys:需要做交集计算键的个数

//1和2做交集 权重(默认1,默认分数乘以权重得到的数)aggregate

127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:t1 user:t2
(integer) 1
127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "mike"
2) "168"

//1和2交集,2个权重边0.5(分数乘以权重得到的数)求max那个2个0.5至集合和集合2的分数都乘以0.5

127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:t1 user:t2 weights 1 0.5 aggregate max
(integer) 1

127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "mike"
2) "91"

2.并集

127.0.0.1:6379> zunionstore user:ranking:1_union_2 2 user:t1 user:t2
(integer) 8
127.0.0.1:6379> zrange user:ranking:1_union_2 0 -1
1) "kris"
2) "james"
3) "tom"
4) "mike"
5) "frank"
6) "tim"
7) "marrin"
8) "martin"

猜你喜欢

转载自blog.csdn.net/weixin_45310323/article/details/114983218