Redis 操作命令

1. 全局命令(keys的通用操作)

所谓通用操作是指,不管value是五种类型中的哪一种类型,都可以用的操作

1.1. KEYS 查询键

  1. keys pattern

获取所有与pattern匹配的key*表示任意0个或多个字符,?表示任意一个字符。如果存在大量键,线上禁止使用此指令

  • 收集:到目为此,*在哪些技术中出现过,分别表示什么意思?
  • 正则表达式,*,0个或多个
  • url-pattern,*,模糊匹配(前缀和后缀)

比如:

  • KEYS * 匹配数据库中所有 key
  • KEYS h?llo 匹配 hellohallohxllo
  • KEYS h*llo 匹配 hlloheeeeello
  • KEYS h[ae]llo 匹配 hellohallo ,但不匹配 hillo

示例:

  1. redis> MSET one 1 two 2 three 3 four 4 # 一次设置 4 个 key
  2. OK
  3. redis> KEYS *o*
  4. 1) "four"
  5. 2) "two"
  6. 3) "one"
  7. redis> KEYS t??
  8. 1) "two"
  9. redis> KEYS t[w]*
  10. 1) "two"
  11. redis> KEYS * # 匹配数据库内所有 key
  12. 1) "four"
  13. 2) "three"
  14. 3) "two"
  15. 4) "one"

1.2. DBSIZE 查询键总数

dbsize

查询目前存在的键的总数量。
示例:

redis> DBSIZE
(integer) 5

1.3. EXISTS 检查键是否存在

exists key

判断该key是否存在,返回1表示存在,0表示不存在

redis> SET db "redis"
OK

redis> EXISTS db
(integer) 1

redis> DEL db
(integer) 1

redis> EXISTS db
(integer) 0

1.4. DEL 删除键

DEL key [key …]

删除给定的一个或多个 key。并返回被删除 key 的数量,如果删除不存在键返回0。无论值是什么数据结构类型,del命令都可以将其删除

#  删除单个 key
redis> DEL name
(integer) 1

# 删除一个不存在的 key
redis> DEL phone # 失败,没有 key 被删除
(integer) 0

# 同时删除多个 key
redis> DEL name type website
(integer) 3

1.5. EXPIRE 设置过期时间(秒级别)

expire key

为给定 key 设置生存时间(单位:秒),当 key 过期时(生存时间为 0 ),它会被自动删除这个key。

redis> EXPIRE cache_page 30  # 设置过期时间为 30 秒
(integer) 1

redis> EXPIRE cache_page 30000   # 如果在过期之前,再次使用EXPIRE命令,则更新过期时间
(integer) 1

1.6. TTL 查询剩余生存时间(秒级别)

ttl key

获取该key所剩余的超时时间(TTL, time to live),返回值类型如下:

  • key 不存在时,返回-2
  • key 存在但没有设置剩余生存时间时,返回-1
  • key 存在且有设置剩余时间,则以秒为单位,返回大于等于0的整数(即 key 的剩余生存时间)

注:在 Redis 2.8 以前,当 key 不存在,或者 key 没有设置剩余生存时间时,命令都返回-1

# 不存在的 key
redis> TTL key
(integer) -2

# key 存在,但没有设置剩余生存时间
redis> TTL key
(integer) -1

# 有剩余生存时间的 key
redis> TTL key
(integer) 10084

1.7. EXPIREAT 设置生存时间(秒级别时间戳)

EXPIREAT key timestamp

EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置生存时间。不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。
如果生存时间设置成功,返回1;当key不存在或没办法设置生存时间,返回0

redis> EXPIREAT cache 1355292000     # 这个 key 将在 2021.12.12 过期
(integer) 1

1.8. PEXPIRE 设置生存时间(毫秒级别)

PEXPIRE key milliseconds

这个命令和 EXPIRE 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间,而EXPIRE命令以秒为单位。设置成功,返回1key不存在或设置失败,返回0

redis> PEXPIRE mykey 1500
(integer) 1

redis> TTL mykey    # TTL 的返回值以秒为单位
(integer) 2

redis> PTTL mykey   # PTTL 可以给出准确的毫秒数
(integer) 1499

1.9. PEXPIREAT 设置生存时间(毫秒级别时间戳)

PEXPIREAT key milliseconds-timestamp

这个命令和 expireat 命令类似,但它以毫秒为单位设置 key 的过期 unix 时间戳,而 expireat 命令则以秒为单位。如果生存时间设置成功,返回1。 当 key 不存在或没办法设置生存时间时,返回0

redis> PEXPIREAT mykey 1555555555005
(integer) 1

redis> TTL mykey           # TTL 返回秒
(integer) 223157079

redis> PTTL mykey          # PTTL 返回毫秒
(integer) 223157079318

1.10. PTTL 查询剩余生存时间(毫秒级别)

PTTL key

这个命令类似于 TTL 命令,但它以毫秒为单位返回 key 的剩余生存时间,而 TTL 命令则以秒为单位。返回值类型如下:

  • key 不存在时,返回-2
  • key 存在但没有设置剩余生存时间时,返回-1
  • key 存在且有设置剩余时间,则以毫秒为单位,返回大于等于0的整数(即 key 的剩余生存时间)

注:在 Redis 2.8 以前,当 key 不存在,或者 key 没有设置剩余生存时间时,命令都返回-1

1.11. PERSIST 移除生存时间

PERSIST key

移除给定 key 的生存时间,相当于将这个key从“易失的”(带生存时间的key)转换成“持久的”(一个不带生存时间、永不过期的key)。当生存时间移除成功时,返回1; 如果 key 不存在或 key 没有设置生存时间,返回0

redis> EXPIRE mykey 10  # 为 key 设置生存时间
(integer) 1

redis> TTL mykey
(integer) 10

redis> PERSIST mykey    # 移除 key 的生存时间
(integer) 1

redis> TTL mykey
(integer) -1

1.12. TYPE 键存储的数据结构类型

TYPE key

返回 key 所储存的值的数据结构类型(以字符串形式返回)。返回值:

  • none (key不存在)
  • string (字符串)
  • list (列表)
  • set (集合)
  • zset (有序集)
  • hash (哈希表)
  • stream (流) ```

    字符串

    redis> SET weather “sunny” OK redis> TYPE weather string

列表

redis> LPUSH book_list “programming in scala” (integer) 1 redis> TYPE book_list list

集合

redis> SADD pat “dog” (integer) 1 redis> TYPE pat set

### 1.13. RANDOMKEY 随机获取一个key

RANDOMKEY

从当前数据库中随机返回(不删除)一个`key`。当数据库不为空时,返回一个`key`。当数据库为空时,返回`nil`

redis> RANDOMKEY “food” redis> RANDOMKEY (nil)

### 1.14. RENAME 重命名

RENAME key newkey

将 `key` 重命名为 `newkey`。返回值如下:

- 当`key`重命名成功时提示`OK`
- 当 `key` 和 `newkey` 相同,或者 `key` 不存在时,返回一个错误

> 特别注意:

> - 当`newkey`已经存在时,`RENAME`命令会覆盖旧值。为了防止被强行`rename`,Redis提供了`renamenx`命令,确保只有`newKey`不存在时候才被覆盖。
> - 通过测试可知,由于重命名键期间会执行`del`命令删除旧的键,如果键对应的值比较大,会存在阻塞Redis的可能性

key 存在且 newkey 不存在

redis> RENAME message greeting OK redis> EXISTS message # message 不复存在 (integer) 0 redis> EXISTS greeting # greeting 取而代之 (integer) 1

当 key 不存在时,返回错误

redis> RENAME fake_key never_exists (error) ERR no such key

newkey 已存在时, RENAME 会覆盖旧 newkey

redis> SET name “moon” OK redis> SET name2 “kira” OK redis> RENAME name name2 OK redis> GET name (nil) redis> GET name2 # 原来的值 kira 被覆盖了 “moon”

### 1.15. SELECT 切换数据库

SELECT index

切换到指定的数据库,数据库索引号 `index` 用数字值指定,以 `0` 作为起始索引值。默认使用 `0` 号数据库。

- 一个Redis服务器可以包括多个数据库,客户端可以指连接Redis中的的哪个数据库,就好比一个mysql服务器中创建多个数据库,客户端连接时指定连接到哪个数据库。
- 一个Redis实例最多可提供`16`个数据库,下标为`0`到`15`,客户端默认连接第`0`个数据库,也可以通过`select`命令选择哪个数据库。

redis> SELECT 1 # 使用 1 号数据库 OK

### 1.16. MOVE 迁移键

MOVE key db

将当前数据库的 `key` 移动到给定的数据库 `db` 当中。移动成功返回`1`,失败则返回`0`<br />如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 `key` ,或者 `key` 不存在于当前数据库,那么 `MOVE` 命令没有任何效果。因此,也可以利用这一特性,将 `MOVE` 命令当作锁(locking)原语(primitive)。

key 存在于当前数据库

redis> SELECT 0 # redis默认使用数据库 0,为了清晰起见,这里再显式指定一次。 OK redis> MOVE song 1 # 将 song 移动到数据库 1 (integer) 1 redis> EXISTS song # song 已经被移走 (integer) 0 redis> SELECT 1 # 使用数据库 1 OK redis:1> EXISTS song # 证实 song 被移到了数据库 1 (注意命令提示符变成了”redis:1”,表明正在使用数据库 1) (integer) 1

当 key 不存在的时候

redis:1> EXISTS fake_key (integer) 0 redis:1> MOVE fake_key 0 # 试图从数据库 1 移动一个不存在的 key 到数据库 0,失败 (integer) 0 redis:1> select 0 # 使用数据库0 OK redis> EXISTS fake_key # 证实 fake_key 不存在 (integer) 0

当源数据库和目标数据库有相同的 key 时

redis:1> SELECT 0 # 使用数据库0,并试图将 favorite_fruit 移动到数据库 1 OK redis> MOVE favorite_fruit 1 # 因为两个数据库有相同的 key,MOVE 失败 (integer) 0 redis> GET favorite_fruit # 数据库 0 的 favorite_fruit 没变 “banana” redis> SELECT 1 OK redis:1> GET favorite_fruit # 数据库 1 的 favorite_fruit 也是 “apple”

### 1.17. 其他小结
#### 1.17.1. KEYS 与 DBSIZE 命令小结

- `dbsize` 命令在计算键总数时不会遍历所有键,而是直接获取 Redis 内置的键总数变量,所以`dbsize`命令的时间复杂度是O(1)。
- `keys` 命令会遍历所有键,所以它的时间复杂度是`o(n)`,当 Redis 保存了大量键时线上环境禁止使用`keys`命令。
#### 1.17.2. 关于使用Redis相关过期命令时注意点

- 如果使用 `expire key` 命令时相应的键不存在,返回结果为0
- 如果过期时间为负值,键会立即被删除,效果与使用`del`命令一样
- `persist`命令可以将键的过期时间清除
- 对于字符串类型的键,执行`set`命令后,会重置过期时间(如果没有设置则重置为不过期),这个问题很容易在开发中被忽视。
- Redis不支持二级数据结构(例如哈希、列表)内部元素的过期功能,例如不能对列表类型的一个元素做过期时间设置。
- 如果关了Redis服务器端,在默认情况下从控制台插入的`key=value`键值对数据,就算key时间未到,也会自动销毁。
## 2. 存储String类型(重点)
字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型**存入和获取的数据相同**。字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),在Redis中字符串类型的Value最多可以容纳的数据长度是512M。

> 注:Redis所有类型的键都是字符串类型,而且其他几种数据结构都是在字符串类型基础上构建的。


### 2.1. SET 赋值

SET key value [EX seconds] [PX milliseconds] [NX|XX]

设定`key`持有指定的字符串`value`,如果该`key`存在则进行覆盖操作,无视类型。返回结果为`OK`代表设置成功。<br />**当 `SET` 命令对一个带有生存时间(TTL)的键进行设置之后,该键原有的生存时间将被清除。**<br />**可选参数**:<br />从 Redis 2.6.12 版本开始,`SET`命令的行为可以通过一系列参数来修改:

- `EX seconds`:将键的过期时间设置为`seconds`秒。执行`SET key value EX seconds`的效果等同于执行`SETEX key seconds value`
- `PX milliseconds`:将键的过期时间设置为`milliseconds`毫秒。执行`SET key value PX milliseconds`的效果等同于执行`PSETEX key milliseconds value`
- `NX`:只在键不存在时,才对键进行设置操作。执行`SET key value NX`的效果等同于执行`SETNX key value`
- `XX`:只在键已经存在时,才对键进行设置操作

> note:因为`SET`命令可以通过参数来实现`SETNX`、`SETEX`以及`PSETEX`命令的效果,所以 Redis 将来的版本可能会移除并废弃`SETNX`、`SETEX`和`PSETEX`这三个命令。


**返回值**:

- 在 Redis 2.6.12 版本以前,`SET` 命令总是返回`OK`
- 从 Redis 2.6.12 版本开始,`SET` 命令只在设置操作成功完成时才返回`OK`;如果命令使用了`NX`或者`XX`选项,但是因为条件没达到而造成设置操作未执行,那么命令将返回空批量回复(NULL Bulk Reply)

**代码示例**:

对不存在的键进行设置:

redis> SET key “value” OK redis> GET key “value”

对已存在的键进行设置:

redis> SET key “new-value” OK redis> GET key “new-value”

使用 EX 选项:

redis> SET key-with-expire-time “hello” EX 10086 OK redis> GET key-with-expire-time “hello” redis> TTL key-with-expire-time (integer) 10069

使用 PX 选项:

redis> SET key-with-pexpire-time “moto” PX 123321 OK redis> GET key-with-pexpire-time “moto” redis> PTTL key-with-pexpire-time (integer) 111939

使用 NX 选项:

redis> SET not-exists-key “value” NX OK # 键不存在,设置成功 redis> GET not-exists-key “value” redis> SET not-exists-key “new-value” NX (nil) # 键已经存在,设置失败 redis> GEt not-exists-key “value” # 维持原值不变

使用 XX 选项:

redis> EXISTS exists-key (integer) 0 redis> SET exists-key “value” XX (nil) # 因为键不存在,设置失败 redis> SET exists-key “value” OK # 先给键设置一个值 redis> SET exists-key “new-value” XX OK # 设置新值成功 redis> GET exists-key “new-value”

### 2.2. SETNX 不存在时赋值

SETNX key value

只在键`key`不存在的情况下,将键`key`的值设置为`value`。若键`key`已经存在,则`SETNX`命令不做任何动作。设置成功时返回`1`,设置失败时返回`0`

> `SETNX`是『SET if Not exists』(如果不存在,则`SET`)的简写。

redis> EXISTS job # job 不存在 (integer) 0 redis> SETNX job “programmer” # job 设置成功 (integer) 1 redis> SETNX job “code-farmer” # 尝试覆盖 job ,失败 (integer) 0 redis> GET job # 没有被覆盖 “programmer”


> note: 由于Redis的单线程命令处理机制,如果有多个客户端同时执行`setnx key value`,根据`setnx`的特性只有一个客户端能设置成功,`setnx`可以作为分布式锁的一种实现方案。


### 2.3. SETEX 赋值并设置生存时间(秒级别)

SETEX key seconds value

将键 `key` 的值设置为 `value`,并将键 `key` 的生存时间设置为 `seconds` 秒钟。如果键 `key` 已经存在, 那么 `SETEX` 命令将覆盖已有的值。设置成功时返回`OK`。 当 `seconds` 参数不合法时,命令将返回一个错误。<br />`SETEX` 命令效果相当于以下命令:

SET key value EXPIRE key seconds # 设置生存时间

`SETEX` 与以上两条命令组合的区别在于 `SETEX` 是一个原子(atomic)操作,它可以在同一时间内完成设置值和设置过期时间这两个操作,因此 `SETEX` 命令在储存缓存的时候非常实用。<br />**代码示例**:

在键 key 不存在的情况下执行 SETEX :

redis> SETEX cache_user_id 60 10086 OK redis> GET cache_user_id # 值 “10086” redis> TTL cache_user_id # 剩余生存时间 (integer) 49

键 key 已经存在, 使用 SETEX 覆盖旧值:

redis> SET cd “timeless” OK redis> SETEX cd 3000 “goodbye my love” OK redis> GET cd “goodbye my love” redis> TTL cd (integer) 2997

### 2.4. PSETEX 赋值并设置生存时间(毫秒级别)

PSETEX key milliseconds value

`PSETEX` 和 `SETEX` 命令相似,但它以毫秒为单位设置 `key` 的生存时间,而 `SETEX` 命令则以秒为单位进行设置。在设置成功时返回`OK`<br />**代码示例**:

redis> PSETEX mykey 1000 “Hello” OK redis> PTTL mykey (integer) 999 redis> GET mykey “Hello”

### 2.5. GET 取值

GET key

返回与键 `key` 相关联的字符串值。返回值情况如下:

- 如果键 `key` 不存在, 那么返回特殊值 `nil`;否则,返回键 `key` 的值
- 如果键 `key` 的值并非字符串类型,那么返回一个错误,因为 `GET` 命令只能用于字符串值。

**代码示例**:

对不存在的键 key 或是字符串类型的键 key 执行 GET 命令:

redis> GET db (nil) redis> SET db redis OK redis> GET db “redis”

对不是字符串类型的键 key 执行 GET 命令:

redis> LPUSH db redis mongodb mysql (integer) 3 redis> GET db (error) ERR Operation against a key holding the wrong kind of value

### 2.6. GETSET 先取值再赋值

GETSET key value

将键 `key` 的值设为 `value`,并返回键 `key` 在被设置之前的旧值。返回值情况如下:

- 返回给定键 `key` 的旧值
- 如果键 `key` 没有旧值,也即是说,键 `key` 在被设置之前并不存在,那么命令返回 `nil`
- 如果键 `key` 存在但不是字符串类型时,命令返回一个错误

**代码示例**:

redis> GETSET db mongodb # 没有旧值,返回 nil (nil) redis> GET db “mongodb” redis> GETSET db redis # 返回旧值 mongodb “mongodb” redis> GET db “redis”

### 2.7. MSET 批量赋值

MSET key value [key value …]

同时为多个键批量设置值。`MSET` 命令总是返回`OK`。如果某个给定键已经存在,那么 `MSET` 将使用新值去覆盖旧值。如果不想覆盖旧值,则使用`MSETNX`,此命令只会在所有给定键都不存在的情况下进行设置。<br />`MSET` 是一个原子性(atomic)操作,所有给定键都会在同一时间内被设置,不会出现某些键被设置了但是另一些键没有被设置的情况。<br />**代码示例**:

同时对多个键进行设置:

redis> MSET date “2021.3.30” time “11:00 a.m.” weather “sunny” OK

redis> MGET date time weather 1) “2021.3.30” 2) “11:00 a.m.” 3) “sunny”

覆盖已有的值:

redis> MGET k1 k2 1) “hello” 2) “world”

redis> MSET k1 “good” k2 “bye” OK

redis> MGET k1 k2 1) “good” 2) “bye”

### 2.8. MGET 批量取值

MGET key [key …]

返回给定的一个或多个字符串键的值。`MGET` 命令结果是返回一个列表,列表中包含了所有给定键的值,是按照传入键的顺序返回。如果给定的字符串键里面,有某个键不存在,那么这个键的值将以特殊值 `nil` 表示。<br />**代码示例**:

redis> MGET redis mongodb mysql # 不存在的 mysql 返回 nil 1) “redis.com” 2) “mongodb.org” 3) (nil)


> note:

> 批量操作命令可以有效提高效率,假如没有`mget`这样的命令,要执行n次`get`命令具体耗时是:`n次get时间=n次网络时间+n次命令时间`

> 使用`mget`命令后,要执行n次`get`命令操作具体耗时是:`n次get时间=1次网络时间+n次命令时间`

> Redis可以支撑每秒数万的读写操作,但是这指的是Redis服务端的处理能力,对于客户端来说,一次命令除了命令时间还是有网络时间,假设网络时间为1毫秒,命令时间为0.1毫秒(按照每秒处理1万条命令算),那么执行1000次`get`命令需要1.1秒(`1000*1+1000*0.1=1100ms`),1次`mget`命令的需要0.101秒(`1*1+1000*0.1=101ms`)。


### 2.9. INCR 数字递增

INCR key

为键 `key` 储存的数字值加1。`INCR` 命令会返回键 `key` 在执行加1操作之后的值。存在以下3种情况:

- 如果键 `key` 不存在,那么它的值会先被初始化为`0`,然后再执行 `INCR` 命令。
- 如果键 `key` 存在并且储存的值为数字,则在原数值加1后替换原来的值,并返回加1后的结果
- 如果键 `key` 储存的值不能被解释为数字,那么 `INCR` 命令将返回一个错误。

> note:

> - 本操作的值限制在 64 位(bit)有符号数字表示之内。
> - `INCR` 命令是一个针对字符串的操作。因为 Redis 并没有专用的整数类型,所以键 `key` 储存的值在执行 `INCR` 命令时会被解释为十进制64位有符号整数。


**代码示例**:

redis> SET page_view 20 OK redis> INCR page_view (integer) 21 redis> GET page_view # 数字值在 Redis 中以字符串的形式保存 “21”

### 2.10. DECR 数字递减

DECR key

为键 `key` 储存的数字值减1。`DECR` 命令会返回键 `key` 在执行减1操作之后的值。存在以下3种情况:

- 如果键 `key` 不存在,那么它的值会先被初始化为`0`,然后再执行 `DECR` 命令。
- 如果键 `key` 存在并且储存的值为数字,则在原数值减1后替换原来的值,并返回减1后的结果
- 如果键 `key` 储存的值不能被解释为数字,那么 `DECR` 命令将返回一个错误。

> note: 本操作的值限制在 64 位(bit)有符号数字表示之内。

对储存数字值的键 key 执行 DECR 命令:

redis> SET failure_times 10 OK redis> DECR failure_times (integer) 9

对不存在的键执行 DECR 命令:

redis> EXISTS count (integer) 0 redis> DECR count (integer) -1

### 2.11. INCRBY/DECRBY  数字递增/递减指定指定值

递增指定值

INCRBY key increment

递减指定值

DECRBY key decrement

为键 `key` 储存的数字值加上增量 `increment`/减去减量`decrement`,并返回该值。存在以下3种情况:

- 如果键 `key` 不存在,那么它的值会先被初始化为`0`,然后再执行 `INCRBY`/`DECRBY` 命令。
- 如果键 `key` 存在并且储存的值为数字,则在原数值加上`increment`值/减去`decrement`值后替换原来的值,并返回结果
- 如果键 `key` 储存的值不能被解释为数字,那么 `INCRBY`/`DECRBY` 命令将返回一个错误。

> note: 本操作的值限制在 64 位(bit)有符号数字表示之内。

键存在,并且值为数字:

redis> SET rank 50 OK redis> INCRBY rank 20 (integer) 70 redis> GET rank “70”

键不存在:

redis> EXISTS counter (integer) 0 redis> INCRBY counter 30 (integer) 30 redis> GET counter “30”

键存在,但值无法被解释为数字:

redis> SET book “long long ago…” OK redis> INCRBY book 200 (error) ERR value is not an integer or out of range

对已经存在的键执行 DECRBY 命令:

redis> SET count 100 OK redis> DECRBY count 20 (integer) 80

对不存在的键执行 DECRBY 命令:

redis> EXISTS pages (integer) 0 redis> DECRBY pages 10 (integer) -10

### 2.12. APPEND 字符追加

APPEND key value

`append` 指令可以向字符串尾部追加值。返回值为追加 `value` 之后,键 `key` 的值的长度。

- 如果键 `key` 已经存在并且它的值是一个字符串,`APPEND` 命令将把 `value` 追加到键 `key` 现有值的末尾。
- 如果键 `key` 不存在,`APPEND` 就简单地将键 `key` 的值设为 `value`,就像执行 `SET key value` 一样。

对不存在的 key 执行 APPEND :

redis> EXISTS myphone # 确保 myphone 不存在 (integer) 0 redis> APPEND myphone “nokia” # 对不存在的 key 进行 APPEND ,等同于 SET myphone “nokia” (integer) 5 # 字符长度

对已存在的字符串进行 APPEND :

redis> APPEND myphone “ - 1110” # 长度从 5 个字符增加到 12 个字符 (integer) 12 redis> GET myphone “nokia - 1110”

### 2.13. STRLEN 字符串长度

STRLEN key

返回键 `key` 储存的字符串值的长度。当键 `key` 不存在时,命令返回0。当 `key` 储存的不是字符串值时,返回一个错误。

获取字符串值的长度:

redis> SET mykey “Hello world” OK redis> STRLEN mykey (integer) 11

不存在的键的长度为 0 :

redis> STRLEN nonexisting (integer) 0

中文字符串

redis> SET chinese “中文” OK redis> STRLEN chinese (integer) 6


> **注意:每个中文占 3 个字节**


### 2.14. GETSET 设置并返回原值

GETSET key value

`getset` 和 `set` 一样会设置值,将键 `key` 的值设为 `value`,但 `GETSET` 命令会返回键 `key` 在被设置之前的旧值。存在以下特殊情况:

- 当键 `key` 在被设置之前并不存在,返回 `nil`
- 当键 `key` 存在但不是字符串类型时,返回一个错误

redis> GETSET db mongodb # 没有旧值,返回 nil (nil) redis> GET db “mongodb” redis> GETSET db redis # 返回旧值 mongodb “mongodb” redis> GET db “redis”

### 2.15. SETRANGE 设置指定位置的字符

SETRANGE key offset value

从偏移量 `offset` 开始, 用 `value` 参数覆写(overwrite)键 `key` 储存的字符串值,并返回被修改之后,字符串值的长度。不存在的键 `key` 当作空白字符串处理。**值的下标是从0开始计算**。<br />`SETRANGE` 命令会确保字符串足够长以便将 `value` 设置到指定的偏移量上, 如果键 `key` 原来储存的字符串长度比偏移量小(比如字符串只有`5`个字符长,但设置的`offset`是`10`),那么原字符和偏移量之间的空白将用零字节(zerobytes, `"\x00"`)进行填充。

> Warning: 当生成一个很长的字符串时, Redis 需要分配内存空间, 该操作有时候可能会造成服务器阻塞(block)。

对非空字符串执行 SETRANGE 命令:

redis> SET greeting “hello world” OK redis> SETRANGE greeting 6 “Redis” (integer) 11 redis> GET greeting “hello Redis”

对空字符串/不存在的键执行 SETRANGE 命令:

redis> EXISTS empty_string (integer) 0 redis> SETRANGE empty_string 5 “Redis!” # 对不存在的 key 使用 SETRANGE (integer) 11 redis> GET empty_string # 空白处被”\x00”填充 “\x00\x00\x00\x00\x00Redis!”

### 2.16. GETRANGE 截取字符串

GETRANGE key start end

返回键 `key` 储存的字符串值的指定部分,字符串的截取范围由 `start` 和 `end` 两个偏移量决定(包括 `start` 和 `end` 在内)。负数偏移量表示从字符串的末尾开始计数,`-1` 表示最后一个字符,`-2` 表示倒数第二个字符,以此类推。<br />`GETRANGE` 通过保证子字符串的值域(range)不超过实际字符串的值域来处理超出范围的值域请求。

> note: `GETRANGE` 命令在 Redis 2.0 之前的版本里面被称为 `SUBSTR` 命令

redis> SET greeting “hello, my friend” OK redis> GETRANGE greeting 0 4 # 返回索引0-4的字符,包括4。 “hello” redis> GETRANGE greeting -1 -5 # 不支持回绕操作 “” redis> GETRANGE greeting -3 -1 # 负数索引 “end” redis> GETRANGE greeting 0 -1 # 从第一个到最后一个 “hello, my friend” redis> GETRANGE greeting 0 1008611 # 值域范围不超过实际字符串,超过部分自动被符略 “hello, my friend”

### 2.17. 命令的时间复杂度
字符串这些命令中,除了`del`、`mset`、`mget`支持多个键的批量操作,时间复杂度和键的个数相关,为`O(n)`,`getrange`和字符串长度相关,也是`O(n)`,其余的命令基本上都是`O(1)`的时间复杂度,所以操作速度非常快
## 3. 存储hash(了解)
Redis中的Hash类型可以看成具有String Key和String Value的map容器。所以该类型非常适合于存储值对象的信息。如Username、Password和Age等。如果Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash可以存储4294967295个键值对。
### 3.1. 赋值

- `hset key field value`:为指定的key设定field/value对(键值对)。
- `hmset key field value [field2 value2 …]`:设置key中的多个filed/value
### 3.2. 取值

- `hget key field`:    返回指定的key中的field的值
- `hmget key fileds`:获取key中的多个filed的值
- `hgetall key`:获取key中的所有filed-vaule
### 3.3. 删除

- `hdel key field [field … ]`:可以删除一个或多个字段,返回值是被删除的字段个数,当value都删除后,key也会删除了
- `del key`:删除对应的key的整个Hash
### 3.4. 增加数字
`hincrby key field increment`:设置key中filed的值增加increment,如:age增加20
### 3.5. 其他命令

- `hexists key field`:判断指定的key中的filed是否存在
- `hlen key`:获取key所包含的field的数量
- `hkeys key`:获得所有的key
- `hvals key`:获得所有的value
## 4. Redis存储数据

- 对于集合类型(List/Set/SortSet),共同点:
- **如果元素都没有,那么这个key自动从Redis中删除**
- **如果强行删除key,那么原来的所有value也会被删除**


## 5. 存储List(了解)
在Redis中,list类型是按照插入顺序排序的字符串链表。我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该key不存在,Redis将为该key创建一个新的链表。与此相反,如果链表中的所有元素都被删除了,那么该key也将会被从数据库中删除。list中可以包含的最大元素数据量4294967295(十亿以上)。<br />List接口<br />
ArrayList<br />
LinkedList,redis中的list就类似于java中的LinkList
### 5.1. 两端添加

- `lpush key value1 value2……`:在指定的key对应的list的头部插入所有的value,如果该key不存在,该命令在插入之前创建一个与该key对应的空链表,再从头部插入数据。插入成功,返回元素的个数。
- `rpush key value1 value2……`:在指定的key对应的list的尾部插入所有的value,如果该key不存在,该命令在插入之前创建一个与该key对应的空链表,再从尾部插入数据。插入成功,返回元素的个数。
### 5.2. 查看列表

- `lrange key start end`:获取链表中从start到end的元素的值,start和end从0开始计数,如果为负数,-1表示倒数第一个元素,-2表示倒数第二个元素,以此类推。
- 查看所有列表:(0~-1就可以查看所有值)。例:`lrange key 0 -1`

![1](https://gitee.com/moonzero/images/raw/master/code-note/20190511095521734_6959.jpg)
### 5.3. 两边弹出

- `lpop key`:返回并弹出指定的key对应链表中头部(left)第一个元素,如果该key不存在,返回nil。
- `rpop key`:返回并弹出指定的key对应链表中尾部(right)第一个元素,如果该key不存在,返回nil。
### 5.4. 获取列表中元素的个数
`llen key`:返回指定key对应链表中元素的个数,l代表list,len代表length
## 6. 存储set(了解)
Redis中,我们可以将set类型看作是没有排序的字符集合,set中可以包含的最大元素数据量4294967295(十亿以上)。<br />**和list不同,set集合不允许出现重复元素,如果多次添加相同元素,set中仅保留一份。**
### 6.1. 添加/删除元素

- `sadd key value1 value2……`:向set中添加数据,如果该key的值已存在,则不会重复添加,返回添加成功个数
- `srem key value1 value2……`:删除set中指定的成员,返回删除成功个数
### 6.2. 获得集体中的元素
`smembers key`:获取set集合中所有元素

- 直接删除key,那么key对应的list-set-sortedset都会删除;
- 如果key对应的所有值删除了,那么key也会自动被删除
### 6.3. 判断元素是否在集合中存在

- `sismember key value`:判断key中指定的元素是否在该set集合中存在。存在则返回1,不存在则返回0
## 7. 存储sortedset
sortedset和set类型极为类似,它们都是字符串的集合,都不允许重复的元素出现在一个set中。它们之间的主要区别是**sortedset中每一个元素都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的元素进行从小到大的排序(默认)。**<br />**sortedset集合中的元素必须是唯一的,但分数(score)却是可以重复。**
### 7.1. 添加元素

- `zadd key score value score value score value`
- 将所有元素以及对应的分数,存放到sortedset集合中,如果该元素已存在则会用新的分数替换原来的分数。
- 返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。


### 7.2. 查询元素(从小到大)

- `zrange key start end`
- 获取集合中下标为start到end的元素,不带分数排序之后的sortedSet与list的位置是一样,位置从左到右是正数,从0开始,位置从右到左是负数,从-1开始,-1是倒数第一个元素,-2倒数第二个元素


- `zrange key start end withscores`
- 获取集合中下标为start到end的元素,带分数按分数从小到大排序
- 如果相同用户的话,不会再将用户名插入集合中,但分数可以替换原来的分数


### 7.3. 查询元素(从大到小)

- `zrevrange key start end`:按照元素分数从大到小,获取集合中下标为start到end的元素,不带分数
- `zrevrange key start end withscores`:按照元素分数从大到小,获取集合中下标为start到end的元素,带分数
### 7.4. 获取元素分数
`zscore key member`:返回指定元素的分数
### 7.5. 获取元素数量
`zcard key`:获取集合中元素数量
### 7.6. 删除元素
`zrem key member member member`:从集合中删除指定的元素
### 7.7. 按照分数范围删除元素
`zremrangebyscore key min max`:按照分数范围删除元素
## 8. redis持久化
### 8.1. 概述
Redis的高性能是由于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。<br />Redis支持两种方式的持久化机制,**RDB方式与AOF方式**。可以单独使用其中一种或将二者结合使用。

1. RDB持久化(默认支持,无需配置):该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘。
1. AOF持久化:该机制将以日志的形式记录服务器所处理的每一个写操作,在Redis服务器启动之初会读取该文件来重新构建数据库,以保证启动后数据库中的数据是完整的。
1. 无持久化:我们可以通过配置的方式禁用Redis服务器的持久化功能,这样我们就可以将Redis视为一个功能加强版的memcached了。
### 8.2. RDB
RDB 持久化把当前进程数据生成快照(.rdb)文件保存到硬盘的过程,有手动触发和自动触发

- 优势
1. 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
1. 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上
1. 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork(分叉)出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
1. 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。


- 劣势
1. 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
1. 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟


#### 8.2.1. 手动触发
手动触发有 `save` 和 `bgsave` 两命令

- `save` 命令:阻塞当前 Redis,直到 RDB 持久化过程完成为止,若内存实例比较大会造成长时间阻塞,线上环境不建议用它
- `bgsave` 命令:redis 进程执行 fork 操作创建子线程,由子线程完成持久化,阻塞时间很短(微秒级),是 save 的优化,在执行 `redis-cli shutdown` 关闭 redis 服务时,如果没有开<br />
启 AOF 持久化,会自动执行 bgsave。显然 bgsave 是对 save 的优化。

bgsave 运行流程<br />![bgsave 运行流程图](https://gitee.com/moonzero/images/raw/master/code-note/20191114135226267_18687.png)
#### 8.2.2. 配置自动触发

- 修改配置文件redis.conf,配置快照参数

save 900 1 # 每900秒(15分钟)至少有1个key发生变化,则dump内存快照。 save 300 10 # 每300秒(5分钟)至少有10个key发生变化,则dump内存快照 save 60 10000 # 每60秒(1分钟)至少有10000个key发生变化,则dump内存快照

![1](https://gitee.com/moonzero/images/raw/master/code-note/20190511102807351_18651.jpg)<br />![2](https://gitee.com/moonzero/images/raw/master/code-note/20190511102821561_20719.jpg)

- 设置保存位置设置

![3](https://gitee.com/moonzero/images/raw/master/code-note/20190511102832208_16601.jpg)<br />![4](https://gitee.com/moonzero/images/raw/master/code-note/20190511102837561_26197.jpg)
#### 8.2.3. RDB 文件的操作

- 命令:`config set dir /usr/local`
- 设置 rdb 文件保存路径


- 备份:`bgsave`
- 将 dump.rdb 保存到 usr/local 下


- 恢复:将 dump.rdb 放到 redis 安装目录与 redis.conf 同级目录,重启 redis 即可
- 优点:
1. 压缩后的二进制文,适用于备份、全量复制,用于灾难恢复
1. 加载 RDB 恢复数据远快于 AOF 方式


- 缺点:
1. 无法做到实时持久化,每次都要创建子进程,频繁操作成本过高
1. 保存后的二进制文件,存在老版本不兼容新版本 rdb 文件的问题


### 8.3. AOF
针对 RDB 不适合实时持久化,redis 提供了 AOF 持久化方式来解决

- 优势
1. 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即**每秒同步、每修改同步和不同步**。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。
1. 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。
1. 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。
1. AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。


- 劣势
1. 对于相同数量的数据集而言,AOF文件通常要大于RDB文件
1. 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。


#### 8.3.1. redis 的 AOF 配置详解

appendonly yes # 启用 aof 持久化方式

appendfsync always # 每收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用

appendfsync everysec # 每秒强制写入磁盘一次,性能和持久化方面做了折中,推荐

appendfsync no # 完全依赖 os,性能最好,持久化没保证(操作系统自身的同步)

no-appendfsync-on-rewrite yes # 正在导出 rdb 快照的过程中,要不要停止同步 aof auto-aof-rewrite-percentage 100 # aof 文件大小比起上次重写时的大小,增长率100%时,重写 auto-aof-rewrite-min-size 64mb # aof 文件,至少超过 64M 时,重写


- 修改 redis.conf 设置文件,修改`appendonly yes` (默认AOF处于关闭,为 no)

![1](https://gitee.com/moonzero/images/raw/master/code-note/20190511103409610_11558.jpg)

- 手动开启

![2](https://gitee.com/moonzero/images/raw/master/code-note/20190511103435927_11796.jpg)

- 策略的选择:

always #每次有数据修改发生时都会写入AOF文件 everysec #每秒钟同步一次,该策略为AOF的缺省策略 no #从不同步。高效但是数据不会被持久化

``` 3
4

  • 默认文件名:appendfilename “appendonly.aof”。可以修改为指定文件名称

5

8.3.2. AOF流程说明

  1. 所有的写入命令(set hset)会 append 追加到 aof_buf 缓冲区中
  2. AOF 缓冲区向硬盘做 sync 同步
  3. 随着 AOF 文件越来越大,需定期对 AOF 文件 rewrite 重写,达到压缩
  4. 当 redis 服务重启,可 load 加载 AOF 文件进行恢复

AOF持久化流程:命令写入(append),文件同步(sync),文件重写(rewrite),重启加载(load)
AOF持久化流程图

8.3.3. AOF 恢复

  1. 设置 appendonly yes
  2. 将 appendonly.aof 文件放到 dir 参数指定的目录
  3. 启动 Redis,Redis 会自动加载 appendonly.aof 文件

    8.4. redis 重启时恢复加载 AOF 与 RDB 顺序及流程

  4. 当 AOF 和 RDB 文件同时存在时,优先加载

  5. 若关闭了 AOF,加载 RDB 文件
  6. 加载 AOF/RDB 成功,redis 重启成功
  7. AOF/RDB 存在错误,redis 启动失败并打印错误信息

    9. Redis其他知识(了解)

    9.1. 服务器命令(自学)

  • ping,测试连接是否存活
  • 执行下面命令之前,我们停止redis 服务器
  • redis 127.0.0.1:6379> ping
  • Could not connect to Redis at 127.0.0.1:6379: Connection refused
  • echo,在命令行打印一些内容
  • select,选择数据库。
  • Redis 数据库编号从0~15,可以选择任意一个数据库来进行数据的存取。
  • 当选择16 时,报错,说明没有编号为16 的这个数据库
  • quit,退出连接。
  • dbsize,返回当前数据库中key 的数目。
  • info,获取服务器的信息和统计。
  • flushdb,删除当前选择数据库中的所有key。
  • flushall,删除所有数据库中的所有key。

    9.2. 消息订阅与发布

  • 命令:subscribe channel

  • 订阅频道,例:subscribe mychat,订阅mychat这个频道
  • 命令:psubscribe channel*
  • 批量订阅频道,例:psubscribe s*,订阅以”s”开头的频道
  • 命令:publish channel content
  • 在指定的频道中发布消息,如 publish mychat ‘today is a newday’

其他详见day50笔记

9.3. redis事务

  • redis事务特征
  • 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务
  • 在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。
  • multi:开启事务用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子的执行,类似与关系型数据库中的:begin transaction
  • exec:提交事务,类似与关系型数据库中的:commit
  • discard:事务回滚,类似与关系型数据库中的:rollback

详见day50笔记