更新 redis扩展部分.md

This commit is contained in:
wxin 2025-03-14 19:46:26 +08:00
parent 92f73d5a89
commit 20fafa11d2

View File

@ -1,416 +1,421 @@
<h2><center>Redis 扩展部分</center></h2> <h2><center>Redis 扩展部分</center></h2>
------ ------
## 一:分布式锁简介 ## 一:分布式锁简介
### 1. 简介 ### 1. 简介
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁
分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
### 2. 分布式锁满足的条件 ### 2. 分布式锁满足的条件
- 可见性:多个线程都能看到相同的结果,注意:这个地方说的可见性并不是并发编程中指的内存可见性,只是说多个进程之间都能感知到的变化 - 可见性:多个线程都能看到相同的结果,注意:这个地方说的可见性并不是并发编程中指的内存可见性,只是说多个进程之间都能感知到的变化
- 互斥:互斥是分布式锁的最基本的条件,使得程序串行执行 - 互斥:互斥是分布式锁的最基本的条件,使得程序串行执行
- 高可用:程序不易崩溃,时时刻刻都保证较高的可用性 - 高可用:程序不易崩溃,时时刻刻都保证较高的可用性
- 高性能:由于加锁本身就让性能降低,所有对于分布式锁本身需要他就较高的加锁性能和释放锁性能 - 高性能:由于加锁本身就让性能降低,所有对于分布式锁本身需要他就较高的加锁性能和释放锁性能
- 安全性:安全也是程序中必不可少的一环 - 安全性:安全也是程序中必不可少的一环
### 3. 常见的分布式锁 ### 3. 常见的分布式锁
Mysql本身就带有锁机制但是由于mysql性能本身一般所以采用分布式锁的情况下其实使用mysql作为分布式锁比较少见 Mysql本身就带有锁机制但是由于mysql性能本身一般所以采用分布式锁的情况下其实使用mysql作为分布式锁比较少见
Redis作为分布式锁是非常常见的一种使用方式现在企业级开发中基本都使用redis或者zookeeper作为分布式锁利用setnx这个方法如果插入key成功则表示获得到了锁如果有人插入成功其他人插入失败则表示无法获得到锁利用这套逻辑来实现分布式锁 Redis作为分布式锁是非常常见的一种使用方式现在企业级开发中基本都使用redis或者zookeeper作为分布式锁利用setnx这个方法如果插入key成功则表示获得到了锁如果有人插入成功其他人插入失败则表示无法获得到锁利用这套逻辑来实现分布式锁
Zookeeper也是企业级开发中较好的一个实现分布式锁的方案 Zookeeper也是企业级开发中较好的一个实现分布式锁的方案
### 4. 设置分布式锁 ### 4. 设置分布式锁
```bash ```bash
[root@redis ~]# redis-cli
``` 127.0.0.1:6379> set name zhangsan NX EX 10
# 添加锁 NX是互斥的 EX设置超时时间
删除: 127.0.0.1:6379> setnx class cloud
# 使用SETNX创建互斥锁
```bash ```
``` 删除:
注意: ```bash
[root@redis ~]# redis-cli
- 在获取锁时加入过期时间;可以避免服务宕机,然后死锁 127.0.0.1:6379> del key
- 添加释放锁需要判断是否是当前线程,避免锁误删操作 ```
### 5. 核心思路 注意:
我们利用redis 的setNx方法当有多个线程进入时我们就利用该方法第一个线程进入时redis 中就有这个key 了返回了1如果结果是1则表示他抢到了锁那么他去执行业务然后再删除锁退出锁逻辑没有抢到锁的等待一定时间后重试即可 - 在获取锁时加入过期时间;可以避免服务宕机,然后死锁
- 添加释放锁需要判断是否是当前线程,避免锁误删操作
### 6. 锁的基本接口
### 5. 核心思路
我们利用redis 的setNx方法当有多个线程进入时我们就利用该方法第一个线程进入时redis 中就有这个key 了返回了1如果结果是1则表示他抢到了锁那么他去执行业务然后再删除锁退出锁逻辑没有抢到锁的等待一定时间后重试即可
## 二Redis 数据结构
### 6. 锁的基本接口
Redis支持的主要数据结构字符串、列表、哈希、集合、有序集合等
### 1. 字符集String
## 二Redis 数据结构
**描述:**
Redis支持的主要数据结构字符串、列表、哈希、集合、有序集合等
最简单的键值对类型值可以是字符串、整数或二进制数据最大512MB
### 1. 字符集String
**应用场景:**
**描述:**
- 缓存静态数据如HTML片段、用户会话
- 计数器(文章阅读量、点赞量)。 最简单的键值对类型值可以是字符串、整数或二进制数据最大512MB
- 分布式锁(结合 SET key value NX PX 实现)。
**应用场景:**
**常用命令:**
- 缓存静态数据如HTML片段、用户会话
```shell - 计数器(文章阅读量、点赞量)。
SET key value # 设置键值 - 分布式锁(结合 SET key value NX PX 实现)。
GET key # 获取值
INCR key # 值自增1原子性 **常用命令:**
DECR key # 值自减1
APPEND key value # 追加字符串 ```shell
STRLEN key # 获取字符串长度 SET key value # 设置键值
SETEX key seconds value # 设置值并指定过期时间(秒) GET key # 获取值
``` INCR key # 值自增1原子性
DECR key # 值自减1
例子: APPEND key value # 追加字符串
STRLEN key # 获取字符串长度
```bash SETEX key seconds value # 设置值并指定过期时间(秒)
[root@redis ~]# redis-cli ```
127.0.0.1:6379> set key value
OK 例子:
127.0.0.1:6379> get key
"value" ```bash
127.0.0.1:6379> exists key [root@redis ~]# redis-cli
(integer) 1 127.0.0.1:6379> set key value
127.0.0.1:6379> strlen key OK
(integer) 5 127.0.0.1:6379> get key
127.0.0.1:6379> del key "value"
(integer) 1 127.0.0.1:6379> exists key
127.0.0.1:6379> get key (integer) 1
(nil) 127.0.0.1:6379> strlen key
``` (integer) 5
127.0.0.1:6379> del key
(integer) 1
127.0.0.1:6379> get key
### 2. 列表List (nil)
```
**描述:**
有序的字符串集合,支持双向操作(类似链表),允许重复元素。
### 2. 列表List
**应用场景:**
**描述:**
- 消息队列LPUSH + BRPOP 实现阻塞队列)。
- 最新消息列表(如微博 Timeline 有序的字符串集合,支持双向操作(类似链表),允许重复元素。
- 记录用户操作历史保留最近N条记录
**应用场景:**
**常用命令:**
- 消息队列LPUSH + BRPOP 实现阻塞队列)。
```shell - 最新消息列表(如微博 Timeline
LPUSH key value1 [value2] # 左侧插入元素 - 记录用户操作历史保留最近N条记录
RPUSH key value1 [value2] # 右侧插入元素
LPOP key # 左侧弹出元素 **常用命令:**
RPOP key # 右侧弹出元素
LRANGE key start stop # 获取指定范围的元素 ```shell
LINDEX key index # 获取指定索引的元素 LPUSH key value1 [value2] # 左侧插入元素
LLEN key # 获取列表长度 RPUSH key value1 [value2] # 右侧插入元素
``` LPOP key # 左侧弹出元素
RPOP key # 右侧弹出元素
例子: LRANGE key start stop # 获取指定范围的元素
LINDEX key index # 获取指定索引的元素
```bash LLEN key # 获取列表长度
[root@redis ~]# redis-cli ```
127.0.0.1:6379> rpush myList value1
(integer) 1 例子:
127.0.0.1:6379> rpush myList value2 value3
(integer) 3 ```bash
127.0.0.1:6379> lpop myList [root@redis ~]# redis-cli
"value1" 127.0.0.1:6379> rpush myList value1
127.0.0.1:6379> lrange myList 0 1 (integer) 1
1) "value2" 127.0.0.1:6379> rpush myList value2 value3
2) "value3" (integer) 3
127.0.0.1:6379> lrange myList 0 -1 127.0.0.1:6379> lpop myList
1) "value2" "value1"
2) "value3" 127.0.0.1:6379> lrange myList 0 1
``` 1) "value2"
2) "value3"
127.0.0.1:6379> lrange myList 0 -1
1) "value2"
### 3. 希哈Hash 2) "value3"
```
**描述:**
键值对的集合,适合存储对象(如用户信息),支持字段级操作。
### 3. 希哈Hash
**应用场景:**
**描述:**
- 存储对象如用户信息user1 包含 nameage 等字段)。
- 聚合统计(如商品属性管理)。 键值对的集合,适合存储对象(如用户信息),支持字段级操作。
- 节省内存(相比 JSON 字符串存储,哈希支持按需存取字段)。
**应用场景:**
**常用命令:**
- 存储对象如用户信息user1 包含 nameage 等字段)。
```shell - 聚合统计(如商品属性管理)。
HSET key field value # 设置字段值 - 节省内存(相比 JSON 字符串存储,哈希支持按需存取字段)。
HGET key field # 获取字段值
HGETALL key # 获取所有字段和值 **常用命令:**
HDEL key field # 删除字段
HEXISTS key field # 判断字段是否存在 ```shell
HINCRBY key field increment # 字段值自增整数 HSET key field value # 设置字段值
``` HGET key field # 获取字段值
HGETALL key # 获取所有字段和值
例子: HDEL key field # 删除字段
HEXISTS key field # 判断字段是否存在
```bash HINCRBY key field increment # 字段值自增整数
[root@redis ~]# redis-cli ```
127.0.0.1:6379> hmset userInfoKey name "guide" description "dev" age 24
OK 例子:
127.0.0.1:6379> hexists userInfoKey name
(integer) 1 ```bash
127.0.0.1:6379> hget userInfoKey name [root@redis ~]# redis-cli
"guide" 127.0.0.1:6379> hmset userInfoKey name "guide" description "dev" age 24
127.0.0.1:6379> hget userInfoKey age OK
"24" 127.0.0.1:6379> hexists userInfoKey name
127.0.0.1:6379> hgetall userInfoKey (integer) 1
1) "name" 127.0.0.1:6379> hget userInfoKey name
2) "guide" "guide"
3) "description" 127.0.0.1:6379> hget userInfoKey age
4) "dev" "24"
5) "age" 127.0.0.1:6379> hgetall userInfoKey
6) "24" 1) "name"
127.0.0.1:6379> hset userInfoKey name "GuideGeGe" 2) "guide"
(integer) 0 3) "description"
127.0.0.1:6379> hget userInfoKey name 4) "dev"
"GuideGeGe" 5) "age"
127.0.0.1:6379> hincrby userInfoKey age 2 6) "24"
(integer) 26 127.0.0.1:6379> hset userInfoKey name "GuideGeGe"
(integer) 0
``` 127.0.0.1:6379> hget userInfoKey name
"GuideGeGe"
127.0.0.1:6379> hincrby userInfoKey age 2
(integer) 26
### 4. 集合Set
```
描述:
无序且唯一的字符串集合,支持交并差运算。
### 4. 集合Set
应用场景:
描述:
- 标签系统(如文章标签)。
- 唯一性控制(如抽奖用户去重)。 无序且唯一的字符串集合,支持交并差运算。
- 社交关系(共同关注、好友推荐)。
应用场景:
常用命令:
- 标签系统(如文章标签)。
```shell - 唯一性控制(如抽奖用户去重)。
SADD key member1 [member2] # 添加元素 - 社交关系(共同关注、好友推荐)。
SMEMBERS key # 获取所有元素(谨慎使用,可能阻塞)
SISMEMBER key member # 判断元素是否存在 常用命令:
SINTER key1 key2 # 计算多个集合的交集
SUNION key1 key2 # 计算并集 ```shell
SDIFF key1 key2 # 计算差集key1有但key2无 SADD key member1 [member2] # 添加元素
SPOP key # 随机弹出一个元素 SMEMBERS key # 获取所有元素(谨慎使用,可能阻塞)
``` SISMEMBER key member # 判断元素是否存在
SINTER key1 key2 # 计算多个集合的交集
例子: SUNION key1 key2 # 计算并集
SDIFF key1 key2 # 计算差集key1有但key2无
```bash SPOP key # 随机弹出一个元素
[root@redis ~]# redis-cli ```
127.0.0.1:6379> sadd mySet value1 value2
(integer) 2 例子:
127.0.0.1:6379> sadd mySet value1
(integer) 0 ```bash
127.0.0.1:6379> smembers mySet [root@redis ~]# redis-cli
1) "value1" 127.0.0.1:6379> sadd mySet value1 value2
2) "value2" (integer) 2
127.0.0.1:6379> scard mySet 127.0.0.1:6379> sadd mySet value1
(integer) 2 (integer) 0
127.0.0.1:6379> sismember mySet value1 127.0.0.1:6379> smembers mySet
(integer) 1 1) "value1"
127.0.0.1:6379> sadd mySet2 value2 value3 2) "value2"
(integer) 2 127.0.0.1:6379> scard mySet
``` (integer) 2
127.0.0.1:6379> sismember mySet value1
(integer) 1
127.0.0.1:6379> sadd mySet2 value2 value3
### 5. 有序集合Sorted Set / ZSet (integer) 2
```
**描述:**
元素唯一且按分数score排序的集合支持范围查询和排名。
### 5. 有序集合Sorted Set / ZSet
**应用场景:**
**描述:**
- 排行榜(如游戏积分排行)。
- 延时队列(用时间戳作为 score定时获取到期任务 元素唯一且按分数score排序的集合支持范围查询和排名。
- 范围查询(如按价格区间筛选商品)。
**应用场景:**
**常用命令:**
- 排行榜(如游戏积分排行)。
```bash - 延时队列(用时间戳作为 score定时获取到期任务
ZADD key score1 member1 [score2 member2] # 添加元素(带分数) - 范围查询(如按价格区间筛选商品)。
ZRANGE key start stop [WITHSCORES] # 按升序获取元素
ZREVRANGE key start stop [WITHSCORES] # 按降序获取元素 **常用命令:**
ZRANK key member # 获取元素升序排名
ZSCORE key member # 获取元素的分数 ```bash
ZRANGEBYSCORE key min max # 按分数范围查询 ZADD key score1 member1 [score2 member2] # 添加元素(带分数)
ZINCRBY key increment member # 增加元素的分数 ZRANGE key start stop [WITHSCORES] # 按升序获取元素
``` ZREVRANGE key start stop [WITHSCORES] # 按降序获取元素
ZRANK key member # 获取元素升序排名
例子: ZSCORE key member # 获取元素的分数
ZRANGEBYSCORE key min max # 按分数范围查询
```bash ZINCRBY key increment member # 增加元素的分数
[root@redis ~]# redis-cli ```
127.0.0.1:6379> zadd myZset 2.0 value1 1.0 value2
(integer) 2 例子:
127.0.0.1:6379> zcard myZset
(integer) 2 ```bash
127.0.0.1:6379> zscore myZset value1 [root@redis ~]# redis-cli
"2" 127.0.0.1:6379> zadd myZset 2.0 value1 1.0 value2
127.0.0.1:6379> zrange myZset 0 1 (integer) 2
1) "value2" 127.0.0.1:6379> zcard myZset
2) "value1" (integer) 2
127.0.0.1:6379> zrevrange myZset 0 1 127.0.0.1:6379> zscore myZset value1
1) "value1" "2"
2) "value2" 127.0.0.1:6379> zrange myZset 0 1
127.0.0.1:6379> zadd myZset2 4.0 value2 3.0 value3 1) "value2"
(integer) 2 2) "value1"
``` 127.0.0.1:6379> zrevrange myZset 0 1
1) "value1"
2) "value2"
127.0.0.1:6379> zadd myZset2 4.0 value2 3.0 value3
## 三Redis 事务处理 (integer) 2
```
Redis 的事务处理机制与关系型数据库如MySQL的ACID事务有所不同它通过 MULTI 、EXEC、DISCARD、WATCH等命令实现提供了一种弱事务性保证。
### 1. Redis 事务核心命令
## 三Redis 事务处理
```shell
MULTI # 标记事务开始后续命令会被放入列表等待EXEC执行 Redis 的事务处理机制与关系型数据库如MySQL的ACID事务有所不同它通过 MULTI 、EXEC、DISCARD、WATCH等命令实现提供了一种弱事务性保证。
EXEC # 执行事务队列中的所有命令
DISCARD # 取消事务,清空队列中的命令 ### 1. Redis 事务核心命令
WATCH # 监视一个或多个键,若事务执行前这些键被修改,则事务终止(乐观锁机制)
UNWATCH # 取消对所有键的监视 ```shell
``` MULTI # 标记事务开始后续命令会被放入列表等待EXEC执行
EXEC # 执行事务队列中的所有命令
### 2. 事务执行流程 DISCARD # 取消事务,清空队列中的命令
WATCH # 监视一个或多个键,若事务执行前这些键被修改,则事务终止(乐观锁机制)
基本事务(无 WATCH UNWATCH # 取消对所有键的监视
```
```bash
# 开启事务 ### 2. 事务执行流程
> MULTI
OK 基本事务(无 WATCH
# 命令入队(不立即执行) ```bash
> SET key1 "value1" # 开启事务
QUEUED > MULTI
> INCR key2 OK
QUEUED
> GET key1 # 命令入队(不立即执行)
QUEUED > SET key1 "value1"
QUEUED
# 执行事务(原子性) > INCR key2
> EXEC QUEUED
1) OK # SET 结果 > GET key1
2) (integer) 5 # INCR 结果 QUEUED
3) "value1" # GET 结果
``` # 执行事务(原子性)
> EXEC
使用 WATCH 实现乐观锁 1) OK # SET 结果
2) (integer) 5 # INCR 结果
```bash 3) "value1" # GET 结果
# 监视键 balance ```
> WATCH balance
OK 使用 WATCH 实现乐观锁
# 获取当前余额 ```bash
> GET balance # 监视键 balance
"100" > WATCH balance
OK
# 开启事务
> MULTI # 获取当前余额
OK > GET balance
"100"
# 扣减余额
> DECRBY balance 20 # 开启事务
QUEUED > MULTI
OK
# 执行事务(若 balance 未被其他客户端修改,则成功)
> EXEC # 扣减余额
1) (integer) 80 # 执行成功 > DECRBY balance 20
QUEUED
# 若 balance 被其他客户端修改EXEC 返回 nil事务终止
> EXEC # 执行事务(若 balance 未被其他客户端修改,则成功)
(nil) > EXEC
``` 1) (integer) 80 # 执行成功
### 3. Redis 事务的特性 # 若 balance 被其他客户端修改EXEC 返回 nil事务终止
> EXEC
**原子性:** (nil)
```
- 事务队列中的命令要么全部执行,要么全部不执行(通过 EXEC 触发原子性)。
- 注意Redis 事务不支持回滚Rollback。即使某个命令执行失败其他命令仍会继续执行。 ### 3. Redis 事务的特性
**隔离性:** **原子性:**
- 事务中的命令在 EXEC 前不会实际执行,因此其他客户端无法看到中间状态。 - 事务队列中的命令要么全部执行,要么全部不执行(通过 EXEC 触发原子性)。
- 无脏读 / 不可重复读问题,但可能存在竞态条件(需配合 WATCH 解决)。 - 注意Redis 事务不支持回滚Rollback。即使某个命令执行失败其他命令仍会继续执行。
**错误处理:** **隔离性:**
- 入队错误如对字符串执行INCR事务提交时EXEC会拒绝整个事务。 - 事务中的命令在 EXEC 前不会实际执行,因此其他客户端无法看到中间状态。
- 无脏读 / 不可重复读问题,但可能存在竞态条件(需配合 WATCH 解决)。
```shell
> MULTI **错误处理:**
OK
> SET key1 "value1" - 入队错误如对字符串执行INCR事务提交时EXEC会拒绝整个事务。
QUEUED
> INVALID_COMMAND # 错误命令 ```shell
(error) ERR unknown command 'INVALID_COMMAND' > MULTI
> EXEC OK
(error) EXECABORT Transaction discarded because of previous errors. > SET key1 "value1"
``` QUEUED
> INVALID_COMMAND # 错误命令
(error) ERR unknown command 'INVALID_COMMAND'
> EXEC
- 执行错误():仅错误命令失败,其他命令继续执行。 (error) EXECABORT Transaction discarded because of previous errors.
```
```shell
> SET key3 "abc"
OK
> MULTI - 执行错误():仅错误命令失败,其他命令继续执行。
OK
> INCR key3 # 执行时会失败 ```shell
QUEUED > SET key3 "abc"
> SET key4 "value4" OK
QUEUED > MULTI
> EXEC OK
1) (error) ERR value is not an integer or out of range > INCR key3 # 执行时会失败
2) OK # SET key4 成功执行 QUEUED
``` > SET key4 "value4"
QUEUED
> EXEC
1) (error) ERR value is not an integer or out of range
### 4. 事务使用场景 2) OK # SET key4 成功执行
```
批量操作原子性:如批量更新用户状态、清理缓存键。
简单事务性逻辑:如转账操作(需配合 WATCH 监控余额)。
### 4. 事务使用场景
避免中间状态暴露:在 MULTI 和 EXEC 之间执行的命令不会立即生效。
批量操作原子性:如批量更新用户状态、清理缓存键。
### 5. 注意事项
简单事务性逻辑:如转账操作(需配合 WATCH 监控余额)。
- 避免事务中包含耗时操作:事务执行期间会阻塞其他客户端命令,影响性能。
- 合理使用 WATCH过度使用 `WATCH` 可能导致频繁事务失败,需结合重试机制。 避免中间状态暴露:在 MULTI 和 EXEC 之间执行的命令不会立即生效。
- 优先使用 Lua 脚本对于复杂逻辑或需严格原子性的场景Lua 脚本更可靠。
### 5. 注意事项
- 避免事务中包含耗时操作:事务执行期间会阻塞其他客户端命令,影响性能。
- 合理使用 WATCH过度使用 `WATCH` 可能导致频繁事务失败,需结合重试机制。
- 优先使用 Lua 脚本对于复杂逻辑或需严格原子性的场景Lua 脚本更可靠。
- 事务不支持回滚:需在应用层处理部分失败的情况(如日志补偿)。 - 事务不支持回滚:需在应用层处理部分失败的情况(如日志补偿)。