Redis01-redis基础

1、redis概述

Redis(Remote Dictionary Server)是一个开源的、基于内存的键值存储系统,它可以用作数据库、缓存和消息代理。Redis 支持多种数据结构,包括字符串(Strings)、哈希(Hashes)、列表(Lists)、集合(Sets)、有序集合(Sorted Sets)等。

1.1、redis的主要特点

  1. 数据主要存储在内存中,提供极高的读写性能
  2. 支持RDB快照和AOF日志两种持久化方式
  3. 有丰富的数据结构
  4. 所有操作都是原子性的
  5. 支持消息发布订阅模式
  6. 通过Redis Sentinel和Redis Cluster支持高可用和分布式

1.2、redis实际应用场景

  1. 作为缓存系统,用于减轻数据库压力,加速应用响应。例如:网页缓存、数据库查询结果缓存、会话(Session)缓存、API响应缓存
  2. 排行榜/计数器,通过Redis的有序集合(ZSET)实现。例如:排行榜、实时计数(点赞数、浏览量等)
  3. 消息队列,通过Redis的列表(List)或发布/订阅功能实现
  4. 分布式锁,通过Redis的原子操作实现。例如:分布式系统协调、防止资源竞争、限流控制
  5. 地理位置应用,通过Redis的GEO功能实现,例如:附近的人/地点、地理位置索引、距离计算
  6. 限速器,例如:限制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>  //添加键值对

image

  • 字段解释
    • 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) 有序链表
image

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

  • 从第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多路复用)

IO多路复用机制

https://www.cnblogs.com/yanguhung/p/10145755.html

posted @ 2024-05-10 10:34  立勋  阅读(17)  评论(0)    收藏  举报