1.redis入门
1)redis的基本概念:
redis是一种开源的,基于内存的和C/S,高性能的键值型NoSQL的基本数据库。可以用作数据库,缓存和消息中间的组件。端口号是6379。
2)关系型数据库+非关系型
关系型数据库 = 用 "表格" 存数据,像 Excel 一样
核心特点
数据存在 表(table) 里
表有固定的 列(字段),结构必须提前定义
支持 SQL 语言 查询
强调 事务、一致性、安全(钱、订单、用户信息必须用它)
表和表之间可以 关联(外键)
典型代表
MySQL
Oracle
SQL Server
PostgreSQL
优点
数据安全、严谨、不会乱
支持复杂查询、事务(ACID)
适合存结构化数据:用户、订单、商品
缺点
扩展性差,大数据高并发吃力
表结构改起来麻烦
不适合存海量非结构化数据
非关系型数据库 = 不用表格存,灵活、快、高并发
NoSQL = Not Only SQL(不仅仅是 SQL)
核心特点
没有固定表结构,想存什么存什么
不强调严格关系
追求 快、高并发、海量存储
数据模型灵活:key-value、文档、列、图
四大类(你课件里也讲了)
KV 键值型(Redis、Memcached)
文档型(MongoDB)
列存储(HBase)
图数据库(Neo4j,社交关系)
优点
速度极快(Redis 纯内存)
高并发轻松扛
水平扩展强,大数据轻松应对
结构灵活,随时加字段
缺点
事务、一致性不如关系型数据库
不适合存特别严谨的数据(如金融交易)
3)redis的基本操作
Redis默认有16个数据库,默认使用的是第0个数据库,可以通过select切换数据库。
select
切换数据库 格式:select index
sql
127.2.2.1:6379> select 3 #切换数据库
OK
dbsize
查看数据的大小,格式:dbsize
sql
127.0.0.1:6379[3]> DBSIZE # 查看数据库的大小
(integer) 0
127.0.0.1:6379[3]> set name wangold
OK
127.0.0.1:6379[3]> set age 28
OK
127.0.0.1:6379[3]> DBSIZE
(integer) 2
keys
查看所有的 key,格式:keys *
sql
127.0.0.1:6379[3]> KEYS * #查看所有的key
1) "name"
2) "age"
flushdb flushall
清空当前数据库和清空所有的数据
sql
127.0.0.1:6379[3]> FLUSHDB #清空当前数据库
OK
127.0.0.1:6379[3]> KEYS *
(empty array)
127.0.0.1:6379[3]> FLUSHALL #清空所有的数据库
OK
4)为什么redis是单线程的?
redis是很快的,官方标识,redis是基于内存操作的,cpu不是redis的性能瓶颈,redis的瓶颈就是根据机器的内存和网络宽带,既然可以使用单线程实现,就使用单线程了。
Redis为什么单线程还这么快?
误区1: 高性能的服务器一定是多线程的
误区2: 多线程(CPU上下文切换)一定比单线程效率高
核心: Redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,相比多
线程,减少了CPU上下文切换的耗时。对于内存系统来说,没有上下文切换效率就是最高的,多次读写
都是在一个CPU上的。
2.redis五个基本类型
redis---key

一、字符串(String)
定义:最基础类型,key = 字符串,value = 字符串 / 数字 / 二进制,最大 512MB。
- 基础操作
cpp
# 1. 设置键值对
SET name "张三"
# 2. 获取值
GET name # 返回 "张三"
# 3. 不存在才设置(分布式锁核心)
SETNX lock 1 # 成功返回1,失败返回0
# 4. 设置并指定过期时间(秒)
SETEX code 60 1234 # 60秒后自动删除
# 5. 批量设置/批量获取
MSET k1 v1 k2 v2 k3 v3
MGET k1 k2 k3 # 返回 v1 v2 v3
# 6. 删除键
DEL name
- 数值操作(原子性,高并发安全)
cpp
SET num 10
# 自增1
INCR num # 11
# 自减1
DECR num # 10
# 自增指定数字
INCRBY num 5 # 15
# 自减指定数字
DECRBY num 5 # 10
- 高级操作
cpp
# 追加字符串
APPEND name "李四" # name 变成 "张三李四"
# 获取字符串长度
STRLEN name
# 覆盖指定位置字符串
SETRANGE name 2 "666"
二、列表(List)
定义:有序、可重复、双向链表,支持左右两边插入 / 弹出。
- 添加元素
sql
# 1. 左边插入(头插)
LPUSH fruits apple banana
# 2. 右边插入(尾插)
RPUSH fruits orange grape
# 3. 插入到某个元素前/后
LINSERT fruits before "orange" "pear"
- 查询元素
sql
# 1. 获取全部元素(0=开始,-1=最后)
LRANGE fruits 0 -1
# 2. 获取列表长度
LLEN fruits
# 3. 获取指定下标元素
LINDEX fruits 1
- 删除 / 弹出元素
sql
# 1. 左边弹出(删除第一个)
LPOP fruits
# 2. 右边弹出(删除最后一个)
RPOP fruits
# 3. 删除指定个数的元素
LREM fruits 2 "apple" # 删除2个apple
# 4. 保留指定区间,其余删除
LTRIM fruits 0 2
- 高级操作
sql
# 右边弹出并左边插入到另一个列表
RPOPLPUSH list1 list2
三、哈希(Hash)
定义:key=field-value 结构,适合存对象(用户、商品)。
- 添加 / 修改字段
sql
# 1. 设置单个字段
HSET user:1001 name "张三" age 20
# 2. 批量设置
HMSET user:1001 phone 13800138000 sex 男
- 查询字段
sql
# 1. 获取单个字段
HGET user:1001 name
# 2. 批量获取字段
HMGET user:1001 age phone
# 3. 获取所有字段+值
HGETALL user:1001
# 4. 获取所有字段名
HKEYS user:1001
# 5. 获取所有值
HVALS user:1001
# 6. 获取字段数量
HLEN user:1001
- 删除 / 判断
sql
# 1. 删除指定字段
HDEL user:1001 sex
# 2. 判断字段是否存在
HEXISTS user:1001 name
# 3. 字段数值自增
HINCRBY user:1001 age 1
四、集合(Set)
定义:无序、不可重复、自动去重,支持交集 / 并集 / 差集。
- 添加 / 删除
sql
# 1. 添加元素
SADD tags java python redis
# 2. 删除元素
SREM tags java
# 3. 随机弹出一个元素
SPOP tags
- 查询元素
cs
# 1. 查看所有元素
SMEMBERS tags
# 2. 判断元素是否存在
SISMEMBER tags python
# 3. 集合大小
SCARD tags
# 4. 随机获取元素(不删除)
SRANDMEMBER tags 2
- 集合运算(最强大功能)
cpp
SADD set1 a b c
SADD set2 c d e
# 1. 交集(共同拥有)
SINTER set1 set2 # c
# 2. 并集(合并去重)
SUNION set1 set2 # a b c d e
# 3. 差集(set1有,set2没有)
SDIFF set1 set2 # a b
五、有序集合(Sorted Set / ZSet)
定义:按 score 自动排序、不重复,专门做排行榜。
- 添加 / 修改
cs
# 1. 添加元素(score 在前,value 在后)
ZADD rank 95 "张三" 98 "李四" 88 "王五"
# 2. 修改分数
ZINCRBY rank 2 "张三" # 95+2=97
- 排序查询(核心)
cpp
# 1. 正序查询(分数从低到高)
ZRANGE rank 0 -1 WITHSCORES
# 2. 倒序查询(分数从高到低,排行榜专用)
ZREVRANGE rank 0 -1 WITHSCORES
# 3. 获取元素分数
ZSCORE rank "李四"
# 4. 获取元素排名(正序/倒序)
ZRANK rank "张三" # 正序排名
ZREVRANK rank "张三" # 倒序排名
- 统计 / 删除
cpp
# 1. 删除元素
ZREM rank "王五"
# 2. 统计分数区间数量
ZCOUNT rank 90 100
# 3. 删除指定排名区间元素
ZREMRANGEBYRANK rank 0 1
# 4. 删除指定分数区间元素
ZREMRANGEBYSCORE rank 80 90
3.redis的三种特殊类型
一、Geospatial(地理空间类型)
- 核心定义
专门用来存储地理位置信息 (经纬度),并支持计算两地距离、查找指定范围内的地点等地理计算。
底层原理:本质是用 ZSet(有序集合) 实现的,把经纬度编码成一个分数存储。
- 核心命令
sql
# 1. 添加地理位置(经度、纬度、名称)
GEOADD key 经度 纬度 名称 [经度 纬度 名称 ...]
# 示例:添加北京、上海、广州的坐标
GEOADD city 116.40 39.90 beijing 121.47 31.23 shanghai 113.27 23.13 guangzhou
# 2. 计算两个地点之间的距离(单位:m/km/mi/ft)
GEODIST key 名称1 名称2 [单位]
# 示例:计算北京到上海的距离(公里)
GEODIST city beijing shanghai km
# 3. 查找以某个坐标为中心,指定半径内的所有地点
GEORADIUS key 经度 纬度 半径 单位 [WITHDIST] [WITHCOORD]
# 示例:查找以北京为中心,1000公里内的城市
GEORADIUS city 116.40 39.90 1000 km
# 4. 获取指定地点的经纬度
GEOPOS key 名称
- 适用场景
-
附近的人、附近的门店(美团 / 饿了么、微信附近的人)
-
打车软件计算司机与乘客距离
-
地理围栏、位置推荐
二、HyperLogLog(基数统计类型)
- 核心定义
专门用来做基数统计 (统计一组数据中不重复元素的个数 ),占用内存极小(仅 12KB),支持海量数据统计。
特点:有极小误差(约 0.81%),不存储原始数据,只存统计结构,适合不需要精确值的场景。
- 核心命令
sql
# 1. 添加元素
PFADD key 元素1 元素2 ...
# 示例:统计网站UV(添加用户ID)
PFADD uv:20260331 user1 user2 user3 user1
# 2. 统计基数(不重复元素数量)
PFCOUNT key
# 示例:查询今日独立访客数
PFCOUNT uv:20260331
# 3. 合并多个HyperLogLog(统计多天总UV)
PFMERGE destKey sourceKey1 sourceKey2 ...
# 示例:合并3天UV
PFMERGE uv:total uv:20260330 uv:20260331
- 适用场景
-
统计网站 / APP 独立访客(UV)
-
统计页面日活、月活
-
海量数据去重计数(不要求 100% 精确)
三、Bitmap(位图类型)
- 核心定义
本质是String 类型的特殊用法 ,把字符串当成二进制位(bit)数组 来用,极致节省内存。
1 个 Byte = 8bit,1MB 可以存储 838 万 个 bit 位 ,适合存储二状态数据(是 / 否、0/1)。
- 核心命令
sql
# 1. 设置指定位置的bit值(0或1)
SETBIT key offset value
# 示例:记录用户1001今日签到(offset=用户ID,1=已签到)
SETBIT sign:20260331 1001 1
# 2. 获取指定位置的bit值
GETBIT key offset
# 示例:查询用户1001是否签到
GETBIT sign:20260331 1001
# 3. 统计key中bit为1的数量
BITCOUNT key [start end]
# 示例:统计今日总签到人数
BITCOUNT sign:20260331
- 适用场景
-
用户签到、打卡统计
-
用户是否在线、是否已读
-
黑名单 / 白名单快速判断
-
海量用户状态标记
4.事务
一、Redis 事务核心特性
一次性,顺序性,排他性,执行一系列的命令!
-
MySQL 中的事务,要么同时成功,要么同时失败,必须保证原子性!
-
Redis 单条命令是保证原子性的,但是 Redis 的事务不保证原子性!
-
Redis 事务是没有隔离级别的概念
1、Redis 的事务机制
事务使用过程
Redis 的事务使用分为三步:
-
开启事务 --
multi -
命令入队 -- ......
-
执行事务 --
exec
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!
正常执行事务
sql
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379> set k2 v2 # 命令入队
QUEUED
127.0.0.1:6379> get k2 # 命令入队
QUEUED
127.0.0.1:6379> set k3 v3 # 命令入队
QUEUED
127.0.0.1:6379> exec # 执行事务
1) OK
2) OK
3) "v2"
4) OK
放弃事务 discard
sql
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discard # 放弃事务
OK
127.0.0.1:6379> get k4 # 事务队列中的命令都不会执行
(nil)
编译型异常(命令有错),事务中所有的命令都不会被执行
sql
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> getset k2 # 命令不正确
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec # 执行事务时,其他命令也不会执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> get k3
(nil)
执行时异常,如果事务队列中存在语法性错误,执行命令的时候,其他命令可以正常执行,错误命令抛出异常
sql
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 # 会执行的时候失败
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec # 执行事务是,第一个命令执行出错,其他命令依旧会执行
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v2"
127.0.0.1:6379> get k2
"v2"
2、Redis 的乐观锁 Watch
悲观锁 & 乐观锁
-
悲观锁:很悲观,认为什么时候都会出问题,无论做什么都会加锁!但是影响效率!
-
乐观锁:很乐观,认为什么时候都不会出问题,所以不会加锁!更新数据时去判断一下,在此期间是否有人修改过这个数据!MySQL 的 version 的使用:先获取 version,更新数据时比较 version,看 version 是否被修改
Redis 的监视测试
1、正常执行成功!
sql
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #监视money对象
OK
127.0.0.1:6379> multi #事务正常结束,数据期间没有发生变动,这个时候就正常执行成功
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
2、多线程修改值,使用 watch 可以当做 Redis 的乐观锁操作!
A 客户端执行命令集合
sql
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
B 客户端执行命令集合
sql
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> exec # 在该命令执行之前,A客户端执行了set命令修改了money的值,就会导致事务执行失败
(nil)
watch 监视下的事务执行失败后,可以 unwatch 解锁,在重新开始监视
sql
127.0.0.1:6379> UNWATCH #事务执行失败,先解锁
OK
127.0.0.1:6379> WATCH money #获取最新的值,再次监视
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> exec # 比对监视的值是否发生变化,如果没有变化,可以执行成功,如果变量被修改了,执行失败
1) (integer) 990
2) (integer) 30