Redis01-redis基础
1、redis概述
Redis(Remote Dictionary Server)是一个开源的、基于内存的键值存储系统,它可以用作数据库、缓存和消息代理。Redis 支持多种数据结构,包括字符串(Strings)、哈希(Hashes)、列表(Lists)、集合(Sets)、有序集合(Sorted Sets)等。
1.1、redis的主要特点
- 数据主要存储在内存中,提供极高的读写性能
- 支持RDB快照和AOF日志两种持久化方式
- 有丰富的数据结构
- 所有操作都是原子性的
- 支持消息发布订阅模式
- 通过Redis Sentinel和Redis Cluster支持高可用和分布式
1.2、redis实际应用场景
- 作为缓存系统,用于减轻数据库压力,加速应用响应。例如:网页缓存、数据库查询结果缓存、会话(Session)缓存、API响应缓存
- 排行榜/计数器,通过Redis的有序集合(ZSET)实现。例如:排行榜、实时计数(点赞数、浏览量等)
- 消息队列,通过Redis的列表(List)或发布/订阅功能实现
- 分布式锁,通过Redis的原子操作实现。例如:分布式系统协调、防止资源竞争、限流控制
- 地理位置应用,通过Redis的GEO功能实现,例如:附近的人/地点、地理位置索引、距离计算
- 限速器,例如:限制API调用频率、防止暴力破解、流量控制
2、编译安装redis(单机模式)
#安装新yum源
yum install centos-release-scl scl-utils-build
#安装编译器工具链(包含了gcc)
yum install -y devtoolset-8-toolchain
#使安装生效(临时有效)
scl enable devtoolset-8 bash
#永久有效执行
echo "source /opt/rh/devtoolset-8/enable" >> /etc/profile
#查看gcc版本
gcc --version
#安装redis
wget https://download.redis.io/releases/redis-6.2.1.tar.gz
tar zvfx ./redis-6.2.1.tar.gz
cd redis-6.2.1/
make
make install PREFIX=/opt/redis-6.2.1/
#使redis命令全局可用
echo 'export PATH=/opt/redis-6.2.1/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
- 如果make执行失败,可以执行make disclean,用于彻底清理项目的构建文件,将源码包恢复到初始状态
- 指定redis安装目录,避免源码包被污染,删除目录即可彻底卸载
- /opt/redis-6.2.1/bin/目录下是redis命令,不指定prefix则是在/usr/local/bin/目录下
2.1、redis提供的工具
- redis-server:Redis服务启动命令
- redis-cli:Redis命令客户端
- redis-benchmark:Redis性能测试工具
- redis-check-aof:Redis AOF持久化文件检测和修复工具
- redis-check-rdb:Redis RDB持久化文件检测和修复工具
- redis-sentinel:启动Redis Sentinel
2.2、启动redis
redis有三种启动方式
1、前台启动,用于测试
2、后台守护进程方式启动
3、指定配置文件启动
#(1) 直接启动(前台运行,测试用),Ctrl+C 可停止 Redis
/opt/redis-6.2.1/bin/redis-server
#(2) 后台守护进程方式启动(推荐生产使用)
/opt/redis-6.2.1/bin/redis-server --daemonize yes
#(3) 指定配置文件启动(推荐)
# Redis 默认不带配置文件运行,但生产环境建议使用配置文件
# 使用配置文件启动
/opt/redis-6.2.1/bin/redis-server /opt/redis-6.2.1/redis.conf
#关键配置项:
daemonize yes # 后台运行
bind 0.0.0.0 # 允许远程访问(生产环境建议限制IP)
protected-mode no # 关闭保护模式(如果需远程访问)
requirepass yourpassword # 设置密码
2.3、停止redis
redis-cli shutdown
#如果设置了密码,指定密码关闭
redis-cli -a yourpassword shutdown
#多实例关闭,指定端口关闭
redis-cli -p 6379 shutdown
2.4、systemd管理redis
- 如果 Redis 是通过 yum/dnf/a- pt 安装的,可以使用systemd管理
- 默认配置文件:/etc/redis.conf 或 /etc/redis/redis.conf。
sudo systemctl start redis # 启动
sudo systemctl enable redis # 开机自启
sudo systemctl status redis # 查看状态
2.4.1、redis开机自启动
#创建 /etc/systemd/system/redis.service:
[Unit]
Description=Redis In-Memory Data Store
After=network.target
[Service]
ExecStart=/opt/redis-6.2.1/bin/redis-server /etc/redis.conf
ExecStop=/opt/redis-6.2.1/bin/redis-cli shutdown
Restart=always
User=redis
Group=redis
[Install]
WantedBy=multi-user.target
#使配置文件生效
sudo systemctl daemon-reload
sudo systemctl enable redis
sudo systemctl start redis
2.5、连接redis
#(1) 本地连接
/opt/redis-6.2.1/bin/redis-cli
#(2) 远程连接
redis-cli -h <IP> -p <PORT> -a <PASSWORD>
2.6、redis命令
redis中文网中有命令的详细介绍:https://www.redis.net.cn/order/
- Redis 键(key)
- Redis 字符串(String)
- Redis 哈希(Hash)
- Redis 列表(List)
- Redis 集合(Set)
- Redis 有序集合(sorted set)
- Redis 连接
- Redis 服务器
- Redis 脚本
- Redis 事务
- Redis HyperLogLog
- Redis 发布订阅
- Redis 地理位置(geo)
select 3 //切换数据库。默认16个数据库,第一个是0
dbsize //查看当前数据库的key的数量
flushdb //清空当前库
flushall //清空全部库
keys * //查看数据库所有key
keys hello* //查看以hello开头的键
ttl key //查看还有多少秒过期,-1永不过期,-2已过期
expire <KEY_NAME> <TIME_IN_SECONDS> //设置键过期
persist <KEY_NAME> //设置键永不过期
3、redis的数据类型
Redis 支持以下5种核心数据类型:
- String(字符串)
- Hash(哈希表)
- List(列表)
- Set(集合)
- Sorted Set(有序集合)
此外,还有 3 种特殊类型(基于核心类型的扩展):
- Bitmaps(位图)
- HyperLogLog(基数统计)
- Geospatial(地理信息)
3.1、Redis 的 String(字符串)
- Redis 的 String(字符串) 是最基础的数据类型,一个key对应一个value
- String类型是二进制安全的,可以存储 文本、数字(整数/浮点数)或二进制数据(如图片、序列化对象)
- 二进制安全是指不会因为数据中包含特殊字符(如 \0、换行符等)导致截断或解析错误
- 一个Redis中字符串value最多可以是512M
常用命令
set <key> <value> //添加键值对

- 字段解释
- NX:当数据库中key不存在时,可以将key-value添加数据库
- XX:当数据库中key存在时,可以将key-value添加数据库,与NX互斥
- EX:key的超时秒数
- PX:key的超时毫秒数,与EX互斥
get {KEY} # 查询对应键值
append {KEY} {VALUE} # 将给定的{VALUE} 追加到原值的末尾
strlen {KEY} # 获得值的长度
setnx {KEY} {VALUE} # 只有在{KEY}不存在时才能设置{KEY}的值
incr {KEY} # 将 {KEY} 中储存的数字值增1只能对数字值操作,如果为空,新增值为1
decr {KEY} # 将 {KEY} 中储存的数字值减1只能对数字值操作,如果为空,新增值为-1
incrby {KEY} {步长} # 将 {KEY} 中储存的数字值增。自定义步长。
decrby {KEY} {步长} # 将 {KEY} 中储存的数字值减。自定义步长。
数据结构
- 采用预分配冗余空间的方式来减少内存的频繁分配.
- 当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。
原子操作
- 所谓原子操作是指不会被线程调度机制打断的操作;
- 这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
- 在单线程中, 能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间。
- 在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。
- Redis单命令的原子性主要得益于Redis的单线程。
3.2、Redis 的 List(列表)
- Redis 的 List(列表) 是双向链表,支持在头部或尾部高效插入/删除元素,适合实现 队列、栈、消息流 等场景
- 允许存储相同的值。
- 元素按顺序插入存储
- 最多可存储 2³² - 1 个元素(约 40 亿)
常用命令
lpush/rpush {key}{value1}{value2}{value3} .... # 从左边/右边插入一个或多个值。
lpop/rpop {key} 从左边/右边吐出一个值。值在键在,值光键亡。
rpoplpush {key1}{key2} 从{key1}列表右边吐出一个值,插到{key2}列表左边。
lrange {key}{start}{stop} 按照索引下标获得元素(从左到右)
lrange mylist 0 -1 0左边第一个,-1右边第一个,(0-1表示获取所有)
lindex {key}{index} 按照索引下标获得元素(从左到右)
llen {key} 获得列表长度
linsert {key} before {value}{newvalue} 在{value}的后面插入{newvalue}插入值
lrem {key}{n}{value} 从左边删除n个value(从左到右)
lset{key}{index}{value} 将列表key下标为index的值替换成value
数据结构
- List的数据结构为快速链表quickList
- 首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。
- 当数据量比较多的时候才会改成quicklist。因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。
- Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
3.3、Redis 的 Set(集合)
- Redis 的 Set 是一个无序、唯一的字符串集合,基于哈希表实现,支持高效的 添加、删除、查找 操作,适合存储不重复的数据(如标签、好友列表)
常用命令
sadd <key><value1><value2> ..... 将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略
smembers <key> 取出该集合的所有值。
sismember <key><value> 判断集合<key>是否为含有该<value>值,有1,没有0
scard<key> 返回该集合的元素个数。
srem <key><value1><value2> .... 删除集合中的某个元素。
spop <key> 随机从该集合中吐出一个值。
srandmember <key><n> 随机从该集合中取出n个值。不会从集合中删除 。
smove <source><destination> value把集合中一个值从一个集合移动到另一个集合
sinter <key1><key2> 返回两个集合的交集元素。
sunion <key1><key2> 返回两个集合的并集元素。
sdiff <key1><key2> 返回两个集合的差集元素(key1中的,不包含key2中的)
数据结构
- 默认使用 哈希表(Hash Table),以 O(1) 时间复杂度实现元素查找。
- 当元素均为整数且较少时,会转为 整数数组(IntSet) 节省内存。
3.4、Redis 的 Hash(哈希表)
- Redis hash是一个string类型的field和value的映射表,适合存储对象类型的数据(如用户信息、商品属性)也可以说适合存储对象。每个 Hash 可以存储多达 2³² - 1 个字段-值对,且支持单独操作字段,无需读取整个 Hash
常用命令
hset <key><field><value> 给<key>集合中的 <field>键赋值<value>
hget <key1><field> 从<key1>集合<field>取出 value
hmset <key1><field1><value1><field2><value2>... 批量设置hash的值
hexists<key1><field> 查看哈希表 key 中,给定域 field 是否存在。
hkeys <key> 列出该hash集合的所有field
hvals <key> 列出该hash集合的所有value
hincrby <key><field><increment> 为哈希表 key 中的域 field 的值加上增量 1 -1
hsetnx <key><field><value> 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在 .
数据结构
- field-value长度较短且个数较少时,使用ziplist
- 当字段数或值超过阈值(默认 hash-max-ziplist-entries 512、hash-max-ziplist-value 64 字节)时,转为 hashtable(哈希表)
- (O(1) 时间复杂度操作)
3.5、Redis 的Zset(有序集合)
- Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。
- 不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了 。
- 因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
- 访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
常用命令
将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
zadd <key><score1><value1><score2><value2>…
返回有序集 key 中,下标在<start><stop>之间的元素。带WITHSCORES,可以让分数一起和值返回到结果集。
zrange <key><start><stop> [WITHSCORES]
返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
zrangebyscore key minmax [withscores] [limit offset count]
同上,改为从大到小排列。
zrevrangebyscore key maxmin [withscores] [limit offset count]
zincrby <key><increment><value> 为元素的score加上增量
zrem <key><value> 删除该集合下,指定值的元素
zcount <key><min><max> 统计该集合,分数区间内的元素个数
zrank <key><value> 返回该值在集合中的排名,从0开始。
数据结构
- SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。
- zset底层使用了两个数据结构
- (1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
- (2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。
跳跃表
有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等。对于有序集合的底层实现,可以用数组、平衡树、链表等。数组不便元素的插入、删除;平衡树或红黑树虽然效率高但结构复杂;链表查询需要遍历所有效率低。Redis采用的是跳跃表。跳跃表效率堪比红黑树,实现远比红黑树简单。
对比有序链表和跳跃表
- 对比有序链表和跳跃表,从链表中查询出51
(1) 有序链表

要查找值为51的元素,需要从第一个元素开始依次查找、比较才能找到。共需要6次比较。
(2) 跳跃表

- 从第2层开始,1节点比51节点小,向后比较。
- 21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层
- 在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下
- 在第0层,51节点为要查找的节点,节点被找到,共查找4次。
3.6、bitmap
- 合理地使用操作位能够有效地提高内存使用率和开发效率。
- Bitmaps本身不是一种数据类型,而是一种数据表示方式,实际上它就是字符串(key-value), 但是它可以对字符串的位进行操作。
- 可以把Bitmaps想象成一个以位为单位的数组,数组的每个元素只能存储0和1, 数组的下标在Bitmaps中叫做偏移量,bitmap抽象为下图中的value
![image]()
命令格式
# 设置Bitmaps中某个偏移量的值(0或1)
setbit <key> <offset> <value>
# 获取Bitmaps中某个偏移量的值
getbit <key> <offset>
# 统计字符串从start字节到end字节值为1的数量,start/end(可选):字节偏移量范围(非位偏移!),支持负数(表示倒数位置)
bitcount <key> [start end]
# 对多个Bitmap进行位运算
BITOP <operation> destkey key [key ...]
operation:位运算类型(AND/OR/XOR/NOT)
destkey:存储结果的键
key:参与运算的Bitmap键(NOT运算只能有1个键)
案例
统计某个用户的签到情况,1表示签到,0表示未签到,偏移量表示第几天
# 用户UID=1001在2023年第10天签到
setbit sign:1001:2023 9 1
# 检查第15天是否签到
GETBIT sign:1001:2023 14
标记用户是否活跃,1表示活跃,0表示不活跃。偏移量表示用户id
# 用户UID=500在2023-10-01活跃
setbit dau:2023-10-01 500 1
# 统计当日活跃用户数
BITCOUNT dau:2023-10-01
统计指定字节范围的1的位数
BITCOUNT mybitmap 0 0 # 统计第1个字节(位0~7)的1的位数
BITCOUNT mybitmap 1 1 # 统计第2个字节(位8~15)的1的位数
bitmap使用案例
- Redis Bitmap 的位编号是 从右向左
# 设置两个Bitmap
SETBIT bitmap1 0 1 # bitmap1 = 10000000
SETBIT bitmap1 3 1 # bitmap1 = 10010000
SETBIT bitmap2 3 1 # bitmap2 = 00010000
(1) 逻辑与(AND)
# 计算交集(两者都为1的位)
BITOP AND result_and bitmap1 bitmap2
GET result_and # 返回 "\x10"(二进制00010000)
(2) 逻辑或(OR)
# 计算并集(任意一个为1的位)
BITOP OR result_or bitmap1 bitmap2
GET result_or # 返回 "\x90"(二进制10010000)
(3) 逻辑异或(XOR)
# 计算差异位(值不同的位)
BITOP XOR result_xor bitmap1 bitmap2
GET result_xor # 返回 "\x80"(二进制10000000)
(4) 逻辑非(NOT)
# 取反(单Bitmap操作)
BITOP NOT result_not bitmap1
GET result_not # 返回 "\x6F"(二进制01101111)
bitmap实际应用场景
(1) 统计共同活跃用户
# 记录两天的活跃用户(用户ID作为偏移量)
SETBIT dau:20231001 1001 1 # 用户1001在2023-10-01活跃
SETBIT dau:20231002 1001 1 # 用户1001在2023-10-02活跃
SETBIT dau:20231002 1002 1 # 用户1002在2023-10-02活跃
# 统计两天都活跃的用户(AND)
BITOP AND dau:both_days dau:20231001 dau:20231002
BITCOUNT dau:both_days # 返回1(用户1001)
(2) 合并多个特征标签
# 用户标签(位偏移=标签ID)
SETBIT user:1001:tags 0 1 # 标签0(如"VIP")
SETBIT user:1001:tags 2 1 # 标签2(如"活跃")
SETBIT user:1002:tags 1 1 # 标签1(如"新用户")
# 合并所有VIP或活跃用户(OR)
BITOP OR vip_or_active user:1001:tags user:1002:tags
BITCOUNT vip_or_active # 返回2(用户1001和1002的标签合并结果)
3.7、HyperLoglog
- HyperLogLog 是 Redis 提供的一种概率型基数统计算法,用于高效估算大规模数据的唯一元素数量(基数),特点如下:
- 极低内存占用:统计 1 亿个不重复元素仅需约 12KB 内存。
- 误差可控:标准误差约 0.81%(实际误差通常更低)。
- 不支持元素查询:仅能估算总数,无法判断某元素是否存在。
- 基数(Cardinality):集合中不重复元素的数量(如 {A, B, B, C} 的基数为 3)
应用场景
- UV(独立访客)统计:统计每日/月访问用户数(去重)。
- 大规模数据去重:如日志分析、网络流量监控。
- 集合合并统计:合并多天的数据估算总 UV。
案例
(1) 统计每日网站 UV(UniqueVisitor,独立访客)
# 记录用户访问(自动去重)
pfadd key
PFADD uv:20231001 "user1" "user2" "user1"
# 获取当日 UV 估算值
PFCOUNT uv:20231001 # 返回 2(实际添加了 3 次,但去重后为 2)
# 合并一周的 UV 数据(合并到uv:week)
PFMERGE uv:week uv:20231001 uv:20231002 ... uv:20231007
PFCOUNT uv:week # 返回周 UV 总量
3.8、Geospatial
Redis 的 Geospatial 数据类型基于 Sorted Set(有序集合) 实现,用于存储和查询地理位置信息(如经纬度坐标),并支持以下核心功能:
- 添加地理位置:存储坐标点及其名称(如商店、用户位置)。
- 计算距离:获取两点之间的直线距离。
- 范围查询:查找某坐标半径内的其他点。
- 经纬度哈希编码:将坐标转换为Geohash字符串。
例子
groadd添加地理位置
# 语法
GEOADD key longitude latitude member [longitude2 latitude2 member2 ...]
# 可以同时添加多个
geoadd china:city 121.47 31.23 shanghai
geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing
geopos获取坐标
# 语法
GEOPOS key member [member ...]
geopos china:city shanghai
geodist获取两个位置之间的直线距离
# 语法
GEODIST key member1 member2 [unit]
# 指定单位是km,默认是米
geodist chaina:city beijing shanghai km
georadius以给定的经纬度为中心,找出某一半径内的元素
# 语法
GEORADIUS key longitude latitude radius unit [WITHDIST] [WITHCOORD] [COUNT n]
WITHDIST:返回距离。
WITHCOORD:返回坐标。
COUNT n:限制返回结果数量。
georadius china:city 110 30 900 km
4、redis配置文件
常用的配置参数
bind # 指定接受请求的主机,默认只接受本机访问,不写则表示不限制访问
protected-mode # 本机访问保护模式,yes只能本机访问,no不限制
port # 端口号
tcp-backlog # 表示未完成三次握手队列 + 已经完成三次握手队列的总数。在高并发环境下你需要一个高backlog值来避免慢客户端连接问题。会收到/proc/sys/net/core/somaxconn(默认128)和/proc/sys/net/ipv4/tcp_max_syn_backlog(默认128)这两个值的影响,需要同步调整
timeout # 一个空闲的客户端维持多少秒会关闭,0表示关闭该功能。即永不关闭
tcp-keepalive # 对访问客户端的一种心跳检测,每个n秒检测一次。单位为秒,如果设置为0,则不会进行Keepalive检测,建议设置成60
daemonize # 是否以守护进程运行redis
pidfile # 存放pid文件的位置,每个实例会产生一个不同的pid文件
loglevel # 指定日志记录级别,debug、verbose、notice、warning,默认为notice,生产环境选择notice 或者warning
logfile # 日志文件名称
databases # 设置数据库数量
requirepass # 设置连接密码
maxclients # 同时连接客户端的数量
maxmemory # 设置redis可以使用的内存量
maxmemory-policy # 设置数据移除策略
maxmemory-samples # 内存淘汰策略采样数配置,数值越大淘汰结果越接近全局最优,但 CPU 消耗更高,数值越小,速度更快,但可能淘汰不够精准
redis的6种数据淘汰策略
1、volatile-lru: 从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
2、volatile-ttl: 从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
3、volatile-random: 从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
4、allkeys-lru: 从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
5、allkeys-random: 从数据集(server.db[i].dict)中任意选择数据淘汰
6、noenviction(驱逐) 禁止驱逐数据
redis发布订阅命令行实现
1、打开客户端A订阅channel1
SUBSCRIBE channel1
2、打开客户端B,给channel1发布消息hello。命令会返回订阅者数量
publish channel1 hello
3、打开客户端A可以看到发送的消息
redis事务
- Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。
- 不保证原子性,事务中如果有一条命令执行失败,其后的命令依然可以被执行
- 没有回滚
- Redis事务的主要作用就是串联多个命令防止别的命令插队。
Multi、Exec、discard
- 如果在组队阶段发生错误,例如:对字符串使用incr,在exec时队列中的命令都不会执行
- 如果组队没有发生错误,但是命令有语法错误,则只有语法错误的命令不会执行
# 开启事务
multi
# 输入命令,返回QUEUED,命令会进入队列
set count 1
set age 30
# 执行队列中的命令
exec
# 放弃队列中的命令
discard
悲观锁
- 每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,一直到锁释放,别人才能拿到数据
- 关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁
乐观锁
- 每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。若版本号不一致则不能操作
- Redis 的乐观锁通过 WATCH 命令实现,是一种 无阻塞的并发控制机制,适用于多客户端竞争修改同一数据的场景
- 其核心思想是:“检测数据是否被他人改动过,若未被改动则执行操作,否则放弃或重试”
- 应用场景有:库存扣减(如电商秒杀,避免超卖)。余额变更(如转账时检查账户余额是否被修改)。配置更新(如集群中多个节点同时修改配置时避免冲突)。
- 取消 WATCH 命令对所有 key 的监视。
- 如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了
场景示例
秒杀库存扣减
# 客户端1(监视库存并尝试扣减)
WATCH item:1001:stock # 监视库存键
current_stock = GET item:1001:stock
if current_stock > 0:
MULTI
DECR item:1001:stock # 扣减库存
EXEC # 若期间库存被其他客户端修改,则返回nil
else:
UNWATCH # 库存不足,取消监视
为啥redis单线程还是这么快
1.redis的所有数据都存放在内存中
2.单线程,不存在多线程的CPU切换上下文的问题也不存在各种锁的问题
3.异步非阻塞IO模型(也叫IO多路复用)


浙公网安备 33010602011771号