Redis跟Mysql性能比较
Reids是基于内存的非关系性数据,而mysql的数据存在磁盘上,所以redis性能普遍高于mysql。但读写性能还受硬件、配置、数据大小、持久化策略、事务、是否集群、是否锁冲突等影响。普遍的认为单机redis一般支持10万级并发读写,mysql只到万级。对于同一热点数据(同一个key或同一行数据),redis单线程排队而mysql需要加锁阻塞,redis依旧支持万级QPS而mysql只支持几百。
Redis的基本命令
安装Redis后,会同步安装一个客户端(reids-cli),客户端可以只接启动,然后在客户端中对redis数据库进行操作。
key操作:
# 查看key是否存在,返回1存在0不存在 exists key # 删除key,支持多个 del key1 key2 # 设置过期时间(秒)expire key60# 设置过期时间(毫秒)pexpire key60000# 查看剩余过期秒数,-1永不过期,-2已删除 ttl key pttl key # 查看key类型 type key # 模糊匹配key,⚠️生产禁止线上用,会阻塞redis keys user*# 安全遍历key,游标迭代 scan0match user*count100# 重命名key rename oldnew# 持久化:把key从内存刷RDB,不用手动执行String 字符串(最常用,缓存、计数、分布式锁)
# 设置 set k1 v1 # 设置+过期时间,原子 set k1 v1 ex30# 仅key不存在才设置(分布式锁NX)set k1 v1 nx ex10# 仅key存在才设置 set k1 v1 xx # 获取 get k1 # 批量 mget k1 k2 k3 mset k1 v1 k2 v2 # 数字自增,原子计数 incr num incrby num10decr num decrby num5# 追加字符串 append k1"_suffix"# 获取字符串长度 strlen k1Hash 哈希(对象存储,user:{id})
# 左边插入(头) lpush list1 a b c # 右边插入(尾) rpush list1 d e # 左边弹出 lpop list1 # 右边弹出 rpop list1 # 阻塞弹出,没有数据就等待秒数,消息队列 blpop list110brpop list110# 指定下标取值,下标从0开始 lindex list10# 区间获取[start,end],-1代表最后一个 lrange list10-1# 裁剪list,只保留区间,用于限流队列 ltrim list1099# list长度 llen list1Set 集合,无序不重复(去重、交集并集)
# 添加 sadd set1 a b c a # 查询全部元素 smembers set1 # 判断是否存在 sismember set1 a # 删除元素 srem set1 b # 随机取元素 srandmember set12# 随机弹出 spop set1 # 集合运算 sinter set1 set2 # 交集 sunion set1 set2 # 并集 sdiff set1 set2 # 差集 # 元素数量 scard set1List 列表,双向链表(队列、栈)
# 左边插入(头) lpush list1 a b c # 右边插入(尾) rpush list1 d e # 左边弹出 lpop list1 # 右边弹出 rpop list1 # 阻塞弹出,没有数据就等待秒数,消息队列 blpop list110brpop list110# 指定下标取值,下标从0开始 lindex list10# 区间获取[start,end],-1代表最后一个 lrange list10-1# 裁剪list,只保留区间,用于限流队列 ltrim list1099# list长度 llen list1ZSet 有序集合,score 权重(排行榜、延时队列)
# 添加 member 带score分数 zadd rank100user195user298user3 # 正序查询[start end],从小到大 zrange rank0-1# 带上分数 zrange rank0-1withscores # 倒序,从大到小(排行榜) zrevrange rank0-1withscores # 根据分数范围查询 zrangebyscore rank90100# 删除成员 zrem rank user1 # 获取成员排名 zrank rank user2 zrevrank rank user2 # 分数自增 zincrby rank5user2 # zset元素总数 zcard rank # 指定分数区间数量 zcount rank90100全局服务命令
# 选择数据库0‑15,默认db0 select1# 清空当前库,危险 flushdb # 清空全部库,非常危险 flushall # 查看信息,cpu、内存、持久化、客户端 info info memory info stats # 查看当前连接客户端 client list # 持久化触发 bgsave # 后台生成RDBbgrewriteaof #AOF重写 # 测试连通 pinginfo命令查看持久化相关信息命令:info persistence
返回示例:
#Persistenceloading:0rdb_changes_since_last_save:0# 上次RDB之后改动key数量 rdb_bgsave_in_progress:0rdb_last_save_time:1756234000# 上次成功bgsave的时间戳 rdb_last_bgsave_status:ok # ok代表RDB成功 rdb_last_bgsave_time_sec:0rdb_current_bgsave_time_sec:-1rdb_enabled:1#RDB功能是否开启(save配置是否存在) aof_enabled:0#0=AOF关闭;1=AOF开启 aof_rewrite_in_progress:0aof_rewrite_scheduled:0aof_last_rewrite_time_sec:-1aof_current_rewrite_time_sec:-1aof_last_bgrewrite_status:ok aof_last_write_status:ok aof_last_fsync:everysec # appendfsync策略:everysec/always/no查看rdb持久化规则:config get save
127.0.0.1:6379>config get save1)"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 设置位
# setbit key offset value # offset:位下标,从0开始;value只能0/1setbit sign:202609011001含义:把offset=100的位设置为 1,代表 id=100 用户今天签到。
offset 可以非常大,中间未设置的位默认是 0。
2. getbit 获取某一位
getbit sign:20260901100返回0/1;判断用户 100 是否签到。
3. bitcount 统计为 1 的总数(统计签到人数)
# 统计整个key里面值为1的bit总数 bitcount sign:20260901# 按字节范围统计,start、end 是**字节偏移**,不是bit! bitcount sign:202609010100⚠️注意:start end 单位是字节 (byte),不是 bit,不要搞错。
4. bitop 位图运算(与、或、异或、非)
做多天签到合并、多日活跃用户统计
# bitop op destkey key1 key2...# op:AND(与)OR(或)XOR(异或)NOT(非)#OR:任意一天签到过的用户,结果存入 sign:all bitopORsign:all sign:20260901sign:20260902sign:20260903#AND:三天全部都签到的用户 bitopANDsign:all sign:20260901sign:20260902sign:20260903bitop 会生成新 key,运算量大,大 bitmap 会阻塞 redis,线上谨慎。
5. bitpos 查找第一个 0/1 的位置
# 找第一个值为1的bit下标 bitpos sign:202609011# 找第一个0bitpos sign:202609010HyperLogLog
HyperLogLog(简称 HLL)是 Redis 用来做基数统计的数据结构。
基数:集合中不重复元素的个数。例如集合
{1,2,2,3},基数 = 3。
核心特性
- 不存储原始数据,只存概率统计估算值;
- 空间极小:每个 HLL key 只占用约12KB内存,最多统计 264个元素;
- 结果是估算值,有误差:标准误差 0.81%;
- 适合大数据量去重计数,不适合要拿到原始元素的场景。
使用场景
- 统计页面 UV、每日独立访客(海量用户,不需要精确值,容忍小误差)
- 直播观看人数、活动去重访问量
- 多维度去重统计合并(
PFMERGE合并多天、多渠道)
数据结构
整体结构 =HLL 头(hllhdr) + 寄存器数据区,寄存器区有两种编码:稀疏编码 (Sparse)、密集编码 (Dense),Redis 自动动态转换
struct hllhdr{charmagic[4];// 魔术标记 "HYLL",标识这是HLL结构uint8_t encoding;// 编码:0=DENSE密集,1=SPARSE稀疏uint8_t notused[3];// 保留3字节,预留扩展,恒为0uint8_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)。
使用示例
统计某个页面每天的访问次数
# 添加访问用户idPFADDuv:20260902user1 user2 user3 user2 user1 # 统计今日UV(去重用户数)PFCOUNTuv:20260902# 合并两天UV到新keyPFMERGEuv:total uv:20260901uv:20260902PFCOUNTuv:totalGEO
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‑0022. GEOPOS 获取 member 的经纬度
GEOPOS shop:geo shop‑001 shop‑0023. GEODIST 计算两点球面距离
单位:m米 (默认)、km千米、mi英里、ft英尺
GEODIST shop:geo shop‑001 shop‑002 km4. GEOHASH 获取 geohash 字符串
GEOHASH shop:geo shop‑001