2025超全面Redis笔记!!!
📑2025全网最全的Redis笔记,学完可以直接上项目👏👏,笔记是结合b站博主【狂神说】的redis课程写的,可以结合课程快速学习redis
Redis
- ✨NoSQL
- 1.解耦
- 2.NoSQL的四大分类
- ✨NoSQL和SQL数据库的比较:
- ✨Redis
- 1.Redis安装
- 2.Redis-Linux测试性能
- 3.Redis基础知识
- 4.Redis常见的五大数据类型
- 💫Redis-key
- 💫String(字符串)
- 1.典型案例:
- 2.`i++`案例:
- 3.切片案例:
- 4.setex和setnx
- 5.mset和mget
- 6.getset
- 💫List(列表)
- 1.lpush和rpush
- 2.lpop和rpop
- 3.lindex
- 4.llen
- 5.lrem
- 6.trim
- 7.rpoplpush
- 8.lset
- 9.linsert
- 💫set(集合)
- 1.sadd、smembers、sismember
- 2.scard
- 3.srem
- 4.srandmember
- 5.spop
- 6.smove
- 7.数字集合
- 💫Hash(哈希)
- 1.hset、hget、hmset、hmget、hgetall
- 2.hlen
- 3.hexists
- 4.hkeys
- 5.hincr
- 💫Zset(有序集合)
- 1.zadd
- 2.排序
- 3.rem
- 4.zcount
- 5.Redis三种特殊类型
- 💫geospatial
- 1.geoadd
- 2.geopos
- 3.geodist
- 4.georadius
- 5.georadiusbymember
- 6.geohash
- 7.zrange
- 💫hyperloglog
- 💫Bitmaps
- 1.周一到周日打卡
- 6.事务
- 💫正常执行事务
- 💫放弃事务
- 💫编译型异常
- 💫运行时异常
- 💫监控(Watch)
- 7.在python中运用
- 💫String(字符串)
- 1.set
- 2.mset(*args,**kwargs)
- 3.mget(key,*args)
- 💫Lits(列表)
- 💫Hash(字典)
- 💫管道/事务
- 💫Django中配置
✨NoSQL
一类新出现的数据库(not only sql)
NoSQL 是 “Not Only SQL” 的缩写,它指的是一类非关系型数据库管理系统,这类系统的设计目的是为了处理大规模数据集和多种数据模型,尤其是在大数据和云计算环境下。与传统的关系型数据库(如 MySQL, Oracle 等)相比,NoSQL 数据库通常具有更好的可扩展性和灵活性。
NoSQL 数据库的特点包括:
-
灵活的数据模型:NoSQL 数据库支持多种数据存储方式,包括键值对、文档、列族以及图形等。这种灵活性使得 NoSQL 能够适应不同类型的应用场景。
-
水平可扩展性:大多数 NoSQL 数据库设计为可以通过添加更多的机器来线性增加性能,这种方式被称为横向扩展或“向外扩展”,相对于关系型数据库常用的纵向扩展或“向上扩展”(通过增强单台服务器的硬件性能)而言。
-
分布式架构:很多 NoSQL 数据库天生就是分布式的,它们能够跨多台服务器进行数据分片,并且通常具备自动故障恢复的能力。
-
弱一致性:不同于关系型数据库追求强一致性的 ACID 属性(原子性、一致性、隔离性、持久性),许多 NoSQL 数据库遵循 BASE 理论(基本可用性、软状态、最终一致性),这意味着在某些情况下,系统可以牺牲一定的数据一致性来换取更高的可用性和分区容错性。
-
简化操作:一些 NoSQL 数据库减少了复杂的表连接操作,简化了查询语言,这有助于提高读写效率,但也可能限制了一些复杂查询的实现。
-
- 不支持SQL语法
- 泛指非关系型数据库
- 存储结构跟传统关系数据库中的那种关系表完全不同马,nosql中存储的数据都是KV形式(键值对)
- NoSQL的世界中没有一种通用的语言,每种nosql数据库都有自己的Api和语法,以及擅长的业务场景
常见的 NoSQL 数据库类型及其代表产品有:
- 键值存储:Redis, DynamoDB
- 文档存储:MongoDB, Couchbase
- 列族存储:Cassandra, HBase
- 图形数据库:Neo4j, ArangoDB
NoSQL 数据库特别适合于需要快速开发迭代、高并发访问、海量数据存储及分析的场合,例如社交网络、物联网(IoT)、实时数据分析等领域。不过,选择使用 NoSQL 还是关系型数据库应基于具体应用需求和技术栈考虑
1.解耦
1.方便扩展(数据之间没有关系,很好扩展)
2.大数据量高性能(redis 一秒写8万次,读取11万,NoSQL的缓存记录级,是一种细粒度的缓存性能会比较高)
3.数据类型是多样型的!(不需要事先设计数据库!随取随用)
4.传统 RDBMS和NoSQL
传统的 RDBMS
- 结构化组织
- SQL
- 数据和关系都存在单独的表中
- 操作操作,数据定义语言
- 严格的一致性
- 基础的事物
NoSQL
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,列存储,文档存储,图形数据库(社会关系)
- 最终一致性
- CAP定理和BASE (异地多话)初级架构师!
- 高性能,高可用,高可扩
2.NoSQL的四大分类
KV键值对:
- 新浪:Redis
- 美团:Redis+Tair
- 阿里、百度:Redis+memecache
文档型数据库(bson格式和json一样)
-
MongoDB(一般必须要掌握)
MongoDB是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档
MongoDB是一个介于关系型数据库和非关系型数据中中间的产品,是非关系数据库中功能最丰富,最像关系型数据库的
分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 |
---|---|---|---|---|---|
键值(Key-Value) | Tokyo Cabinet/Tyrant | 内容缓存,主要用于处理大量数据库的高访问负载,也用于一些日志系统等 | Key指向Value的键值对,通常用hash table来实现 | 查找速度快 | 数据无结构化,通常只被当作字符串或者二进制数据 |
列存储数据库 | Cassandra,HBase | 分布式的文件系统 | 以列簇式存储,将同一列数据存在一起 | 查找速度快,可扩展性强,更容易进行分布式扩展 | 功能相对局限 |
文档型数据库 | CouchDB,MongoDB | Web应用(Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) | Key-Value对应的键值对,Value为结构化数据 | 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 | 查询性能不高,而且缺乏统一的查询语法 |
图形(Graph)数据库 | Neo4J,InfoGrid,Infinite Graph | 社交网络,推荐系统等,专注于构建关系谱 | 结构图 | 利用图机构相关算法,比如最短路径寻址,N度关系查找 | 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案 |
✨NoSQL和SQL数据库的比较:
NoSQL 和 SQL(关系型)数据库各有优势和适用场景。它们之间的主要区别可以从多个维度来比较,包括数据模型、一致性、扩展性、事务支持等方面。
-
数据模型
-
SQL 数据库:使用预定义的模式(Schema),通常是表格形式的数据结构,其中数据被组织成行和列,并且表之间可以通过外键建立关联。
-
NoSQL 数据库:提供更加灵活的数据模型,如键值对、文档、列族和图形等,通常不需要固定的模式,允许存储非结构化或半结构化的数据。
-
-
一致性与可用性
-
SQL 数据库:倾向于强一致性的 ACID 属性(原子性、一致性、隔离性、持久性),这意味着所有操作要么全部成功,要么全部失败,确保了数据的一致性和可靠性。
-
NoSQL 数据库:很多 NoSQL 数据库遵循 BASE 理论(基本可用性、软状态、最终一致性),牺牲了一定程度的一致性来换取更高的可用性和分区容错性。这使得它们更适合分布式环境下的应用。
-
-
扩展方式
-
SQL 数据库:传统上通过垂直扩展(增加单个服务器的资源,比如 CPU 或内存)来提高性能,虽然现在一些现代的关系型数据库也支持一定程度的水平扩展。
-
NoSQL 数据库:设计时就考虑到了水平扩展能力,即通过添加更多的机器来分散负载和存储容量,从而实现更好的性能和更大的存储空间。
-
-
查询语言
-
SQL 数据库:使用标准的 SQL 语言进行查询,这种语言功能强大,可以执行复杂的多表连接和其他高级操作。
-
NoSQL 数据库:查询语言因类型而异,通常比 SQL 更简单,但可能缺乏处理复杂关系的能力。例如,文档数据库可能只支持简单的键值查找或者基于 JSON 的查询。
-
-
事务支持
-
SQL 数据库:sql对事物的支持非常完善,全面支持事务,保证一组操作要么完全成功,要么完全不发生。
-
NoSQL 数据库:部分 NoSQL 数据库开始支持事务,但其事务模型往往不如 SQL 数据库那么成熟或广泛支持。
-
-
适用场景
-
SQL 数据库:适用于需要严格遵守数据完整性和事务管理的应用,如金融交易系统。
-
NoSQL 数据库:适合于大规模、高性能、高可扩展性的应用,特别是那些数据结构不确定或经常变化的情况,如社交网络、内容管理系统、实时分析平台等。
-
两者在不断地取长补短,呈现融合趋势
选择哪种类型的数据库取决于具体的应用需求,包括数据特性、读写模式、一致性要求以及预期的规模和增长等因素。在某些情况下,同时采用 SQL 和 NoSQL 数据库也是常见的做法,以充分利用各自的优势。
✨Redis
Redis(Remote Dictionary Server)是一个开源的、使用ANSI C语言编写、支持网络的、基于键值对存储的内存数据库。它通常被用作数据库、缓存和消息中间件,具有高性能的数据读写能力。
以下是 Redis 的一些主要特性和应用场景:
-
特性
-
内存存储:Redis 将数据存储在内存中,这使得它的读写速度非常快
-
持久化选项:虽然 Redis 是一个内存数据库,但它提供了两种将数据持久化到磁盘的方法:RDB(快照)和 AOF(追加文件)
-
多数据类型支持:除了简单的键值存储,Redis 还支持丰富的数据类型
-
事务处理:Redis 提供了事务的支持,可以保证一组命令的原子执行
-
发布/订阅模式:支持消息队列功能,可以用于构建实时通信系统
-
高可用性:通过主从复制和哨兵机制来实现数据冗余和故障转移
-
集群模式:Redis 集群能够自动分片,提供线性扩展的能力,以应对大量数据和高并发访问
-
-
丰富的数据结构
支持多种数据类型,适应不同业务需求:
- String:文本、数字、二进制数据
- Hash:键值对集合(如存储对象)
- List:有序列表(消息队列、最新动态)
- Set:无序唯一集合(标签、共同好友)
- Sorted Set:带权重的有序集合(排行榜)
- Stream:消息流(类似Kafka)
- Geospatial:地理位置(附近的人)
- Bitmap/HyperLogLog:统计类场景(UV统计)
-
应用场景
-
缓存:这是 Redis 最常见的用途之一,它可以显著提高应用程序的响应时间
-
会话存储:在分布式应用环境中,Redis 可以用来存储用户的会话信息
-
排行榜/计数器:利用有序集合可以很容易地创建排行榜或进行各种统计计数
-
消息队列:Redis 的列表数据结构非常适合用来实现轻量级的消息队列
-
实时分析:由于其快速的数据处理能力,Redis 也适用于需要实时数据分析的应用场景
-
地理位置应用:Redis 的地理空间索引功能可以用来处理地理位置相关的查询
-
分布式锁:通过
List
(LPUSH/RPOP)或Stream
实现异步任务队列、延迟队列 -
实时数据处理:统计在线用户数(基于
Set
)、实时热门搜索(Sorted Set
)、UV去重(HyperLogLog
) -
限流与频率控制:通过
INCR
和过期时间限制API调用频率(如短信防刷)
-
-
Redis优势
- 性能极高 - Redis能读的速度是110000次/s,写的速度是81000次/s
- 丰富的数据类型 - Redis支持二进制案例的 Strings,Lists,Hashes,Sets及Ordered Sets数据类型操作
- 原子 - Redis的所有操作都是原子性的
- 丰富的特性 - Redis还支持 publish/subscribe,通知,key过期等
总之,Redis 因其高速度、灵活性和丰富的特性,在很多互联网服务中扮演着重要角色。无论是作为缓存层加速数据访问,还是作为一个轻量级的消息队列系统,或者是直接作为数据库使用,Redis 都能提供强大的支持。
1.Redis安装
-
Windows安装教程:Redis如何在windows中简单安装?-CSDN博客
-
Linux安装教程:Linux目录介绍+Redis部署(小白篇)-CSDN博客
-
终端测试
启动服务命令:
redis-server
C:\Users\29496>redis-cli 127.0.0.1:6379> set k1 v1 OK 127.0.0.1:6379> get k1 "v1" 127.0.0.1:6379>
2.Redis-Linux测试性能
程序一般放在opt/
目录下,所以Redis也是放在该目录下,可以使用ps
命令查看进程:
ps -ef|grep redis
关闭redis
状态
shutdown
exit
测试性能
redis-benchmark
是一个压力测试工具,官方自带的性能测试工具,例如:
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
# 对我们的10万个请求进行写入测试
# 100个并发客户端
# 每次写入3字节
# 只有一台服务器来处理这些请求,单机性能
# 所有请求再3毫秒处理完成
# 每秒处理 次请求
参数详细可看:Redis 性能测试 | 菜鸟教程
3.Redis基础知识
Redis是单线程,明白Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来是实现,就使用单线程了
Redis是C语言写的,官方提供的数据为10w+的QPS,完全不必同样是使用Key-value的Memecache差!
Redis为什么单线程还这么快?
误区1:高性能的服务器一定是多线程
误区2:多线程(CPU上下文会切换)一定比单线程效率高!
先去 CPU >内存 > 硬盘的速度要有所了解
核心:redis是将所有的数据存放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是再一个CPU上的,在内存情况下,这个就是最佳的方案!
操作示例,redis
默认有16个数据库,可以使用select
切换数据库,如:
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> dbsize
(integer) 0
例如:写入、获取和大小
127.0.0.1:6379[3]> set name qingjiang
OK
127.0.0.1:6379[3]> get name
"qingjiang"
127.0.0.1:6379[3]> dbsize
(integer) 1
查看数据所有的key
127.0.0.1:6379[3]> keys *
1) "name"
清除当前数据库flushdb
127.0.0.1:6379[3]> flushdb
OK
127.0.0.1:6379[3]> keys *
(empty array)
注意:清除所有数据库内容,使用flushall
4.Redis常见的五大数据类型
常用的命令:redis命令手册
💫Redis-key
语法
Redis 键命令的基本语法如下:
redis 127.0.0.1:6379> COMMAND KEY_NAME
实例
127.0.0.1:6379> set name zhangsan # 存入数据,以Key-Value的形式
OK
127.0.0.1:6379> keys * # 查询所有的key
1) "name"
127.0.0.1:6379> get name # 获取key为`name`的Value
"zhangsan"
127.0.0.1:6379> exists name # 判断`name`这个key是否存在,如果存在输出(integer) 1
(integer) 1
127.0.0.1:6379> exists name1 # 判断`name1`这个key是否存在,如果不存在输出(integer) 0
(integer) 0
Redis-key基本命令
序号 | 命令及描述 |
---|---|
1 | DEL key 该命令用于在 key 存在是删除 key。 |
2 | DUMP key 序列化给定 key ,并返回被序列化的值。 |
3 | EXISTS key 检查给定 key 是否存在。 |
4 | EXPIRE key seconds 为给定 key 设置过期时间。 |
5 | EXPIREAT key timestamp EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置过期时间。 不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。 |
6 | PEXPIRE key milliseconds 设置 key 的过期时间亿以毫秒计。 |
7 | PEXPIREAT key milliseconds-timestamp 设置 key 过期时间的时间戳(unix timestamp) 以毫秒计 |
8 | KEYS pattern 查找所有符合给定模式( pattern)的 key 。 |
9 | MOVE key db 将当前数据库的 key 移动到给定的数据库 db 当中。 |
10 | PERSIST key 移除 key 的过期时间,key 将持久保持。 |
11 | PTTL key 以毫秒为单位返回 key 的剩余的过期时间。 |
12 | TTL key 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 |
13 | RANDOMKEY 从当前数据库中随机返回一个 key 。 |
14 | RENAME key newkey 修改 key 的名称 |
15 | RENAMENX key newkey 仅当 newkey 不存在时,将 key 改名为 newkey 。 |
16 | TYPE key 返回 key 所储存的值的类型。 |
典型案例:
127.0.0.1:6379> get name # 获取name的value
"zhangsan"
127.0.0.1:6379> expire name 10 # 给定 key 设置过期时间为10秒
(integer) 1
127.0.0.1:6379> ttl name #以秒为单位,返回给定 key 的剩余生存时间
(integer) 5
127.0.0.1:6379> ttl name
(integer) 4
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) 1
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> keys * # 10秒过后可以看到这个值过期了
(empty array)
💫String(字符串)
Redis 字符串数据类型的相关命令用于管理 redis 字符串值,基本语法如下:
语法
redis 127.0.0.1:6379> COMMAND KEY_NAME
实例
redis 127.0.0.1:6379> SET w3ckey redis
OK
redis 127.0.0.1:6379> GET w3ckey
"redis"
在以上实例中我们使用了 SET 和 GET 命令,键为 w3ckey。
常用的 redis String(字符串)
命令:
序号 | 命令及描述 |
---|---|
1 | SET key value 设置指定 key 的值 |
2 | GET key 获取指定 key 的值。 |
3 | GETRANGE key start end 返回 key 中字符串值的子字符 |
4 | GETSET key value 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 |
5 | GETBIT key offset 对 key 所储存的字符串值,获取指定偏移量上的位(bit)。 |
6 | [MGET key1 key2…] 获取所有(一个或多个)给定 key 的值。 |
7 | SETBIT key offset value 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。 |
8 | SETEX key seconds value 将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。 |
9 | SETNX key value 只有在 key 不存在时设置 key 的值。 |
10 | SETRANGE key offset value 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。 |
11 | STRLEN key 返回 key 所储存的字符串值的长度。 |
12 | [MSET key value key value …] 同时设置一个或多个 key-value 对。 |
13 | [MSETNX key value key value …] 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。 |
14 | PSETEX key milliseconds value 这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。 |
15 | INCR key 将 key 中储存的数字值增一。 |
16 | INCRBY key increment 将 key 所储存的值加上给定的增量值(increment) 。 |
17 | INCRBYFLOAT key increment 将 key 所储存的值加上给定的浮点增量值(increment) 。 |
18 | DECR key 将 key 中储存的数字值减一。 |
19 | DECRBY key decrement key 所储存的值减去给定的减量值(decrement) 。 |
20 | APPEND key value 如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。 |
1.典型案例:
127.0.0.1:6379> set key1 value1 # 存入键值对
OK
127.0.0.1:6379> get key1 # 获取key1的值
"value1"
127.0.0.1:6379> exists key1 # 判断是否存在
(integer) 1
127.0.0.1:6379> append key1 "hello" # 向是key1的value值末尾追加字符
(integer) 11
127.0.0.1:6379> get key1 # 结果
"value1hello"
127.0.0.1:6379> strlen key1 # 查看key1,所储存的字符串值的长度
(integer) 11
2.i++
案例:
自增
127.0.0.1:6379> set view 0
OK
127.0.0.1:6379> get view
"0"
127.0.0.1:6379> incr view # 调用incr view实现自增
(integer) 1
127.0.0.1:6379> incr view
(integer) 2
127.0.0.1:6379> incr view
(integer) 3
127.0.0.1:6379> get view
"3"
自减
127.0.0.1:6379> set view 3
OK
127.0.0.1:6379> get view
"3"
127.0.0.1:6379> decr view
(integer) 2
127.0.0.1:6379> decr view
(integer) 1
127.0.0.1:6379> decr view
(integer) 0
127.0.0.1:6379> decr view
(integer) -1
127.0.0.1:6379> get view
"-1"
根据指定步长自增
127.0.0.1:6379> set view -1
OK
127.0.0.1:6379> get view
"-1"
127.0.0.1:6379> incrby view 10 # 指定步长为10,自增
(integer) 9
127.0.0.1:6379> incrby view 10
(integer) 19
127.0.0.1:6379> get view
"19"
根据指定步长自减就自行完成,不就是在多加两字母by
3.切片案例:
127.0.0.1:6379> set key1 "zhangsan is cute"
OK
127.0.0.1:6379> get key1
"zhangsan is cute"
127.0.0.1:6379> getrange key1 0 7 # 取出zhangsan
"zhangsan"
指定位置覆盖原字符串
127.0.0.1:6379> set key1 "zhangsan is cute"
OK
127.0.0.1:6379> get key1
"zhangsan is cute"
127.0.0.1:6379> setrange key1 11 " very cute" # 将cute替换成了very cute
(integer) 21
127.0.0.1:6379> get key1
"zhangsan is very cute"
4.setex和setnx
setex(set with expire)
:设置过期时间
setnx(set if not exist)
:不存在在设置(在分布式锁中会常常使用)
127.0.0.1:6379> setex key1 30 "hello" # 设置key1的值为hello hello 30 秒后过期
OK
127.0.0.1:6379> ttl key1
(integer) 22
127.0.0.1:6379> ttl key1
(integer) 21
127.0.0.1:6379> setnx mykey "redis" # 如果mykey 不存在,则创建
(integer) 1
127.0.0.1:6379> keys *
1) "mykey"
127.0.0.1:6379> setnx mykey "redis" # 如果mykey 已经存在,则创建失败
(integer) 0
5.mset和mget
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 # 同时创建多个键值对
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k2"
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> mget k1 k2 k3 # 同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 # msetnx 是一个原子性的操作,要么一起成功,要么一起失败
(integer) 0
127.0.0.1:6379> get k4
(nil)
对象
127.0.0.1:6379> set user:1 {name:zhangsan,age:3} # 设置一个user:1 对象值为接送字符来保存一个对象
OK
127.0.0.1:6379> get user:1
"{name:zhangsan,age:3}"
127.0.0.1:6379> mset user:1:name lisi user:1:age 3
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "lisi"
2) "3"
注意:这里key也有一个巧妙的设计:user:{id}:{filed}
,如果此设计在redis
中完全是可以的
6.getset
先get
然后再set
127.0.0.1:6379> getset db redis # 如果不存在值,则返回 nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db MongoDB # 注意优先级,所有先获取db原来的值,再设置新的值
"redis"
127.0.0.1:6379> get db
"MongoDB"
数据结构是相同的!
String
类似的使用场景:value除了是我们的字符串还可以是我们的数字
- 计数器
- 统计多单位的数量
- 粉丝数
- 对象缓存存储
💫List(列表)
在redis里面,我们可以把list玩成,栈、队列、阻塞队列!所有的list命令都是用l开头的,列表相关的基本命令:
序号 | 命令及描述 |
---|---|
1 | [BLPOP key1 key2 ] timeout 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
2 | [BRPOP key1 key2 ] timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
3 | BRPOPLPUSH source destination timeout 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
4 | LINDEX key index 通过索引获取列表中的元素 |
5 | LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素 |
6 | LLEN key 获取列表长度 |
7 | LPOP key 移出并获取列表的第一个元素 |
8 | [LPUSH key value1 value2] 将一个或多个值插入到列表头部 |
9 | LPUSHX key value 将一个或多个值插入到已存在的列表头部 |
10 | LRANGE key start stop 获取列表指定范围内的元素 |
11 | LREM key count value 移除列表元素 |
12 | LSET key index value 通过索引设置列表元素的值 |
13 | LTRIM key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 |
14 | RPOP key 移除并获取列表最后一个元素 |
15 | RPOPLPUSH source destination 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 |
16 | [RPUSH key value1 value2] 在列表中添加一个或多个值 |
17 | RPUSHX key value 为已存在的列表添加值 |
1.lpush和rpush
# 将三个数据添加至list中(左边开始)
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1 # 获取list中的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1 # 通过区间获取值,注意添加的顺序
1) "three"
2) "two"# 将一个值或多个值,插入到列表尾部(右边)
127.0.0.1:6379> rpush list right
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
注意:lpush
是从左边添加插入数据,rpush
是从右边插入数据
2.lpop和rpop
# 先自信添加数据
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> lpop list # 移除列表中的第一个元素(左边)
"three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "right"
127.0.0.1:6379> rpop list # 移除列表中最后一个元素(右边)
"right"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3.lindex
127.0.0.1:6379> lrange list 0 -1 # 如果没有数据,需要自己先创建并添加
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1 # 通过索引来获取列表下标的值
"one"
127.0.0.1:6379> lindex list 0
"two"
4.llen
127.0.0.1:6379> llen list # 获取列表长度
(integer) 2
5.lrem
移除指定的值,运用如:取关 uid
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lpush list right
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "right"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 one
(integer) 1
127.0.0.1:6379> lrange list 0 -1 # 移除list中指定个数的value值,精确匹配
1) "right"
2) "three"
3) "two"127.0.0.1:6379> lpush list three
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "right"
4) "two"
127.0.0.1:6379> lrem list 2 three # 移除多个 three值
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "right"
2) "two"
6.trim
截断、类似语言中的切片,不过截取后,会将这个列表改变
127.0.0.1:6379> rpush list "hello"
(integer) 1
127.0.0.1:6379> rpush list "hello1"
(integer) 2
127.0.0.1:6379> rpush list "hello2"
(integer) 3
127.0.0.1:6379> rpush list "hello3"
(integer) 4
127.0.0.1:6379> ltrim list 1 2 # 通过下标截取指定长度,这个list已经被改变了,截断了只剩下截取的元素
OK
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello2"
7.rpoplpush
命令:
# 移除最后一个元素并添加到新列表
rpoplpush 【列表1】 【列表2】
案例:
127.0.0.1:6379> rpush list "hello"
(integer) 1
127.0.0.1:6379> rpush list "hello2"
(integer) 2
127.0.0.1:6379> rpush list "hello3"
(integer) 3
127.0.0.1:6379> rpoplpush list otherlist # 移除列表最后一个元素,将他移动到新列表中
"hello3"
127.0.0.1:6379> lrange list 0 -1 # 原来的列表
1) "hello"
2) "hello2"
127.0.0.1:6379> lrange otherlist 0 -1 # 新列表
1) "hello3"
8.lset
将列表中指定下标的值替换为另一个值,更新操作
127.0.0.1:6379> exists list # 判断这个列表是否存在
(integer) 0
127.0.0.1:6379> lset list 0 item # 在更新的时候,如果这个列表不存在,则会报错
(error) ERR no such key
127.0.0.1:6379> lpush list v1
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "v1"
127.0.0.1:6379> lset list 0 item # 更新列表中下标是0的值
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
127.0.0.1:6379> lset list 1 item # 如果列表中没有相应的下标值,则也会报错
(error) ERR index out of range
9.linsert
将某个具体的value插入到列表中某个元素的前面或者后面
127.0.0.1:6379> rpush list "one"
(integer) 1
127.0.0.1:6379> rpush list "two"
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "two"
127.0.0.1:6379> linsert list before "one" "1" # 在one的前面插入1这个值
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "1"
2) "one"
3) "two"
127.0.0.1:6379> linsert list after "one" "right" # 在one的后面插入 right 这个值
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "1"
2) "one"
3) "right"
4) "two"
小结
- list实际上是一个链表,before Node after ,left,right 都可以插入值
- 如果key不存在,创建新的链表
- 如果key存在,新增内容
- 如果移除了所有值,空链表,也代表不存在
- 在两边插入或者改动值,效率最高!中间元素,相对来说效率会低一点
消息排队!消息队列(Lpush Rpop),栈(Lpush Lpop)
💫set(集合)
set中的值不能重复,且是无序的
Redis 集合基本命令:
序号 | 命令及描述 |
---|---|
1 | [SADD key member1 member2] 向集合添加一个或多个成员 |
2 | SCARD key 获取集合的成员数 |
3 | [SDIFF key1 key2] 返回给定所有集合的差集 |
4 | [SDIFFSTORE destination key1 key2] 返回给定所有集合的差集并存储在 destination 中 |
5 | [SINTER key1 key2] 返回给定所有集合的交集 |
6 | [SINTERSTORE destination key1 key2] 返回给定所有集合的交集并存储在 destination 中 |
7 | SISMEMBER key member 判断 member 元素是否是集合 key 的成员 |
8 | SMEMBERS key 返回集合中的所有成员 |
9 | SMOVE source destination member 将 member 元素从 source 集合移动到 destination 集合 |
10 | SPOP key 移除并返回集合中的一个随机元素 |
11 | [SRANDMEMBER key count] 返回集合中一个或多个随机数 |
12 | [SREM key member1 member2] 移除集合中一个或多个成员 |
13 | [SUNION key1 key2] 返回所有给定集合的并集 |
14 | [SUNIONSTORE destination key1 key2] 所有给定集合的并集存储在 destination 集合中 |
15 | [SSCAN key cursor MATCH pattern] [COUNT count] 迭代集合中的元素 |
1.sadd、smembers、sismember
127.0.0.1:6379> sadd myset "hello" # 添加数据
(integer) 1
127.0.0.1:6379> sadd myset "hello" # hello数据已经添加,set类型是不允许有重复的元素,所以返回0
(integer) 0
127.0.0.1:6379> sadd myset "orange"
(integer) 1
127.0.0.1:6379> sadd myset "green"
(integer) 1
127.0.0.1:6379> smembers myset # 查看集合中的数据
1) "hello"
2) "orange"
3) "green"
127.0.0.1:6379> sismember myset hello # 判断某一个值是不是在myset集合中
(integer) 1
127.0.0.1:6379> sismember myset pink # pink不在myset集合中
(integer) 0
2.scard
127.0.0.1:6379> scard myset # 集合的元素个数
(integer) 3
3.srem
127.0.0.1:6379> sadd myset "hello" # 添加数据
(integer) 1
127.0.0.1:6379> sadd myset "orange"
(integer) 1
127.0.0.1:6379> sadd myset "green"
(integer) 1
127.0.0.1:6379> srem myset hello
(integer) 1
127.0.0.1:6379> scard myset
(integer) 2
127.0.0.1:6379> smembers myset
1) "orange"
2) "green"
4.srandmember
随机抽取set元素
127.0.0.1:6379> sadd myset "orange"
(integer) 1
127.0.0.1:6379> sadd myset "green"
(integer) 1
127.0.0.1:6379> sadd myset "pink"
(integer) 1
127.0.0.1:6379> smembers myset
1) "orange"
2) "green"
3) "pink"
127.0.0.1:6379> srandmember myset # 随机抽取一个元素
"green"
127.0.0.1:6379> srandmember myset
"pink"
127.0.0.1:6379> srandmember myset 2 # 随机抽取两个元素,指定元素个数
1) "orange"
2) "pink"
5.spop
127.0.0.1:6379> smembers myset
1) "orange"
2) "green"
3) "pink"
127.0.0.1:6379> spop myset # 随机删除集合中的一个元素
"green"
127.0.0.1:6379> smembers myset
1) "orange"
2) "pink"
6.smove
127.0.0.1:6379> sadd myset "red"
(integer) 1
127.0.0.1:6379> sadd myset "orange"
(integer) 1
127.0.0.1:6379> sadd myset "green"
(integer) 1
127.0.0.1:6379> sadd myset "blue"
(integer) 1
127.0.0.1:6379> smove myset myset1 "green" # 将myset中的green 移动到myset1中
(integer) 1
127.0.0.1:6379> smembers myset # 查看myset中的元素
1) "red"
2) "orange"
3) "blue"
127.0.0.1:6379> smembers myset1 # 查看myset1中的元素
1) "green"
7.数字集合
例子:共同好友(并集)
数字集合类:
- 差集 sdiff
- 交集
- 并集
127.0.0.1:6379> sadd myset "a"
(integer) 1
127.0.0.1:6379> sadd myset "b"
(integer) 1
127.0.0.1:6379> sadd myset "c"
(integer) 1
127.0.0.1:6379> sadd myset "d"
(integer) 1
127.0.0.1:6379> smembers myset
1) "a"
2) "b"
3) "c"
4) "d"
127.0.0.1:6379> sadd myset1 "b"
(integer) 1
127.0.0.1:6379> sadd myset1 "c"
(integer) 1
127.0.0.1:6379> sadd myset1 "e"
(integer) 1
127.0.0.1:6379> sadd myset1 "f"
(integer) 1
# 集合myset与集合myset1的差集 (去掉myset中与myset1的共同元素和myset1所有元素,保留myset其余元素)
127.0.0.1:6379> sdiff myset myset1
1) "a"
2) "d"
127.0.0.1:6379> sinter myset myset1 # 集合myset与集合myset1的并集(共同元素)
1) "b"
2) "c"
127.0.0.1:6379> sunion myset myset1 # 集合myset与集合myset1的并集(两个集合合并)
1) "d"
2) "a"
3) "b"
4) "f"
5) "e"
6) "c"
myset中的元素: “a”、“b”、“c”、“d”
myset1中的元素:“e”、“b”、“c”、“f”
共同元素:“b”、“c”
微博、A用户将所有关注的人放在一个set集合中,将它粉丝也放在一个集合中,
共同关注,共同爱好,二度好友(六度分割理论)
💫Hash(哈希)
Map集合,Key-map!时候这个值是一个map集合,本质和String类型没有太大区别,还是一个简单的key-value
redis hash 基本的相关命令:
序号 | 命令及描述 |
---|---|
1 | [HDEL key field2 field2] 删除一个或多个哈希表字段 |
2 | HEXISTS key field 查看哈希表 key 中,指定的字段是否存在。 |
3 | HGET key field 获取存储在哈希表中指定字段的值/td> |
4 | HGETALL key 获取在哈希表中指定 key 的所有字段和值 |
5 | HINCRBY key field increment 为哈希表 key 中的指定字段的整数值加上增量 increment 。 |
6 | HINCRBYFLOAT key field increment 为哈希表 key 中的指定字段的浮点数值加上增量 increment 。 |
7 | HKEYS key 获取所有哈希表中的字段 |
8 | HLEN key 获取哈希表中字段的数量 |
9 | [HMGET key field1 field2] 获取所有给定字段的值 |
10 | [HMSET key field1 value1 field2 value2 ] 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
11 | HSET key field value 将哈希表 key 中的字段 field 的值设为 value 。 |
12 | HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值。 |
13 | HVALS key 获取哈希表中所有值 |
14 | HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对。 |
1.hset、hget、hmset、hmget、hgetall
其命令类似String类型
127.0.0.1:6379> hset myhash field zhangsan # set 一个具体 key-value
(integer) 1
127.0.0.1:6379> hget myhash field # 获取field的对应值
"zhangsan"
127.0.0.1:6379> hmset myhash field zhangsan field1 lisi field2 wangwu # set多个key-value
OK
127.0.0.1:6379> hmget myhash field field1 field2 # 获取多个字段值
1) "zhangsan"
2) "lisi"
3) "wangwu"
127.0.0.1:6379> hgetall myhash # 获取全部数据
1) "field"
2) "zhangsan"
3) "field1"
4) "lisi"
5) "field2"
6) "wangwu"
2.hlen
127.0.0.1:6379> hmset myhash field zhangsan field1 lisi field2 wangwu
OK
127.0.0.1:6379> hlen myhash
(integer) 3
3.hexists
127.0.0.1:6379> hmset myhash field zhangsan field1 lisi field2 wangwu
OK
127.0.0.1:6379> hexists myhash field
(integer) 1
127.0.0.1:6379> hexists myhash field3
(integer) 0
4.hkeys
分开获取所有key和value
127.0.0.1:6379> hmset myhash field zhangsan field1 lisi field2 wangwu
OK
127.0.0.1:6379> hkeys myhash # 获取所有key
1) "field"
2) "field1"
3) "field2"
127.0.0.1:6379> hvals myhash # 获取所有value
1) "zhangsan"
2) "lisi"
3) "wangwu"
5.hincr
127.0.0.1:6379> hset myhash field 1
(integer) 1
127.0.0.1:6379> hincrby myhash filed 1 # 指定增量为1
(integer) 1
127.0.0.1:6379> hincrby myhash filed 1
(integer) 2
127.0.0.1:6379> hincrby myhash filed -1 # 指定减量为-1
(integer) 2
127.0.0.1:6379> hsetnx myhash field1 hello # 如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash field1 world # 如果存在则不可以设置
(integer) 0
总结:hash变更的数据uer name age,尤其是用户信息之类的,经常变动的信息!hash更适合于对象的存储,String更加适合字符串存储
💫Zset(有序集合)
Redis 有序集合(sorted set)的基本命令:
序号 | 命令及描述 |
---|---|
1 | [ZADD key score1 member1 score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
2 | ZCARD key 获取有序集合的成员数 |
3 | ZCOUNT key min max 计算在有序集合中指定区间分数的成员数 |
4 | ZINCRBY key increment member 有序集合中对指定成员的分数加上增量 increment |
5 | [ZINTERSTORE destination numkeys key key …] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 |
6 | ZLEXCOUNT key min max 在有序集合中计算指定字典区间内成员数量 |
7 | [ZRANGE key start stop WITHSCORES] 通过索引区间返回有序集合成指定区间内的成员 |
8 | [ZRANGEBYLEX key min max LIMIT offset count] 通过字典区间返回有序集合的成员 |
9 | [ZRANGEBYSCORE key min max WITHSCORES] [LIMIT] 通过分数返回有序集合指定区间内的成员 |
10 | ZRANK key member 返回有序集合中指定成员的索引 |
11 | [ZREM key member member …] 移除有序集合中的一个或多个成员 |
12 | ZREMRANGEBYLEX key min max 移除有序集合中给定的字典区间的所有成员 |
13 | ZREMRANGEBYRANK key start stop 移除有序集合中给定的排名区间的所有成员 |
14 | ZREMRANGEBYSCORE key min max 移除有序集合中给定的分数区间的所有成员 |
15 | [ZREVRANGE key start stop WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到底 |
16 | [ZREVRANGEBYSCORE key max min WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序 |
17 | ZREVRANK key member 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
18 | ZSCORE key member 返回有序集中,成员的分数值 |
19 | [ZUNIONSTORE destination numkeys key key …] 计算给定的一个或多个有序集的并集,并存储在新的 key 中 |
20 | [ZSCAN key cursor MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值) |
1.zadd
127.0.0.1:6379> zadd myset 1 one # 添加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three # 添加多个值
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
2.排序
127.0.0.1:6379> zadd salary 2500 xiaohong
(integer) 1
127.0.0.1:6379> zadd salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> zadd salary 500 lihua
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf # 显示全部用户 从小到大
1) "lihua"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores # 显示全部的用户 从小到大
1) "lihua"
2) "500"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "5000"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores # 显示工资小于或等于2500员工的升序排序
1) "lihua"
2) "500"
3) "xiaohong"
4) "2500"
3.rem
127.0.0.1:6379> zrange salary 0 -1
1) "lihua"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrem salary xiaohong # 移除xiaohong
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "lihua"
127.0.0.1:6379> zcard salary # 获取有序集合中的个数
(integer) 2
4.zcount
127.0.0.1:6379> zadd myset 1 hello
(integer) 1
127.0.0.1:6379> zadd myset 2 world 3 zhangsan
(integer) 2
127.0.0.1:6379> zcount myset 1 3 # 获取指定区间的成员数量
(integer) 3
127.0.0.1:6379> zcount myset 1 2
(integer) 2
5.Redis三种特殊类型
Redis 除了常见的基本数据类型(如 String、List、Hash、Set、Sorted Set),还提供了三种特殊的数据类型:HyperLogLog(基数统计)、Bitmap(位图)、Geospatial(地理空间)
💫geospatial
命令 | 描述 |
---|---|
Redis GEOHASH 命令 | 返回一个或多个位置元素的 Geohash 表示 |
Redis GEOPOS 命令 | 从key里返回所有给定位置元素的位置(经度和纬度) |
Redis GEODIST 命令 | 返回两个给定位置之间的距离 |
Redis GEORADIUS 命令 | 以给定的经纬度为中心, 找出某一半径内的元素 |
Redis GEOADD 命令 | 将指定的地理空间位置(经度、纬度、名称)添加到指定的key中 |
Redis GEORADIUSBYMEMBER 命令 | 找出位于指定范围内的元素,中心点是由给定的位置元素决定 |
1.geoadd
添加地理位置
规则:两级无法直接添加,我们一般会下载城市数据,直接通过python/java程序一次性导入
有效的经度从-180度到180度
有效的纬度从
-85.05112878
度到85.05112878
度当前坐标位置超出上述指定范围时,该命令将会返回一个错误,例如:
127.0.0.1:6379> geoadd city 39.904989 116.405285 Beijing (error) ERR invalid longitude,latitude pair 39.904989,116.405285 # 错误:纬度 116.405285 超出有效范围(-85 到 85)
注意:存储的是(经度、纬度、名称),命令为
geoadd key 经度 纬度 名称
127.0.0.1:6379> geoadd china:city 116.405285 39.904989 Beijing # 添加单个数据
(integer) 1
127.0.0.1:6379> geoadd china:city 114.13116 22.54836 shenzhen
(integer) 1
127.0.0.1:6379> geoadd china:city 121.49295 31.22337 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 108.40869 30.80788 chongqing 117.26104 31.85117 hefei # 添加多个数据
(integer) 2
经纬度查询:城市经纬度查询-国内城市经度纬度在线查询工具
2.geopos
获取当前定位:一个定位是一个坐标值
127.0.0.1:6379> geopos china:city Beijing # 查询单个值
1) 1) "116.40528291463852"2) "39.9049884229125"
127.0.0.1:6379> geopos china:city Beijing shanghai chongqing # 查询多个值
1) 1) "116.40528291463852"2) "39.9049884229125"
2) 1) "121.49295061826706"2) "31.22337074392616"
3) 1) "108.40868979692459"2) "30.80787925148732"
3.geodist
两人之间的距离,指定单位的参数 unit 必须是以下单位的其中一个:
- m 表示单位为米
- km 表示单位为千米
- mi 表示单位为英里
- ft 表示单位为英尺
127.0.0.1:6379> geodist china:city Beijing shenzhen km # 查看北京到深圳的距离,单位是km
"1942.3977"
127.0.0.1:6379> geodist china:city Beijing shanghai km # 查看北京到上海的距离,单位是km
"1069.2305"
在百度搜索,可以查找到大概的直线距离
4.georadius
以给定的经纬度为中心,找出某一半径内的元素
我附近的人?(获取所有附近的人的地址,定位!)通过半径来查询
获取指定数量的人,200
# 添加四条地理位置,北京市、朝阳区、顺义区、房山区
127.0.0.1:6379> geoadd china:city 116.405285 39.904989 beijing 116.48548 39.9484 zhaoyangqu
(integer) 2
127.0.0.1:6379> geoadd china:city 116.65417 40.1302 shunyiqu 116.14257 39.74786 fangshanqu
(integer) 2
127.0.0.1:6379> georadius china:city 116 39 1000 km # 以116 39 这个经纬度为中心,寻找方圆1000km内的城市
1) "fangshanqu"
2) "beijing"
3) "zhaoyangqu"
4) "shunyiqu"
127.0.0.1:6379> georadius china:city 116 39 500 km
1) "fangshanqu"
2) "beijing"
3) "zhaoyangqu"
4) "shunyiqu"
127.0.0.1:6379> georadius china:city 116 39 500 km withdist # 显示到中间距离的位置
1) 1) "fangshanqu"2) "84.0802"
2) 1) "beijing"2) "106.5063"
3) 1) "zhaoyangqu"2) "113.4230"
4) 1) "shunyiqu"2) "137.6534"
127.0.0.1:6379> georadius china:city 116 39 500 km withdist withcoord count 1 # 筛选指定的结果,一个
1) 1) "fangshanqu"2) "84.0802"3) 1) "116.14257127046585"2) "39.74786105824508"
127.0.0.1:6379> georadius china:city 116 39 500 km withdist withcoord count 2 # 筛选指定的结果,二个
1) 1) "fangshanqu"2) "84.0802"3) 1) "116.14257127046585"2) "39.74786105824508"
2) 1) "beijing"2) "106.5063"3) 1) "116.40528291463852"2) "39.9049884229125"
5.georadiusbymember
# 添加四条地理位置,北京市、朝阳区、顺义区、房山区、上海市、长宁区
127.0.0.1:6379> geoadd china:city 116.405285 39.904989 beijing 116.48548 39.9484 zhaoyangqu
(integer) 2
127.0.0.1:6379> geoadd china:city 116.65417 40.1302 shunyiqu 116.14257 39.74786 fangshanqu
(integer) 2
127.0.0.1:6379> geoadd china:city 121.49295 31.22337 shanghai 121.42462 31.22036 changningqu
(integer) 2
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km # 以北京市为中心 1000km的距离的位置
1) "fangshanqu"
2) "beijing"
3) "zhaoyangqu"
4) "shunyiqu"
6.geohash
返回一个或多个位置元素的Geohash表示
该命令将返回11个字符的Geohash字符串
将二维经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近
# 添加四条地理位置,北京市、朝阳区、顺义区、房山区、上海市、长宁区
127.0.0.1:6379> geoadd china:city 116.405285 39.904989 beijing 116.48548 39.9484 zhaoyangqu
(integer) 2
127.0.0.1:6379> geoadd china:city 116.65417 40.1302 shunyiqu 116.14257 39.74786 fangshanqu
(integer) 2
127.0.0.1:6379> geoadd china:city 121.49295 31.22337 shanghai 121.42462 31.22036 changningqu
(integer) 2
127.0.0.1:6379> geohash china:city beijing shunyiqu
1) "wx4g0b7xrt0"
2) "wx4uq92zr50"
7.zrange
GEO底层的实现原理其实就是Zset!可以使用Zset命令来操作geo
127.0.0.1:6379> zrange china:city 0 -1 # 查询数据
1) "changningqu"
2) "shanghai"
3) "fangshanqu"
4) "beijing"
5) "zhaoyangqu"
6) "shunyiqu"
127.0.0.1:6379> zrem china:city beijing # 移除指定元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "changningqu"
2) "shanghai"
3) "fangshanqu"
4) "zhaoyangqu"
5) "shunyiqu"
💫hyperloglog
什么是基数?
- A{1,3,5,7,8,7}
- B{1,3,5,7,8}
基数(不重复的元素) = 5,可以接收误差
简介
Redis 2.8.9版本就更新了Hyperloglog数据结构
Redis Hyperloglog 基数统计的算计
优点:占用的内存是固定,
2^64
不同的元素的技术,只需要废12KB
内存!如果要从内存角度来比较的话Hyperloglog首先!网页UV(一个人访问一个网站多次,但是还是算作一个人)
传统的方式,
set
保存用户的id
,然后就可以统计set
中的元素数量作为标准判断!这个方式如果保存大量的用户
id
,就会比较麻烦,我们的目的是为了计较,而不是保存用户id;0.81%错误率! 统计UV任务,可以忽略不计的
常用命令
序号 | 命令及描述 |
---|---|
1 | [PFADD key element element …] 添加指定元素到 HyperLogLog 中。 |
2 | [PFCOUNT key key …] 返回给定 HyperLogLog 的基数估算值。 |
3 | [PFMERGE destkey sourcekey sourcekey …] 将多个 HyperLogLog 合并为一个 HyperLogLog |
测试使用
127.0.0.1:6379> pfadd mykey a b c d e f g h i j # 创建第一组元素 mykey
(integer) 1
127.0.0.1:6379> pfcount mykey # 统计 mykey 元素的基数数量
(integer) 10
127.0.0.1:6379> pfadd mykey1 i j z x c v b n m # 创建第二组元素 mykey1
(integer) 1
127.0.0.1:6379> pfcount mykey1
(integer) 9
127.0.0.1:6379> pfmerge mykey2 mykey mykey1 # 合并两组 mykey mykey1 => mykey2 并集
OK
127.0.0.1:6379> pfcount mykey2 # 查看并集的数量
(integer) 15
如果允许容错,那么一定可以使用Hyperloglog
如果不允许容错,就使用set或者自己的数据类型即可
💫Bitmaps
位存储
统计用户信息,活跃,不活跃!登录、未登录!打卡,365天打卡,两个状态的,都可以使用Bitmaps
Bitmaps位图,数据结构,都是操作二进制位来进行记录,就只用0和1两个状态
365天 = 365bit 1字节=8bit 46个字节左右
1.周一到周日打卡
打卡记录
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 0
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0
查看某一天是否有打卡
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 5
(integer) 0
统计操作,统计打卡的天数
127.0.0.1:6379> bitcount sign
(integer) 4
6.事务
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行
一次性、顺序性、排他性,执行一些列的命令
Redis事务没有隔离级别的概念
所有的命令在事务中,并没有直接被执行!只有发起执行命令的适合才会执行!
Redis单条命令保存原子性的,但是事务不保证原子性
redis事务:
- 开启事务(multi)
- 命令入队(……)
- 执行事务(exec)
💫正常执行事务
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务
1) OK
2) OK
3) "v1"
💫放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> discard # 放弃事务
OK
127.0.0.1:6379> get k2
(nil)
💫编译型异常
在事务中的代码有问题,命令报错,事务中所有命令都不会被执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> getset k2 # 命令报错
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec # 事务中所有命令不被执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> keys *
(empty array)
💫运行时异常
(1/0),如果事务队列中存在语法性,那么执行命令的适合,其他命令是可以正常执行的,错误命令抛出异常
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> set k2 1
QUEUED
127.0.0.1:6379(TX)> incr k2
QUEUED
127.0.0.1:6379(TX)> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) (integer) 2
💫监控(Watch)
悲观锁:
- 很悲观,认为什么时候都会出问题,无论做什么都会加锁
乐观锁:
- 很乐观,认为什么时候都不会出问题,所以不会上锁!,更新数据的时候去判断一下,在此期间是否有人修改过这个数据
- 获取version
- 更新的时候比较version
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(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
测试多线程修改值,使用watch
可以当做redis
的乐观锁操作!
线程1
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> watch money # 监视 money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec # 执行之前,另外一个线程(线程2),修改了money的值,这个时候,就会导致事务执行失败
(nil)
线程2
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> get money
"1000"
乐观锁:
127.0.0.1:6379> unwatch # 1.如果发现事务执行失败,就先解锁
OK
127.0.0.1:6379> watch money # 获取最新的值,再次监视,select version
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec # 3.比对监视值是否发生了变化,如果没有变化,那么可以执行成功,如果变化就执行失败
1) (integer) 990
2) (integer) 10
7.在python中运用
首先需要在python环境中安装redis
,存储的是Bytes格式
pip install redis
- 连接方式1
import redis# 拿到redis链接
coon = redis.Redis(host='127.0.0.1', port=6379)# 朝内存数据库存放key是name,value是zrg,的字符串
coon.set('name', 'hello')
print(coon.get('name'))
- 连接方式2
import redis# 连接池
pool = redis.ConnectionPool(host='127.0.0.1',port=6379)# 从池子里拿一个连接
coon = redis.Redis(connection_pool=pool)
coon.set('k1','zrg')
# 存储多个
coon.mset({'k1':'zrg','k2':'zrg'})
print(coon.get('k1'))
在python中使用redis的命令都是很非常类似的,直接使用实例化对象,然后直接使用即可
💫String(字符串)
1.set
set(key,value,ex=None,px=None,nx=False,xx=False)
# 在redis中设置值,默认,不存在则创建,存在则修改:
参数:
ex
过期时间(秒)px
过期时间(毫秒)nx
如果设置为True
,则只有key
不存在时,当前set
操作才执行,值存在,就修改不了,执行没效果xx
如果设置为True
,则只有key
存在时,当前set
操作才执行,值存在才能修改,值不存在,不会设置新值
coon.set('k1','zrg')
2.mset(*args,**kwargs)
批量设置(存)跟set区别,set只能放一个
coon.mset({'k1':'zrg','k2':'zrg'})
3.mget(key,*args)
接收以位置传的多个参数
values = coon.mget('name','Language','Title','Pages')
其他命令:
方法 | 解释 |
---|---|
getset(key,value) | 设置新值并获取原来的值 |
getrange(key,start,end) | 获取子序列,根据字节取,非字符 |
setrange(key,offset,value) | 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)参数 offset,字符串的索引,字节(一个汉字三个字节) value,要设置的值 |
setbit(key,offset,value) | 对key对应值得二进制表示的位进行操作 参数value的值只能是1或0. |
getbiyt(key,offset,value) | 获取key对应的值 |
strlen(name) | 返回字符串的长度 |
incr(self,name,amount=1) | 自增 不写默认1 文章阅读数用这个,提高网站性能。amount必须是整数 |
append(key,value) | 朝字符串里追加内容 |
💫Lits(列表)
方法 | 解释 |
---|---|
lpush(key,values) | 在name对应的list中添加元素,每个新的元素都添加到列表的最左边 |
lpushx(name,value) | 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边 |
llen(name) | name对应的list元素的个数 |
linsert(name, where, refvalue, value)) | 在name对应的列表的某一个值前或后插入一个新值,where ,BEFORE或AFTER(小写也可以), refvalue ,标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准),value ,要插入的数据 |
lset(name, index, value) | 对name对应的list中的某一个索引位置重新赋值 |
lrem(name, value, num) | 在name对应的list中删除指定的值 |
lpop(name) | 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素 |
lindex(name, index) | 在name对应的列表中根据索引获取列表元素 |
lrange(name, start, end) | 在name对应的列表分片获取数据 |
ltrim(name, start, end) | 在name对应的列表中移除没有在start-end索引之间的值 |
rpoplpush(src, dst) | 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边 |
blpop(keys,timeout) | 将多个列表排列,按照从左到右去pop对应列表的元素 |
brpoplpush(src,dst, timeout=0) | 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧 |
案例:
# 由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要:# 1、获取name对应的所有列表# 2、循环列表
# 但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能:
import redis
conn=redis.Redis(host='127.0.0.1',port=6379)
# conn.lpush('test',*[1,2,3,4,45,5,6,7,7,8,43,5,6,768,89,9,65,4,23,54,6757,8,68])
# conn.flushall()
def scan_list(name,count=2):index=0while True:data_list=conn.lrange(name,index,count+index-1)if not data_list:returnindex+=countfor item in data_list:yield item
print(conn.lrange('test',0,100))
for item in scan_list('test',5):print('---')print(item)
💫Hash(字典)
方法 | 解释 |
---|---|
hset(name,key,value) | name对应的hash中设置一个键值对(不存在,则创建,否则,修改) |
hmset(name,mapping) | 批量设值 |
hget(name,key) | 在对应的hash中根据key获取value |
hmget(name,keys,*args) | 批量的取值 |
hgetall(name) | 取出name对应hash的所有键值 |
hlen(name) | 获取对应的hash中键值对的个数 |
hkeys(name) | 返回左右key |
hvals(name) | 返回所有value |
hexisits(name,key) | 判断这个hash的key在不在 |
hinrby(name,key,amount=1) | 限制范围 |
hscan(name,cursor=0,match=None,count=None) | 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完 |
💫管道/事务
redis.py 默认在执行每一次请求都会创建(连接池申请连接)和断开(归还连接池)一次连续操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次()事务pipline都是原子性(都成功或都不成功)操作
- multi开启事务 —> 内容 —> execute执行
import redis# 事务
coon = redis.Redis(host='127.0.0.1',port=6379)# 拿到一个管道,transaction、
pi = coon.pipeline(transaction=True)
# 说明是批量命令
pi.multi()
pi.set('author','作者')
pi.set('publish','出版社')
# 执行
pi.execute()
💫Django中配置
安装
pip install django-redis
方式1,在utils
中新建一个redis_coon.py
import redisPOOL = redis.ConnectionPool(host='127.0.0.1',port=6379)
在view.py
中运用
import redis
from utils import redis_coon# 从池子里拿一个连接
coon = redis.Redis(connection_pool=redis_coon.POOL)
方式2,在配置文件中
CACHES = {"default": {"BACKEND": "django.core.cache.backends.redis.RedisCache","LOCATION": "redis://127.0.0.1:6379",}
}
报错:在配置过程中,配置好了redis
缓存,但是出现了ModuleNotFoundError: No module named 'distutils'
distutils在python标准库从2012年就开始断更了,python12中已经移除该库,可以安装以下库进行解决
pip install setuptools --upgrade
“setuptools”是一个处理Python软件包的工具包,它依赖于 distutils。安装 setuptools可以间接解决没有“distutils”的问题
然后django服务就可以正常启动了
示例
from django_redis import get_redis_connectiondef redis_demo(requset):# 实例化对象conn = get_redis_connection()# 统计数量,每访问一次数量加1res = conn.incrby('count')print(conn.keys("*")) # 查看数据库中的所有keyprint(res)return HttpResponse(f'您是我们第:{res}个用户')
使用cache
也可以
from django.core.cache import cache
cache.set('count1', "hello")
配置多个redis
缓存设置
CACHES = {# 默认缓存"default": {"BACKEND": "django_redis.cache.RedisCache",# 项目上线时,需要调整这里的路径# "LOCATION": "redis://:密码@IP地址:端口/库编号","LOCATION": "redis://:@127.0.0.1:6379/0","OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient","CONNECTION_POOL_KWARGS": {"max_connections": 10},}},# 提供给admin运营站点的session存储"session": {"BACKEND": "django_redis.cache.RedisCache","LOCATION": "redis://:@127.0.0.1:6379/1","OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient","CONNECTION_POOL_KWARGS": {"max_connections": 10},}},# 提供存储短信验证码"sms_code":{"BACKEND": "django_redis.cache.RedisCache","LOCATION": "redis://:@127.0.0.1:6379/2","OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient","CONNECTION_POOL_KWARGS": {"max_connections": 10},}}
}
注意: get_redis_connection()
不传值,默认使用的是default
conn = get_redis_connection() # 不传值,默认使用的是default
# conn = get_redis_connection('sms_code') #指定别名
作为一个刚毕业的新人👑,我也正面临着就业难题,一直没能找到合适的专业技术岗位。想请教各位职场前辈,能否分享一下你们的面试经验和求职心得?
可以私信笨笨轻松熊