Redis 4.x系列(三):Redis 数据类型之String、List

  Redis 与关系数据库不同,不是以表的模式来存储数据,无法使用 SQL 来操作 Redis 数据,而是直接使用 API 发送命令来操作目标数据。

  Redis 支持的数据类型与不同与关系数据库, Redis 支持的数据类型有String, List, Set, Hash, Sorted set, Bitmap, HyperLogLog, 具体使用参考Redis 数据类型, 官网:介绍 Redis 数据类型和抽象。  

String

String 字符串类型是 Redis 的基本数据类型,是二进制安全的,可以包含任何类型的数据,如媒体文件或序列化的对像数据。值最大可存 512 MB 数据。可以使用其它命令来对 String 数据做更复杂的操作。

  • set/get:设置存储键值对,根据键获取值。
  • strlen:根据键获取值的长度。
  • append:末尾追加。
  • setrange:从指定索引位开始覆盖。
  • getrange:返回字符串子串,由偏移量(索引位) start 和 end 确定(两者都包括)
  • setnx:键不存在时设置。
  • mset:一次批量设置多个键值对。
  • mget:根据多个键一次获取多个对应的值。

set/get

set/get 是 Redis 操作数据的最基本命令。 set 用于设置键值对, 存储键值对数据, 成功返回 OK;get 根据键获取值, 不存在返回**(nil)**。

  1. set 语法:set key value [EX seconds] [PX milliseconds] [NX|XX]

    NX:键不存在时设置值;XX:键存在时设置值

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> set nickName HelloKitty ex 5
    OK
    127.0.0.1:6379> get nickName
    "HelloKitty"
    127.0.0.1:6379> get nickName
    (nil)
  2. get 语法:get key

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> get email
    (nil)
    127.0.0.1:6379> set email hello@163.com
    OK
    127.0.0.1:6379> get email
    "hello@163.com"

strlen

strlen:返回字符串长度

语法:strlen key
示例:

1
2
3
4
5
6
127.0.0.1:6379> set name "Trump"
OK
127.0.0.1:6379> strlen name
(integer) 5
127.0.0.1:6379> strlen home
(integer) 0

append

append:字符串末尾追加字符串,返回新字符串长度

语法:append key value
示例:

1
2
3
4
5
6
127.0.0.1:6379> set address Shenzhen
OK
127.0.0.1:6379> append address Baoan
(integer) 13
127.0.0.1:6379> get address
"ShenzhenBaoan"

setrange

setrange:覆盖字符串的一部分,索引起始位是 0,从指定索引位开始,返回新字符串长度。

语法:setrange key index value
示例:

1
2
3
4
5
6
127.0.0.1:6379> set welcome "Hello World!"
OK
127.0.0.1:6379> setrange welcome 6 China
(integer) 12
127.0.0.1:6379> get welcome
"Hello China!"

getrange

返回 key 处的字符串值的子字符串,由偏移量 startend 确定(两者都包括在内)。 可以使用负偏移以便从字符串的末尾开始提供偏移。 所以**-1表示最后一个字符,-2**表示倒数第二个字符,依此类推。该API通过将结果范围限制为字符串的实际长度来处理超出范围的请求。

语法:GETRANGE key start end
示例:

1
2
3
4
5
6
7
8
9
10
11
redis> SET mykey "This is a string"
"OK"
redis> GETRANGE mykey 0 3
"This"
redis> GETRANGE mykey -3 -1
"ing"
redis> GETRANGE mykey 0 -1
"This is a string"
redis> GETRANGE mykey 10 100
"string"
redis>

setnx

setnx:仅在键不存在时设置,等同于 set命令的 NX选项
若键已存在,那么 set 命令会覆盖该键对应的值,若不希望已存在的键的值被覆盖,可以使用 setnx 命令(键不存在时 set),用于原子性、仅在键不存在时设置键的值。如果设置成功,返回 1,若键已存在,则返回 0,且不覆盖原来的值。

语法:setnx key value
示例:

1
2
3
4
5
6
127.0.0.1:6379> set age 23
OK
127.0.0.1:6379> setnx age 25
(integer) 0
127.0.0.1:6379> setnx address "Shenzhen"
(integer) 1

键已存在返回 0;不存在则返回 1,值设置成功

setex

setex 相当于 set 包含过期时间的简写。

语法:setex key seconds value
示例:

1
2
3
4
5
6
127.0.0.1:6379> setex name 5 HelloKitty
OK
127.0.0.1:6379> get name
"HelloKitty"
127.0.0.1:6379> get name
(nil)

mset

mset:设置多个键值对,在一次通信中设置。

语法:mset key value [key value ...]
示例:

1
2
127.0.0.1:6379> mset key1 value1 key2 value2 key3 value3
OK

mget

mget:一次获取多个值,在一次通信中设置。

语法:mget key [key ....]
示例:

1
2
3
4
127.0.0.1:6379> mget key1 key2 key3
1) "value1"
2) "value2"
3) "value3"

使用mset 和 mget命令来一次性地设置和获取多个健的值,是在一次通信中执行,可以节省网强行开销。

incr

如果 setvalue 值是个整数(integer),可使用 incr对该键进行自增操作。

语法:incr key
示例:

1
2
3
4
5
6
7
8
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> incr age
(integer) 21
127.0.0.1:6379> set price 22.3
OK
127.0.0.1:6379> incr price
(error) ERR value is not an integer or out of range

object encoding key

object encoding key:查看编码方式, Redis 使用了三种不同的编码方式来存储字符串对象,并会根据每个字符串的值自动决定所要使用的编码方式:

  • int:用于能够使用 64 位有符号整数表示的字符串。
  • embstr:用于长度小于或等于 44 字节的字符串;此编码在内存使用和性能方面更有效率。
  • raw:用于长度大于 44 字节的字符串。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> object encoding age
"int"
127.0.0.1:6379> set name Tom
OK
127.0.0.1:6379> object encoding name
"embstr"
127.0.0.1:6379> set str "Strings are the most basic kind of Redis value. Redis Strings are binary safe"
OK
127.0.0.1:6379> object encoding str
"raw"

List

list 列表能够存储一组有序的字符串(元素),因此可用作队列,在 Redis 中与键相关联的值可以是字符串组成的列表。一个列表最多可存储 2 的 32 次方减 1 个元素。可以对列表两端插入(push)和弹出(pop)。

两个特点:

  1. 列表中的元素是有序的。可以通过索引下标获取某个元素或某个索引范围的元素列表。
  2. 列表中的元素是可重复的。

使用场景:

  1. lpush + lpop = Stack(栈)
  2. lpush + rpop = Queue(队列)
  3. lpush + ltrim = Capped Collection(有限集合)
  4. lpush + brpop = Message Queue(消息队列)

List索引下标,从左到右分别是 0 到 N-1,但从右到左分别是 -1 到 -N。

Redis 中的列表更像 Java 中的 linkedlist,插入和删除操作非常快,时间复杂度是 O(1),但索引定位很慢,时间复杂度是 O(N)。

List 列表索引定义:从左到右,起始索引是 0,最后位索引是 (N个元素 - 1)。

  • lpush:左插入,排在最左端。
  • rpush:右插入,排在最右端。
  • linsert:在指定元素的前后位置插入。
  • lpop:删除最左端一个元素。
  • rpop:删除最右端一个元素。
  • ltrim:删除索引范围之外的元素。
  • lset:设置指定索引元素的的值。
  • lrange:获取列表元素,可根据索引位范围进行查询。
  • lindex:获取索引位的值,根据指定的索引位查询。
  • llen:获取列表长度。
  • lrem:删除指定值的元素。

Redis 内部使用 quicklist 存储列表对像,有两个配置项可选:

  • list-max-ziplist-size:一个列表条目中一个内部节点的最大大小,通常使用默认值。
  • list-compress-depth:列表压缩策略。当列表很长时,最可能被访问的是位于列表两端的数据,可使用此参数进行调优,获得更好的压缩比。

插入元素

list 插入操作命令有 lpush,rpush,linsert,插入成功后返回列表的长度,如果向一个不存在的键插入元素, Redis 将首先创建空列表并将其与键关联,无需人工为一个键初始化一个空列表;Redis 也会自动值为空列表的键。

  1. lpush:左插入,将元素添加到列表的左端, 最后插入的排在最前面。

    语法:lpush key value [value ...]
    示例:

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> lpush hot_product product1 product2 product3
    (integer) 3
    127.0.0.1:6379> lrange hot_product 0 -1
    1) "product3"
    2) "product2"
    3) "product1"
  2. rpush:右插入,将元素添加到列表的右端, 最后插入的排在最后面。

    语法:rpush key value [value ...]
    示例:

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> rpush cold_product pro1 pro2 pro3
    (integer) 3
    127.0.0.1:6379> lrange cold_product 0 -1
    1) "pro1"
    2) "pro2"
    3) "pro3"
  3. linsert:前后插入,将元素插入到表的支点/枢轴元素之前或之后。

    语法:linsert key BEFORE|AFTER pivot value
    示例:

    1
    2
    3
    4
    5
    6
    7
    127.0.0.1:6379> linsert cold_product after pro2 pro2-1
    (integer) 4
    127.0.0.1:6379> lrange cold_product 0 -1
    1) "pro1"
    2) "pro2"
    3) "pro2-1"
    4) "pro3"

查看列表

  1. lrange
    lrange:查看列表元素,可根据索引位范围进行查询,。

语法:lrange key start_index end_index //end_index 包含自身
示例:0 到 -1表示整个列表。

1
2
3
4
127.0.0.1:6379> lrange hot_product 0 -1
1) "product3"
2) "product2"
3) "product1"
  1. lindex
    lindex:查看索引位的值,根据指定的索引位查询。

语法:lindex key index
示例:

1
2
3
4
5
6
7
8
127.0.0.1:6379> lindex hot_product 0
"product3"
127.0.0.1:6379> lindex hot_product 1
"product2"
127.0.0.1:6379> lindex hot_product 2
"product1"
127.0.0.1:6379> lindex hot_product 3
(nil)

删除元素

当删除的键不存在时,返回(nil)。

  1. lpop:从左删除一个,从列表的左端删除第一个元素,返回删除的元素值。

    语法:lpop key
    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # lpop 从左端开始删除
    127.0.0.1:6379> lrange hot_product 0 -1
    1) "product3"
    2) "product2"
    3) "product1"
    127.0.0.1:6379> lpop hot_product
    "product3"
    127.0.0.1:6379> lpop hot_product
    "product2"
    127.0.0.1:6379> lpop hot_product
    "product1"
    127.0.0.1:6379> lpop hot_product
    (nil)
  2. rpop:从右删除一个,从列表的右端删除第一个元素,返回删除的元素值。

    语法:rpop key
    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # rpop 从右端开始删除
    127.0.0.1:6379> lrange cold_product 0 -1
    1) "pro1"
    2) "pro2"
    3) "pro2-1"
    4) "pro3"
    127.0.0.1:6379> rpop cold_product
    "pro3"
    127.0.0.1:6379> rpop cold_product
    "pro2-1"
    127.0.0.1:6379> rpop cold_product
    "pro2"
    127.0.0.1:6379> rpop cold_product
    "pro1"
    127.0.0.1:6379> rpop cold_product
    (nil)
  3. ltrim:删除索引范围外,删除多个元素,只保留索引范围内的元素, 索引范围之外的元素删除。

    语法:ltrim key start_idx end_idx
    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # ltrim 只保留索引范围内的元素
    127.0.0.1:6379> rpush hot_products product1 product2 product3 product4 product5 product6
    (integer) 6
    127.0.0.1:6379> lrange hot_products 0 -1
    1) "product1"
    2) "product2"
    3) "product3"
    4) "product4"
    5) "product5"
    6) "product6"
    127.0.0.1:6379> ltrim hot_products 2 4
    OK
    127.0.0.1:6379> lrange hot_products 0 -1
    1) "product3"
    2) "product4"
    3) "product5"
  4. lrem:从列表中找到等于 value 的元素进行删除。

    语法:lrem key count value
    count的值分三种情况,大于 0 表示从左到右删除 count 元素;等于 0 是删除所有;**小于 0 **表示从右到左删除 count 绝对值个元素。
    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # 添加元素
    127.0.0.1:6379> lrange user_list 0 -1
    1) "user3"
    2) "user4"
    3) "user2"
    4) "user2"
    5) "user5"
    6) "user4"
    7) "user3"
    8) "user2"
    9) "user1"
    127.0.0.1:6379> lrem user_list 0 user2
    (integer) 3
    127.0.0.1:6379> lrange user_list 0 -1
    1) "user3"
    2) "user4"
    3) "user5"
    4) "user4"
    5) "user3"
    6) "user1"
  5. lset:设置值,设置列表中指定索引位置元素的值。

    语法:lset key index value
    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # lset 设置指定索引位元素的值
    127.0.0.1:6379> lrange hot_products 0 -1
    1) "product3"
    2) "product4"
    3) "product5"
    127.0.0.1:6379> lset hot_products 1 product_apple
    OK
    127.0.0.1:6379> lrange hot_products 0 -1
    1) "product3"
    2) "product_apple"
    3) "product5"

阻塞删除

LPOPRPOP命令有对应的阻塞版本:BLPOPBRPOP,也是从列表最左端或右端弹出元素;当列表为空时,阻塞命令将客户端阻塞。使用阻塞命令必须指定一个以秒为单位的超时时间,表示最长等待几秒。当超时时间为零时,表示永久等待。

阻塞特性在任务调度场景中非常有用,多个任务执行程序等待任务调度程序分配任务,任务执行程序只需对列表使用 BLPOPBRPOP,每当调度程序把新任务插入到列表中,任务执行程序之一便会获取到该任务。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# worker-1:阻塞
127.0.0.1:6379> brpop job_queue 0
# worker-2:阻塞
127.0.0.1:6379> brpop job_queue 0

# 分配任务
127.0.0.1:6379> lpush job_queue task1

# worker-1:先阻塞先获取到任务(弹出)
1) "job_queue"
2) "task1"
(32.18s)

# 再分配任务
127.0.0.1:6379> lpush job_queue task2
127.0.0.1:6379> lpush job_queue task3

# worker-2:获取到任务(弹出)
1) "job_queue"
2) "task2"
(32.96s)

# 查看任务
127.0.0.1:6379> lrange job_queue 0 -1
1) "task3"

相关参考

Redis 命令参考
Redis 数据结构和主要命令

Redis 4.x系列(三):Redis 数据类型之String、List

http://blog.gxitsky.com/2018/09/24/Redis-3-datatype-1-string-list/

作者

光星

发布于

2018-09-24

更新于

2022-08-14

许可协议

评论