Redis跟Mysql性能比较
Reids是基于内存的非关系性数据,而mysql的数据存在磁盘上,所以redis性能普遍高于mysql。但读写性能还受硬件、配置、数据大小、持久化策略、事务、是否集群、是否锁冲突等影响。普遍的认为单机redis一般支持10万级并发读写,mysql只到万级。对于同一热点数据(同一个key或同一行数据),redis单线程排队而mysql需要加锁阻塞,redis依旧支持万级QPS而mysql只支持几百。
Redis的基本命令
安装Redis后,会同步安装一个客户端(reids-cli),客户端可以只接启动,然后在客户端中对redis数据库进行操作。
key操作:
java
# 查看key是否存在,返回1存在 0不存在
exists key
# 删除key,支持多个
del key1 key2
# 设置过期时间(秒)
expire key 60
# 设置过期时间(毫秒)
pexpire key 60000
# 查看剩余过期秒数,-1永不过期,-2已删除
ttl key
pttl key
# 查看key类型
type key
# 模糊匹配key,⚠️生产禁止线上用,会阻塞redis
keys user*
# 安全遍历key,游标迭代
scan 0 match user* count 100
# 重命名key
rename old new
# 持久化:把key从内存刷RDB,不用手动执行
String 字符串(最常用,缓存、计数、分布式锁)
java
# 设置
set k1 v1
# 设置+过期时间,原子
set k1 v1 ex 30
# 仅key不存在才设置(分布式锁NX)
set k1 v1 nx ex 10
# 仅key存在才设置
set k1 v1 xx
# 获取
get k1
# 批量
mget k1 k2 k3
mset k1 v1 k2 v2
# 数字自增,原子计数
incr num
incrby num 10
decr num
decrby num 5
# 追加字符串
append k1 "_suffix"
# 获取字符串长度
strlen k1
Hash 哈希(对象存储,user:{id})
java
# 左边插入(头)
lpush list1 a b c
# 右边插入(尾)
rpush list1 d e
# 左边弹出
lpop list1
# 右边弹出
rpop list1
# 阻塞弹出,没有数据就等待秒数,消息队列
blpop list1 10
brpop list1 10
# 指定下标取值,下标从0开始
lindex list1 0
# 区间获取 [start,end],-1代表最后一个
lrange list1 0 -1
# 裁剪list,只保留区间,用于限流队列
ltrim list1 0 99
# list长度
llen list1
Set 集合,无序不重复(去重、交集并集)
java
# 添加
sadd set1 a b c a
# 查询全部元素
smembers set1
# 判断是否存在
sismember set1 a
# 删除元素
srem set1 b
# 随机取元素
srandmember set1 2
# 随机弹出
spop set1
# 集合运算
sinter set1 set2 # 交集
sunion set1 set2 # 并集
sdiff set1 set2 # 差集
# 元素数量
scard set1
List 列表,双向链表(队列、栈)
java
# 左边插入(头)
lpush list1 a b c
# 右边插入(尾)
rpush list1 d e
# 左边弹出
lpop list1
# 右边弹出
rpop list1
# 阻塞弹出,没有数据就等待秒数,消息队列
blpop list1 10
brpop list1 10
# 指定下标取值,下标从0开始
lindex list1 0
# 区间获取 [start,end],-1代表最后一个
lrange list1 0 -1
# 裁剪list,只保留区间,用于限流队列
ltrim list1 0 99
# list长度
llen list1
ZSet 有序集合,score 权重(排行榜、延时队列)
java
# 添加 member 带score分数
zadd rank 100 user1 95 user2 98 user3
# 正序查询 [start end],从小到大
zrange rank 0 -1
# 带上分数
zrange rank 0 -1 withscores
# 倒序,从大到小(排行榜)
zrevrange rank 0 -1 withscores
# 根据分数范围查询
zrangebyscore rank 90 100
# 删除成员
zrem rank user1
# 获取成员排名
zrank rank user2
zrevrank rank user2
# 分数自增
zincrby rank 5 user2
# zset元素总数
zcard rank
# 指定分数区间数量
zcount rank 90 100
全局服务命令
java
# 选择数据库 0‑15,默认db0
select 1
# 清空当前库,危险
flushdb
# 清空全部库,非常危险
flushall
# 查看信息,cpu、内存、持久化、客户端
info
info memory
info stats
# 查看当前连接客户端
client list
# 持久化触发
bgsave # 后台生成RDB
bgrewriteaof # AOF重写
# 测试连通
ping
info命令查看持久化相关信息命令:info persistence
返回示例:
java
# Persistence
loading:0
rdb_changes_since_last_save:0 # 上次RDB之后改动key数量
rdb_bgsave_in_progress:0
rdb_last_save_time:1756234000 # 上次成功bgsave的时间戳
rdb_last_bgsave_status:ok # ok代表RDB成功
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
rdb_enabled:1 # RDB功能是否开启(save配置是否存在)
aof_enabled:0 # 0=AOF关闭;1=AOF开启
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_fsync:everysec # appendfsync策略:everysec / always / no
查看rdb持久化规则:config get save
java
127.0.0.1:6379> config get save
1) "save"
2) "900 1 300 10 60 10000" # 表示900s内改动key至少为1个 或300s内至少10个 或60s内至少10000个 则触发rdb持久化
若返回""则表示RDB 自动快照关闭 ,不会自动 bgsave;但你依然可以手动执行 bgsave 生成 rdb 文件。
Redis Bitmap(位图)
Bitmap 不是独立数据类型,底层就是 String 字符串 ,按 bit(位)操作。一个 key 的字符串最多 512MB,对应 2^32 个 bit,约 42.9 亿位。
1 byte = 8 bit;100 万用户只需要~125KB 内存。
典型场景:用户签到、日活统计、是否访问、布尔标记、去重。
以统计用户签到为例,一个二进制位代表一个用户,每个日期存一个bitmap,统计当前日期已签到用户,已签到对应二进制位存1否则存0。若用户id已满足自增可直接使用用户id,若用户ID非自增或偏移量较大(如ID从1000000开始),可以考虑通过hashcode方式映射,但hash映射方式可能出现hash冲突导致统计异常。要想统计准确可以单独添加用户id和自增id的映射表,保证用ID从1开始。
核心命令
1. setbit 设置位
java
# setbit key offset value
# offset:位下标,从0开始;value只能 0 / 1
setbit sign:20260901 100 1
含义:把 offset=100 的位设置为 1,代表 id=100 用户今天签到。
offset 可以非常大,中间未设置的位默认是 0。
2. getbit 获取某一位
java
getbit sign:20260901 100
返回 0 / 1;判断用户 100 是否签到。
3. bitcount 统计为 1 的总数(统计签到人数)
java
# 统计整个key里面值为1的bit总数
bitcount sign:20260901
# 按字节范围统计,start、end 是**字节偏移**,不是bit!
bitcount sign:20260901 0 100
⚠️注意:start end 单位是字节 (byte),不是 bit,不要搞错。
4. bitop 位图运算(与、或、异或、非)
做多天签到合并、多日活跃用户统计
java
# bitop op destkey key1 key2 ...
# op: AND(与) OR(或) XOR(异或) NOT(非)
# OR:任意一天签到过的用户,结果存入 sign:all
bitop OR sign:all sign:20260901 sign:20260902 sign:20260903
# AND:三天全部都签到的用户
bitop AND sign:all sign:20260901 sign:20260902 sign:20260903
bitop 会生成新 key,运算量大,大 bitmap 会阻塞 redis,线上谨慎。
5. bitpos 查找第一个 0/1 的位置
java
# 找第一个值为1的bit下标
bitpos sign:20260901 1
# 找第一个0
bitpos sign:20260901 0
HyperLogLog
HyperLogLog(简称 HLL)是 Redis 用来做基数统计的数据结构。
基数:集合中不重复元素的个数。例如集合
{1,2,2,3},基数 = 3。
核心特性
- 不存储原始数据,只存概率统计估算值;
- 空间极小:每个 HLL key 只占用约 12KB 内存,最多统计 264 个元素;
- 结果是估算值,有误差:标准误差 0.81%;
- 适合大数据量去重计数,不适合要拿到原始元素的场景。
使用场景
- 统计页面 UV、每日独立访客(海量用户,不需要精确值,容忍小误差)
- 直播观看人数、活动去重访问量
- 多维度去重统计合并(
PFMERGE合并多天、多渠道)
数据结构
整体结构 = HLL 头(hllhdr) + 寄存器数据区 ,寄存器区有两种编码:稀疏编码 (Sparse) 、密集编码 (Dense),Redis 自动动态转换
java
struct hllhdr {
char magic[4]; // 魔术标记 "HYLL",标识这是HLL结构
uint8_t encoding; // 编码:0=DENSE密集,1=SPARSE稀疏
uint8_t notused[3]; // 保留3字节,预留扩展,恒为0
uint8_t card[8]; // 基数缓存,小端序;最高1bit标记缓存是否有效,低63bit存上次PFCOUNT结果
uint8_t registers[]; // 柔性数组:寄存器数据区
};
参数常量:
HLL_P =14→ 桶数量 (M=214=16384) 个寄存器(桶)HLL_BITS=6:每个寄存器占 6bit,最大存 63,足够保存最多 50 位哈希的前导零最大值。
总大小:
(16384 ×6bit ÷8 =12288字节 =12KB)
加上头部 16 字节,完整对象约 12KB+
编码切换时机
- 初始化:默认稀疏编码
- 触发转密集:
- PFADD 导致某个寄存器值 > 32
- 稀疏编码自身占用超过阈值
底层原理简要
HyperLogLog 基于伯努利试验 + 调和平均数估算基数:
- 对输入元素做 hash,得到 64bit 哈希值;
- 低 14 位用来选桶(Redis HLL 一共 16384 个桶,(214=16384));
- 剩余高位计算前导 0 的个数,每个桶记录该桶出现过最大前导 0 数量,(前导0越多,表明出现该数的概率越小,基数就越多。相同基数由于hash一样,选择的桶一样,前导0的个数也一样所以总的基数不会更新);
- 最后对 16384 个桶的值做调和平均,修正偏差,估算出总基数。
- 桶数量固定 16384,所以内存固定约 12KB;
- 误差来源是概率估算,标准误差 0.81%;
- Redis HLL 内部有稀疏、稠密两种编码:数据少时用稀疏编码占用更小,超过阈值自动转为稠密(12KB)。
使用示例
统计某个页面每天的访问次数
java
# 添加访问用户id
PFADD uv:20260902 user1 user2 user3 user2 user1
# 统计今日UV(去重用户数)
PFCOUNT uv:20260902
# 合并两天UV到新key
PFMERGE uv:total uv:20260901 uv:20260902
PFCOUNT uv:total
GEO
Redis 3.2 新增 GEO,用于存储经纬度、计算距离、查询附近点位;底层没有新数据结构,基于 ZSet(有序集合)+ GeoHash 算法实现
典型业务场景
- LBS 业务:附近门店、附近商家、附近的人;
- 配送业务:骑手、车辆位置,计算两点距离;
- 简单地理围栏(小数据量)。
核心命令
1. GEOADD 添加点位
# key 经度 纬度 member
GEOADD shop:geo 104.06 30.67 shop‑001 104.07 30.68 shop‑002
2. GEOPOS 获取 member 的经纬度
GEOPOS shop:geo shop‑001 shop‑002
3. GEODIST 计算两点球面距离
单位:m米 (默认)、km千米、mi英里、ft英尺
GEODIST shop:geo shop‑001 shop‑002 km
4. GEOHASH 获取 geohash 字符串
GEOHASH shop:geo shop‑001