# 2.2 针对字符串的命令

Redis是基于"键值对"的NoSQL.而此处的"string"是指"键值对"中的"值"是以"string"形式存储数据的.

## 2.2.1 读写字符串的set和get命令

设置字符串对象的语法:

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

其中:

* `key`: 键名.若对应的key中已经有值,那么再次执行`SET`命令时会用新的value替换旧的value
* `value`: **字符串类型的**值
* `EX/PX`: 设置生存周期.其中`EX`的单位为秒;`PX`的单位为毫秒
* `NX`: 当`key`不存在时才进行设置值的操作,若key存在则该命令不执行
* `XX`: 与`NX`相反,表示当key存在时才进行操作
* `KEEPTTL`: 在设置新的键值对时,保持原有的TTL(Time To Live,生存时间)不变.
  * 该选项为Redis 6.0新增选项.当使用`SET`命令对一个已经存在的key设置value时,若该key已经存在生存周期,则新的`SET`命令会移除这个生存周期.此时加上`KEEPTTL`选项,则表示保持该key原有的生存周期不变.

```
127.0.0.1:6379> SET name foo ex 5
OK
127.0.0.1:6379> GET name bar keepttl
OK
127.0.0.1:6379> SET name
"bar"
```

需求:工号为001的员工姓名为Mike,这条数据是存储在Emp表里的,但是每次查询该数据时需要读表,这会影响数据库的性能,所以需要在Redis里缓存该条数据.

设置了错误的员工姓名:

```
127.0.0.1:6379> SET 001 'Mary'
OK
```

注意:此处的`'Mary'`带有`'`,表示设置的值类型为字符串

此处的返回值`OK`表示设置成功

尝试将姓名修改为Mike:

```
127.0.0.1:6379> SET 001 'Mike' NX
(nil)
```

此处的返回值`nil`表示设置不成功

```
127.0.0.1:6379> SETNX 001 'Mike'
(integer) 0
```

此处的`setNX key value`和`set key value NX`是等价的.返回值`(integer) 0`同样表示设置不成功

查看`key`为`001`的值:

```
127.0.0.1:6379> GET 001
"Mary"
```

将`key`为`001`的值替换为Mike:

```
127.0.0.1:6379> SET 001 'Mike'
OK
```

查看结果:

```
127.0.0.1:6379> GET 001
"Mike"
```

例:将工号为002、姓名为Tom的数据写入Redis:

确认key是否存在:

```
127.0.0.1:6379> GET 002
(nil)
```

此处返回值`nil`表示没有找到key为`002`的数据

当key为`002`的数据存在时设置其值为`'Tom'`:

```
127.0.0.1:6379> SET 002 'Tom' XX
(nil)
```

此处由于key为`002`的数据不存在,故不进行操作,该`SET`命令返回值为`nil`表示没有进行操作

设置key为002的value为`'Tom'`,生存周期为10ms:

```
127.0.0.1:6379> SET 002 'Tom' PX 10
OK
```

10ms后获取key为002的value:

```
127.0.0.1:6379> GET 002
(nil)
```

此处返回`nil`表示没有获取到对应的值

设置key为002的value为`'Tom'`,生存周期为12分钟:

```
127.0.0.1:6379> SET 002 'Tom' EX 60*12
(error) ERR value is not an integer or out of range
```

报错原因:生存周期必须为一个数值,不能是一个表达式

```
127.0.0.1:6379> SET 002 'Tom' EX 720
OK
```

可以看到,值设置成功.

查看结果:

```
127.0.0.1:6379> GET 002
"Tom"
```

## 2.2.2 设置和获取多个字符串的命令

* `MSET`:设置多个字符串
  * 语法:`MSET key value [key value]`
* `MGET`:获取多个字符串
  * 语法:`MGET key [key]`

注意:`MSET`、`MGET`命令不包含`NX`、`XX`、`EX`、`PX`等参数

例:

同时对`003`和`004`这2个key设置string类型的值:

```
127.0.0.1:6379> MSET 003 'Peter' 004 Mary EX 10
OK
```

注:此处虽然使用`EX`参数设置了生命周期,但实际上这个生命周期不会生效

注:可以看到,字符串可以用`"`或`'`包含,也可以不包含.效果相同

10秒后使用`MGET`命令获取key为003和004的value:

```
127.0.0.1:6379> MGET 003 004
1) "Peter"
2) "Mary"
```

可以看到,虽然使用`MSET`命令设置了生命周期为10s,但并未生效.10s后依旧能通过key取到value

使用`MSET`指令时同时指定`NX`或`XX`参数:

```
127.0.0.1:6379> MSET 003 "Peter" 006 'JohnSon' NX
(error) ERR wrong number of arguments for MSET
```

```
127.0.0.1:6379> MSET 003 "Peter" 006 'JohnSon' XX
(error) ERR wrong number of arguments for MSET
```

可以看到,`MSET`命令不支持`NX`和`XX`参数

同时对`007`和`008`这2个key设置string类型的值:

```
127.0.0.1:6379> MSET 007 "John" 008 'Tim' PX 10
OK
```

注:此处虽然使用`PX`参数设置了生命周期,但实际上这个生命周期不会生效

10ms后使用`MGET`命令获取key为`007`和`008`的value:

```
127.0.0.1:6379> MGET 007 008
1) "John"
2) "Tim"
```

可以看到,虽然使用`MSET`命令时通过`PX`选项设置了生命周期为10ms,但并未生效.10ms后依旧能通过key取到value.**即:`PX`和`EX`参数不会生效**

## 2.2.3 对值进行增量和减量操作

* `INCR key`:对key所对应的**数字类型值**进行加1操作
* `DECR key`:对key所对应的**数字类型值**进行减1操作
* `INCRBY key increment`:对key对应的值进行加increment的操作
* `DECRBY key decrement`:对key对应的值进行减decrement的操作

```
127.0.0.1:6379> GET visit
(nil)
```

对visit变量进行加1操作:

```
127.0.0.1:6379> INCR visit
(integer) 1
```

对visit进行加10操作:

```
127.0.0.1:6379> INCRBY visit 10
(integer) 11
```

对visit进行减1操作:

```
127.0.0.1:6379> DECR visit
(integer) 10
```

对visit进行减5操作:

```
127.0.0.1:6379> DECRBY visit 5
(integer) 5
```

```
127.0.0.1:6379> GET visit
"5"
```

将`INCR`命令和`DECR`命令作用在字符串类型上:

```
127.0.0.1:6379> SET visitPersion 'Peter'
OK
127.0.0.1:6379> INCR visitPersion
(error) ERR value is not an integer or out of range
127.0.0.1:6379> DECR visitPersion
(error) ERR value is not an integer or out of range
```

可以看到,**将`INCR`命令和`DECR`命令作用在字符串类型上会报错**.

## 2.2.4 通过getset命令设置新值

`GETSET`:若key对应的值存在,则用给定的值覆盖旧的值,同时返回旧的值;若key对应的值不存在,也会设置值,但会返回`nil`.语法:`GETSET key value`

```
127.0.0.1:6379> GETSET 009 'Alex'
(nil)
```

```
127.0.0.1:6379> GET 009
"Alex"
```

```
127.0.0.1:6379> GETSET 009 'Frank'
"Alex"
```

```
127.0.0.1:6379> GET 009
"Frank"
```

## 2.2.5 针对字符串的其他操作

* `GETRANGE`:
  * 语法:`GETRANGE key start end`
  * 功能:获取key的子字符串.返回key对应的值从start位置开始到end位置为止的子字符串.其中位置的计算从0开始.返回的子字符串包含start位置和end位置

例:

```
127.0.0.1:6379> SET tel 021-12345678
OK
```

```
127.0.0.1:6379> GETRANGE tel 4 12
"12345678"
```

* `SETRANGE`:
  * 语法:`SETRANGE key offset value`
  * 功能:从offset位置开始,把值替换为value.该命令的返回值是字符串的长度

例:

```
127.0.0.1:6379> GET tel
"021-12345678"
```

```
127.0.0.1:6379> SETRANGE tel 4 87654321
(integer) 12
```

```
127.0.0.1:6379> GET tel
"021-87654321"
```

注:若offset超出了字符串的长度,则会用空白字符(`\x00`)进行填充,填充至到达指定的偏移量后再进行替换.这个过程可能会导致字符串长度的增加.

```
127.0.0.1:6379> SETRANGE tel 15 -2468
(integer) 20
```

```
127.0.0.1:6379> GET tel
"021-87654321\x00\x00\x00-2468"
```

* `STRLEN`:
  * 语法:`STRLEN key`
  * 功能:返回字符串的长度

```
127.0.0.1:6379> STRLEN tel
(integer) 20
```

* `APPEND`
  * 语法:`APPEND key value`
  * 功能:将value追加到原值的末尾.该命令的返回值是追加后的字符串的长度

```
127.0.0.1:6379> GET tel
"021-87654321\x00\x00\x00-2468"
```

```
127.0.0.1:6379> GET tel
"021-87654321\x00\x00\x00-2468-3579"
```

注:若对一个不存在的key使用`APPEND`指令,则等价于`SET`指令

```
127.0.0.1:6379> GET keym
(nil)
```

```
127.0.0.1:6379> APPEND keym content
(integer) 7
```

```
127.0.0.1:6379> GET keym
"content"
```
