signed

QiShunwang

“诚信为本、客户至上”

02_API的理解和使用

2021/3/21 11:16:35   来源:

文章目录

  • 一、预备
    • 1 全局命令
    • 2 数据结构和内部编码
    • 3 单线程架构
  • 二、数据类型
    • 1. 字符串
    • 2. 哈希
    • 3. list列表

一、预备

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"

计数

语法:lncr key

lncr只能对数字自增
值不是整数的时候,返回错误
值=整数,返回自增后的结果
键不存在,按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.文章列表