redis/redis扩展部分.md
2025-03-15 10:55:01 +08:00

11 KiB
Raw Permalink Blame History

Redis 扩展部分


一:分布式锁简介

1. 简介

分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁

分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路

2. 分布式锁满足的条件

  • 可见性:多个线程都能看到相同的结果,注意:这个地方说的可见性并不是并发编程中指的内存可见性,只是说多个进程之间都能感知到的变化
  • 互斥:互斥是分布式锁的最基本的条件,使得程序串行执行
  • 高可用:程序不易崩溃,时时刻刻都保证较高的可用性
  • 高性能:由于加锁本身就让性能降低,所有对于分布式锁本身需要他就较高的加锁性能和释放锁性能
  • 安全性:安全也是程序中必不可少的一环

3. 常见的分布式锁

Mysql本身就带有锁机制但是由于mysql性能本身一般所以采用分布式锁的情况下其实使用mysql作为分布式锁比较少见

Redis作为分布式锁是非常常见的一种使用方式现在企业级开发中基本都使用redis或者zookeeper作为分布式锁利用setnx这个方法如果插入key成功则表示获得到了锁如果有人插入成功其他人插入失败则表示无法获得到锁利用这套逻辑来实现分布式锁

Zookeeper也是企业级开发中较好的一个实现分布式锁的方案

4. 设置分布式锁

[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创建互斥锁

删除:

[root@redis ~]# redis-cli
127.0.0.1:6379> del key

注意:

  • 在获取锁时加入过期时间;可以避免服务宕机,然后死锁
  • 添加释放锁需要判断是否是当前线程,避免锁误删操作

5. 核心思路

我们利用redis 的setNx方法当有多个线程进入时我们就利用该方法第一个线程进入时redis 中就有这个key 了返回了1如果结果是1则表示他抢到了锁那么他去执行业务然后再删除锁退出锁逻辑没有抢到锁的等待一定时间后重试即可

6. 锁的基本接口

Redis 数据结构

Redis支持的主要数据结构字符串、列表、哈希、集合、有序集合等

1. 字符集String

描述:

最简单的键值对类型值可以是字符串、整数或二进制数据最大512MB

应用场景:

  • 缓存静态数据如HTML片段、用户会话
  • 计数器(文章阅读量、点赞量)。
  • 分布式锁(结合 SET key value NX PX 实现)。

常用命令:

SET key value         # 设置键值
GET key               # 获取值
INCR key              # 值自增1原子性
DECR key              # 值自减1
APPEND key value      # 追加字符串
STRLEN key            # 获取字符串长度
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"
127.0.0.1:6379> exists key
(integer) 1
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
(nil)

2. 列表List

描述:

有序的字符串集合,支持双向操作(类似链表),允许重复元素。

应用场景:

  • 消息队列LPUSH + BRPOP 实现阻塞队列)。
  • 最新消息列表(如微博 Timeline
  • 记录用户操作历史保留最近N条记录

常用命令:

LPUSH key value1 [value2]    # 左侧插入元素
RPUSH key value1 [value2]    # 右侧插入元素
LPOP key                     # 左侧弹出元素
RPOP key                     # 右侧弹出元素
LRANGE key start stop        # 获取指定范围的元素
LINDEX key index             # 获取指定索引的元素
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
127.0.0.1:6379> lpop myList
"value1"
127.0.0.1:6379> lrange myList 0 1
1) "value2"
2) "value3"
127.0.0.1:6379> lrange myList 0 -1
1) "value2"
2) "value3"

3. 希哈Hash

描述:

键值对的集合,适合存储对象(如用户信息),支持字段级操作。

应用场景:

  • 存储对象如用户信息user1 包含 nameage 等字段)。
  • 聚合统计(如商品属性管理)。
  • 节省内存(相比 JSON 字符串存储,哈希支持按需存取字段)。

常用命令:

HSET key field value      # 设置字段值
HGET key field            # 获取字段值
HGETALL key               # 获取所有字段和值
HDEL key field            # 删除字段
HEXISTS key field         # 判断字段是否存在
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
127.0.0.1:6379> hget userInfoKey name
"guide"
127.0.0.1:6379> hget userInfoKey age
"24"
127.0.0.1:6379> hgetall userInfoKey
1) "name"
2) "guide"
3) "description"
4) "dev"
5) "age"
6) "24"
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

描述:

无序且唯一的字符串集合,支持交并差运算。

应用场景:

  • 标签系统(如文章标签)。
  • 唯一性控制(如抽奖用户去重)。
  • 社交关系(共同关注、好友推荐)。

常用命令:

SADD key member1 [member2]   # 添加元素
SMEMBERS key                 # 获取所有元素(谨慎使用,可能阻塞)
SISMEMBER key member         # 判断元素是否存在
SINTER key1 key2             # 计算多个集合的交集
SUNION key1 key2             # 计算并集
SDIFF key1 key2              # 计算差集key1有但key2无
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
127.0.0.1:6379> smembers mySet
1) "value1"
2) "value2"
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
(integer) 2

5. 有序集合Sorted Set / ZSet

描述:

元素唯一且按分数score排序的集合支持范围查询和排名。

应用场景:

  • 排行榜(如游戏积分排行)。
  • 延时队列(用时间戳作为 score定时获取到期任务
  • 范围查询(如按价格区间筛选商品)。

常用命令:

ZADD key score1 member1 [score2 member2]  # 添加元素(带分数)
ZRANGE key start stop [WITHSCORES]        # 按升序获取元素
ZREVRANGE key start stop [WITHSCORES]     # 按降序获取元素
ZRANK key member                          # 获取元素升序排名
ZSCORE key member                         # 获取元素的分数
ZRANGEBYSCORE key min max                 # 按分数范围查询
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
127.0.0.1:6379> zscore myZset value1
"2"
127.0.0.1:6379> zrange myZset 0 1
1) "value2"
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
(integer) 2

Redis 事务处理

Redis 的事务处理机制与关系型数据库如MySQL的ACID事务有所不同它通过 MULTI 、EXEC、DISCARD、WATCH等命令实现提供了一种弱事务性保证。

1. Redis 事务核心命令

MULTI         # 标记事务开始后续命令会被放入列表等待EXEC执行
EXEC          # 执行事务队列中的所有命令
DISCARD       # 取消事务,清空队列中的命令
WATCH         # 监视一个或多个键,若事务执行前这些键被修改,则事务终止(乐观锁机制)
UNWATCH       # 取消对所有键的监视

2. 事务执行流程

基本事务(无 WATCH

# 开启事务
> MULTI
OK

# 命令入队(不立即执行)
> SET key1 "value1"
QUEUED
> INCR key2
QUEUED
> GET key1
QUEUED

# 执行事务(原子性)
> EXEC
1) OK          # SET 结果
2) (integer) 5 # INCR 结果
3) "value1"    # GET 结果

使用 WATCH 实现乐观锁

# 监视键 balance
> WATCH balance
OK

# 获取当前余额
> GET balance
"100"

# 开启事务
> MULTI
OK

# 扣减余额
> DECRBY balance 20
QUEUED

# 执行事务(若 balance 未被其他客户端修改,则成功)
> EXEC
1) (integer) 80  # 执行成功

# 若 balance 被其他客户端修改EXEC 返回 nil事务终止
> EXEC
(nil)

3. Redis 事务的特性

原子性:

  • 事务队列中的命令要么全部执行,要么全部不执行(通过 EXEC 触发原子性)。
  • 注意Redis 事务不支持回滚Rollback。即使某个命令执行失败其他命令仍会继续执行。

隔离性:

  • 事务中的命令在 EXEC 前不会实际执行,因此其他客户端无法看到中间状态。
  • 无脏读 / 不可重复读问题,但可能存在竞态条件(需配合 WATCH 解决)。

错误处理:

  • 入队错误如对字符串执行INCR事务提交时EXEC会拒绝整个事务。

    > MULTI
    OK
    > SET key1 "value1"
    QUEUED
    > INVALID_COMMAND   # 错误命令
    (error) ERR unknown command 'INVALID_COMMAND'
    > EXEC
    (error) EXECABORT Transaction discarded because of previous errors.
    
  • 执行错误():仅错误命令失败,其他命令继续执行。

    > SET key3 "abc"
    OK
    > MULTI
    OK
    > INCR key3        # 执行时会失败
    QUEUED
    > SET key4 "value4"
    QUEUED
    > EXEC
    1) (error) ERR value is not an integer or out of range
    2) OK              # SET key4 成功执行
    

4. 事务使用场景

批量操作原子性:如批量更新用户状态、清理缓存键。

简单事务性逻辑:如转账操作(需配合 WATCH 监控余额)。

避免中间状态暴露:在 MULTI 和 EXEC 之间执行的命令不会立即生效。

5. 注意事项

  • 避免事务中包含耗时操作:事务执行期间会阻塞其他客户端命令,影响性能。
  • 合理使用 WATCH过度使用 WATCH 可能导致频繁事务失败,需结合重试机制。
  • 优先使用 Lua 脚本对于复杂逻辑或需严格原子性的场景Lua 脚本更可靠。
  • 事务不支持回滚:需在应用层处理部分失败的情况(如日志补偿)。