第一章 Redis 的介绍和安装
NoSQL(NoSQL = Not Only SQL),意即 不仅仅是SQL
,是一项全新的数据库理念,泛指 非关系型的数据库。
1.1 为什么需要学习 NOSQL
随着互联网的高速崛起,网站的用户群的增加,访问量的上升,传统(关系型)数据库上都开始出现了性能瓶颈,Web 程序不再仅仅专注在功能上,同时也在追求性能。所以 NOSQL 数据库应运而上,具体表现为对如下三高问题的解决:
- High performance - 对数据库高并发读写的需求。web2.0 网站要根据用户个性化信息来实时生成动态页面和提供动态信息,所以基本上无法使用动态页面静态化技术,因此数据库并发负载非常高,往往要达到每秒上万次读写请求。关系数据库应付上万次 SQL查询还勉强顶得住,但是应付上万次 SQL 写数据请求,硬盘IO就已经无法承受了。其实对于普通的 BBS 网站,往往也存在对高并发写请求的需求,例如网站的实时统计在线用户状态,记录热门帖子的点击次数,投票计数等,因此这是一个相当普遍的需求。
- Huge Storage - 对海量数据的高效率存储和访问的需求。类似 Facebook,twitter,Friendfeed 这样的 SNS 网站,每天用户产生海量的用户动态,以 Friendfeed 为例,一个月就达到了 2.5亿 条用户动态,对于关系数据库来说,在一张 2.5亿 条记录的表里面进行 SQL 查询,效率是极其低下乃至不可忍受的。再例如大型 Web 网站的用户登录系统,例如腾讯,盛大,动辄数以亿计的帐号,关系数据库也很难应付。
- High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求。在基于 Web 的架构当中,数据库是最难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,你的数据库却没有办法像 Web Server 和 App Server 那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。对于很多需要提供24小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数据迁移,为什么数据库不能通过不断的添加服务器节点来实现扩展呢?
1.2 主流的 NOSQL 产品
NoSQL数据库的四大分类如下:
-
键值(Key-Value)存储数据库:
相关产品:Tokyo Cabinet/Tyrant、Redis、Voldemeort、Berkeley DB 典型应用:内容缓存,主要用于处理大量数据的高访问负载 数据模型:一系列键值对 优势:快速查询 劣势:存储的数据缺少结构化
-
列存储数据库(分布式):
相关产品:Cassandra、HBase、Riak 典型应用:分布式的文件系统 数据模型:以列簇式存储,将同一列数据存在一起 优势:查找速度快,可扩展性强,更容易进行分布式扩展 劣势:功能相对局限
-
文档型数据库:
相关产品:CouchDB、MongoDB 典型应用:Web应用(与Key-Value类似,Value是结构化的) 数据模型:一系列键值对 优势:数据结构要求不严格 劣势:查询性能不高,而且缺乏统一的查询语法
-
图形(Graph)数据库:
相关产品:Neo4J、InfoGrid、Infinite Graph 典型应用:社交网络 数据模型:图结构 优势:利用图结构相关算法 劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案
1.3 NOSQL 的特点
在大数据存取上具备关系型数据库无法比拟的性能优势,例如:
- 易扩展。NoSQL 数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。也无形之间,在架构的层面上带来了可扩展的能力。
- 大数据量,高性能。NoSQL 数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单、在内存中存取数据。
- 灵活的数据模型。NoSQL 无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的 Web2.0 时代尤其明显。
- 高可用。NoSQL 在不太影响性能的情况,就可以方便的实现高可用的架构。比如 Cassandra,HBase 模型,通过复制模型也能实现高可用。
1.4 Redis 概述
Redis 是用 C语言 开发的一个开源的高性能 键值对(key-value)数据库,数据是保存在内存里面的。官方提供测试数据,50个并发执行100000个请求,读的速度是 110000次/s,写的速度是 81000次/s,且 Redis 通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止 Redis 支持的键值数据类型如下:
- 字符串类型 string(最常用)
- 散列类型 hash
- 列表类型 list
- 集合类型 set
- 有序集合类型 sortedset
Redis 的应用场景:
- 缓存(数据查询、短连接、新闻内容、商品内容等等)。
- 任务队列。(秒杀、抢购、12306等等)。
- 数据过期处理(可以精确到毫秒, 短信验证码)。
- 分布式集群架构中的 session 分离 session 服务器里面。
- 聊天室的在线好友列表。
- 应用排行榜。
- 网站访问统计。
Redis 的特点:高性能:Redis 读的速度是 11W次/s,写的速度是 8.1W次/S。 原子性:保证数据的准确性。持久存储:支持两种方式的持久化,RDB 和 AOF, 可以把内存中的数据持久化到磁盘中。支持主从: master-slave 架构,可以实现负载均衡、高可用。支持集群:从3.0版本开始支持。
1.5 Window 版 Redis 的安装(了解)
官方提倡使用 Linux 版的 Redis,所以官网只提供了 Linux 版的 Redis 下载,我们可以从 GitHub上下载 Window 版的 Redis,具体链接地址如下:
- 官网下载地址:http://redis.io/download
- github下载地址:https://github.com/MSOpenTech/redis/tags
但是笔者不选择解压安装,以如下方式进行安装:
下载好了之后,双击开始安装,直接下一步下一步安装即可,记着把设置环境变量前面的复选框勾上,其他采用默认设置就好了:
安装步骤可以参考这篇博文:https://www.cnblogs.com/liuqingzheng/p/9831331.html
也可以采用解压 Redis 压缩包的安装方式,见到如下目录结构:
安装好之后,黑窗口测试:
1.6 Linux 版本 Redis 的安装
将下载好的安装包上传到 bigdata04 机器的 /data/soft 目录下:
解压:tar -zxvf redis-5.0.9.tar.gz
编译+安装:cd redis-5.0.9、make、make install。注意:由于 Redis 需要依赖于C语言环境,如果读者安装的 Centos 镜像是精简版,会缺失C语言的依赖,所以需要安装C语言环境才可以编译成功。笔者在这使用的 Centos 镜像是完整版,里面是包含C语言环境的,所以就不存在这个问题了。
修改 redis.conf 配置文件:vi redis.conf,修改内容如下:
daemonize yes
logfile /data/soft/redis-5.0.9/log
bind 127.0.0.1 192.168.61.103
daemonize 参数的值默认是 no,表示在前台启动 Redis,但是 Redis 是一个数据库,我们希望把它放到后台运行,所以将参数的值改为 yes。logfile 参数的值默认为空,表示 Redis 会将日志输出到 /dev/null(黑洞) 里面,也就是不保存了,建议在这设置一个日志路径记录 Redis 的日志,便于后期排查问题。bind 参数可以绑定指定 ip,这样就可以通过这里指定的 ip 来访问 Redis 服务了,可以在后面指定当前机器的本地回环地址(127.0.0.1)和内网地址(192.168.61.103),指定本地回环地址是为了能够在本机自己连自己比较方便。指定内网地址是为了能够让公司局域网内的其它服务器也能连到这个 Redis,如果你这台机器有外网地址的话不建议在这配置,因为使用外网地址的话就不安全了,容易受到网络攻击。
启动 redis:redis-server redis.conf
验证:
[root@bigdata04 redis-5.0.9]# ps -ef | grep redis
root 34734 1 0 18:40 ? 00:00:00 redis-server 127.0.0.1:6379
root 34742 1731 0 18:40 pts/0 00:00:00 grep --color=auto redis
连接 redis 数据库:redis-cli
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379>
[root@bigdata04 redis-5.0.9]# redis-cli -h 192.168.61.103 -p 6379
192.168.61.103:6379>
[root@bigdata04 redis-5.0.9]# redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379>
停止 redis 数据库:
[root@bigdata04 redis-5.0.9]# redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> shutdown
not connected>
[root@bigdata04 redis-5.0.9]# ps -ef | grep redis
root 34881 1731 0 18:51 pts/0 00:00:00 grep --color=auto redis
[root@bigdata04 redis-5.0.9]# redis-server redis.conf
[root@bigdata04 redis-5.0.9]# redis-cli shutdown
[root@bigdata04 redis-5.0.9]# ps -ef | grep redis
root 34897 1731 0 18:52 pts/0 00:00:00 grep --color=auto redis
在可以在 Windows 机器上使用图形化工具进行连接:
1.7 Redis 基础命令
启动 redis 服务,使用 redis-cli 客户端连到 redis 数据库里面:redis-server redis.conf、redis-cli
退出客户端:quit、exit。不过我还是习惯使用 Ctrl+C 退出客户端
帮助命令:help
获得符合规则的键:keys 例如:keys *、keys a*、在生产环境下建议禁用 keys 命令,因为这个命令会查询过滤 redis 中的所有数据,可能会造成服务阻塞,影响 redis 执行效率。如果有类似的查询需求建议使用 scan。scan 命令是一个基于游标的迭代器。这意味着命令每次被调用都需要使用上一次调用返回的游标作为该次调用的游标参数,以此来延续之前的迭代过程。当 scan 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。scan 命令是一个基于游标的迭代器。这意味着命令每次被调用都需要使用上一次调用返回的游标作为该次调用的游标参数,以此来延续之前的迭代过程。当 scan 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。
过滤:scan 0 match a* count 10
判断键是否存在:exists 例如:
127.0.0.1:6379> exists a1
(integer) 1
127.0.0.1:6379> exists c1
(integer) 0
删除键:del 例如:del a1。也支持删除多个 key,例如:del b2 b1 a2
获得键值的类型:type,例如:type a2。这个命令可以帮我们快速识别某一个 key 中存储的数据是什么类型的,因为针对存储了不同类型值的 key,操作的命令是不一样的。
1.8 多数据库特性
Redis 默认支持 16 个数据库,通过 databases 参数控制的,这个参数在 redis.conf 配置文件中,如下:
每个数据库对外都是以一个从0开始的递增数字命名,不支持自定义。Redis 默认选择的是0号数据库,可以通过 select 命令切换。例如:select 1
一般在工作中会使用 2~3
个数据库,可以根据业务类型来分库,不同业务的数据存到不同的库里面,还有一种用法是,一个库作为测试库,一个库作为正式库,如果没有特殊需求,一般使用0号数据库就可以了,这个库使用起来比较方便,默认就是0号库,不需要使用 select 切换。具体在工作中怎么用都行,只要理解它的特性就可以了。但是有一点需要注意:多个数据库之间并不是完全隔离的,如果使用 flushall 命令,则会清空 redis 中所有数据库内的数据。并且我们在 redis 中使用多个库,并不能提高 redis 的存储能力,因为默认这16个库共用 redis 的内存存储空间,如果想要提高 redis 的存储能力,需要给我们的服务器增加内存才可以。如果只想清空当前数据库中的数据,可以使用 flushdb。
第二章 Redis 的数据类型
redis 中存储的数据是以 key-value 的形式存在的,其中 value 支持5种数据类型,在日常开发中主要使用比较多的有字符串、哈希、字符串列表、字符串集合四种类型,其中最为常用的是字符串类型。
- 字符串(String)
- 哈希(hash) 类似 HashMap
- 字符串列表(list)
- 字符串集合(set) 类似 HashSet
- 有序的字符串集合(sorted-set或者叫zset)
2.1 Redis 字符串(String)
string 是 redis 最基本的类型,用的也是最多的,一个 key 对应一个 value。应用场景:
- 缓存功能:字符串最经典的使用场景,redis 作为缓存层,MySQL 作为储存层,绝大部分请求数据都是在 redis 中操作,由于 redis 具有支撑高并发特性,所以缓存通常能起到加速读写和降低后端压力的作用。
- 计数器功能:比如视频播放次数,点赞次数。
- ID 递增。
常见命令:
命令 | 描述 |
---|---|
SET key value(重点) | 设置指定 key 的值 |
GET key(重点) | 获取指定 key 的值 |
DEL key | 删除key |
GETSET key value | 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 |
SETEX key seconds value(重点) | 将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。 |
SETNX key value | 只有在 key 不存在时设置 key 的值。 |
INCR key(重点) | 将 key 中储存的数字值增一。 |
INCRBY key increment | 将 key 所储存的值加上给定的增量值(increment) 。 |
DECR key | 将 key 中储存的数字值减一。 |
DECRBY key decrement | key 所储存的值减去给定的减量值(decrement) 。 |
应用举例: |
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379> set str a
OK
127.0.0.1:6379> get str
"a"
127.0.0.1:6379> mset str1 a1 str2 a2
OK
127.0.0.1:6379> mget str1 str2
1) "a1"
2) "a2"
127.0.0.1:6379> set num 1
OK
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> get num
"2"
127.0.0.1:6379> decr num
(integer) 1
127.0.0.1:6379> get num
"1"
127.0.0.1:6379> incrby num 2
(integer) 3
127.0.0.1:6379> get num
"3"
127.0.0.1:6379> incrbyfloat num 2.1
"5.1"
127.0.0.1:6379> incrbyfloat num -2.1
"3"
127.0.0.1:6379> get str
"a"
127.0.0.1:6379> strlen str
(integer) 1
127.0.0.1:6379> set str abcd
OK
127.0.0.1:6379> strlen str
(integer) 4
127.0.0.1:6379> getset str edfg
"abcd"
商品编号、订单号采用string的递增数字特性生成。
定义商品编号key:product:id
192.168.101.3:7003> INCR product:id
(integer) 2
192.168.101.3:7003> INCR product:id
(integer) 3
使用 string 的问题:假设有 User 对象以 JSON 序列化的形式存储到 Redis 中,User 对象有 id、username、password、age、name 等属性,存储的过程如下: 保存、更新。User 对象 ==> json(string) ==>redis
,如果在业务上只是更新 age 属性,其他的属性并不做更新我应该怎么做呢? 如果仍然采用上边的方法在传输、处理时会造成资源浪费,hash 可以很好的解决这个问题。
2.2 Redis 哈希(Hash)
Redis 中 hash 是一个键值对集合。 Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。Redis 存储 hash 可以看成是 String key 和 String value 的 map 容器,也就是说把值看成 map 集合。它特别适合存储对象,相比较而言,将一个对象类型存储在 Hash 类型里要比存储在 String 类型里占用更少的内存空间并方便存取整个对象。
key value
u1 name zs
age 18
u2 name ls
age 19
Map<String,Map>
用一个对象来存储用户信息,商品信息,订单信息等等。 常见命令如下:
命令 | 命令描述 |
---|---|
hset key filed value | 将哈希表 key 中的字段 field 的值设为 value |
hmset key field1 value1 [field2 value2]…(重点) | 同时将多个 field-value (字段-值)对设置到哈希表 key 中 |
hget key filed | 获取存储在哈希表中指定字段的值 |
hmget key filed1 filed2 (重点) | 获取多个给定字段的值 |
hdel key filed1 [filed2] (重点) | 删除一个或多个哈希表字段 |
hlen key | 获取哈希表中字段的数量 |
del key | 删除整个hash(对象) |
HGETALL key (重点) | 获取在哈希表中指定 key 的所有字段和值 |
HKEYS key | 获取所有哈希表中的字段 |
HVALS key | 获取哈希表中所有值 |
应用举例:
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379> hset user:1 name amo age 18
(integer) 2
127.0.0.1:6379> hget user:1 name
"amo"
127.0.0.1:6379> hget user:1 age
"18"
127.0.0.1:6379> hmset user:2 name jerry age 32
OK
127.0.0.1:6379> hmget user:2 name age
1) "jerry"
2) "32"
127.0.0.1:6379> hmget user:1 name age
1) "amo"
2) "18"
127.0.0.1:6379> hgetall user:2
1) "name"
2) "jerry"
3) "age"
4) "32"
127.0.0.1:6379> hexists user:2 name
(integer) 1
127.0.0.1:6379> hexists user:2 city
(integer) 0
127.0.0.1:6379> hincrby user:2 age 2
(integer) 34
127.0.0.1:6379> hdel user:2 age
(integer) 1
127.0.0.1:6379> hset user:2 city beijing
(integer) 1
127.0.0.1:6379> hkeys user:2
1) "name"
2) "city"
127.0.0.1:6379> hvals user:2
1) "jerry"
2) "beijing"
127.0.0.1:6379> hlen user:2
(integer) 2
hset 和 hmset:
2.3 Redis 列表(List)
ArrayList 使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。 LinkedList 使用双向链表方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快。然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。
列表类型(list)可以存储一个有序的字符串列表(链表)
,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为O(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。应用场景:
- 如好友列表,粉丝列表,消息队列,最新消息排行等。
- rpush 方法就相当于将消息放入到队列中,lpop/rpop 就相当于从队列中拿去消息进行消费 。
常见命令:
命令 | 命令描述 |
---|---|
lpush key value1 value2…(重点) | 将一个或多个值插入到列表头部(左边) |
rpush key value1 value2…(重点) | 在列表中添加一个或多个值(右边) |
lpop key(重点) | 左边弹出一个 相当于移除第一个 |
rpop key(重点) | 右边弹出一个 相当于移除最后一个 |
llen key | 返回指定key所对应的list中元素个数 |
LINDEX key index | 通过索引获取列表中的元素 |
LINSERT key BEFORE| AFTER pivot value | 在列表的元素前或者后插入元素 |
应用举例: |
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379> lpush list1 a
(integer) 1
127.0.0.1:6379> lpush list1 b
(integer) 2
127.0.0.1:6379> lpop list1
"b"
127.0.0.1:6379> lpop list1
"a"
127.0.0.1:6379> lpop list1
(nil)
127.0.0.1:6379> rpush list2 x
(integer) 1
127.0.0.1:6379> rpush list2 y
(integer) 2
127.0.0.1:6379> rpop list2
"y"
127.0.0.1:6379> rpop list2
"x"
127.0.0.1:6379> lpush list3 a b c d
(integer) 4
127.0.0.1:6379> llen list3
(integer) 4
127.0.0.1:6379> lrange list3 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> lindex list3 1
"c"
127.0.0.1:6379> lset list3 1 m
OK
127.0.0.1:6379> lrange list3 0 -1
1) "d"
2) "m"
3) "b"
4) "a
127.0.0.1:6379> linsert list3 before "m" "g"
(integer) 5
127.0.0.1:6379> lrange list3 0 -1
1) "d"
2) "g"
3) "m"
4) "b"
5) "a"
2.4 Redis 集合(Set)
Redis 的 Set 是 string 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中集合是通过哈希表实现的,所以添加、删除,查找的时间复杂度都是 O(1)。集合中最大的成员数为2的32次方-1 (4294967295, 每个集合可存储40多亿个成员)。Redis 还提供了多个集合之间的交集、并集、差集的运算,特点:无序+唯一。 应用场景:
- 投票记录
- 共同好友、共同兴趣、分类标签
常见命令:
命令 | 命令描述 |
---|---|
sadd key member1 [member2] (重点) | 向集合添加一个或多个成员 |
srem key member1 [member2] | 移除一个成员或者多个成员 |
smembers key | 返回集合中的所有成员,查看所有 |
SCARD key | 获取集合的成员数 |
SPOP key | 移除并返回集合中的一个随机元素 |
SDIFF key1 [key2] (重点) | 返回给定所有集合的差集 |
SUNION key1 [key2] (重点) | 返回所有给定集合的并集 |
SINTER key1 [key2] (重点) | 返回给定所有集合的交集 |
应用举例: |
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379> sadd set1 a
(integer) 1
127.0.0.1:6379> sadd set1 b
(integer) 1
127.0.0.1:6379> smembers set1
1) "a"
2) "b"
127.0.0.1:6379> srem set1 a
(integer) 1
127.0.0.1:6379> sismember set1 b
(integer) 1
127.0.0.1:6379> sismember set1 a
(integer) 0
127.0.0.1:6379> sadd set2 a b c
(integer) 3
127.0.0.1:6379> sadd set3 a b x
(integer) 3
127.0.0.1:6379> sdiff set2 set3
1) "c"
127.0.0.1:6379> sdiff set3 set2
1) "x"
127.0.0.1:6379> sinter set2 set3
1) "a"
2) "b"
127.0.0.1:6379> sunion set2 set3
1) "a"
2) "x"
3) "b"
4) "c"
127.0.0.1:6379> scard set3
(integer) 3
127.0.0.1:6379> spop set3
"b"
127.0.0.1:6379> smembers set3
1) "a"
2) "x"
2.5 Redis 有序集合(sorted set) zset
Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是(1)。 集合中最大的成员数为2的32次方-1(4294967295,每个集合可存储40多亿个成员)。特点: 有序(根据分数排序)+唯一。应用场景:
- 排行榜:例如视频网站需要对用户上传的视频做排行榜。
常见命令:
命令 | 命令描述 |
---|---|
ZADD key score member [score member …](重点) | 增加元素 |
ZSCORE key member | 获取元素的分数 |
ZREM key member [member …] | 删除元素 |
ZCARD key | 获得集合中元素的数量 |
ZRANGE key start stop[WITHSCORES] (重点) | 获得排名在某个范围的元素列表 |
ZREVRANGE key start stop (重点) | 按照分数从高到低排序 |
应用举例: |
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379> zadd zset1 5 a
(integer) 1
127.0.0.1:6379> zadd zset1 3 b
(integer) 1
127.0.0.1:6379> zadd zset1 4 c
(integer) 1
127.0.0.1:6379> zscore zset1 a
"5"
127.0.0.1:6379> zrange zset1 0 -1
1) "b"
2) "c"
3) "a"
127.0.0.1:6379> zrevrange zset1 0 -1
1) "a"
2) "c"
3) "b"
127.0.0.1:6379> zincrby zset1 3 a
"8"
127.0.0.1:6379> zscore zset1 a
"8"
127.0.0.1:6379> zcard zset1
(integer) 3
127.0.0.1:6379> zrem zset1 a
(integer) 1
127.0.0.1:6379> zrange zset1 0 -1
1) "b"
2) "c"
第三章 Redis 通用操作小结、发布订阅和持久化
3.1 Redis 通用操作小结
keys *: 查询所有的 key。
exists key:判断是否有指定的key 若有返回1,否则返回0。
expire key 秒数:设置这个key在缓存中的存活时间(重要)。 expireat abc 1626968100 也可以指定时间戳。
ttl key:展示指定 key 的剩余时间。若返回值为 -1:永不过期、若返回值为 -2:已过期或者不存在。
del key:删除指定key(重要)。
rename key 新key:重命名。
type key:判断一个key的类型。
ping:测试连接是否连接。
redis 默认是16个数据库,编号是从0~15。【默认是0号库】
select index:切换库。
move key index:把key移动到几号库(index是库的编号)。
flushdb:清空当前数据库。
flushall:清空当前实例下所有的数据库。
info:返回关于 Redis 服务器的各种信息和统计数值。
# 筛选其中几条重要信息查看一下
# Server
# redis 服务器版本信息
redis_version:5.0.9
# redis 服务的可执行文件路径
executable:/data/soft/redis-5.0.9/redis-server
# 启动redis时使用的配置文件路径
config_file:/data/soft/redis-5.0.9/redis.conf
# Clients
# 已连接客户端数量
connected_clients:2
# Memory
# redis 目前存储数据使用的内存
used_memory_human:857.25K
# redis 可以使用的内存总量,和服务器的内存有关
total_system_memory_human:1.78G
# Keyspace
# db0表示0号数据库,keys:表示0号数据库的key总量,expires:表示0号数据库失效被删除的key总量
db0:keys=10,expires=0,avg_ttl=0
db3:keys=1,expires=0,avg_ttl=0
设置密码: vi redis.conf、
redis-cli shutdown、redis-server redis.conf、redis-cli、auth admin
禁用命令:
Redis 监控命令-monitor:monitor 可以监控我们对 redis 的所有操作,如果在线上的服务器上打开了这个功能,这里面就会频繁打印出来我们对 redis 数据库的所有操作,这样会影响 redis 的性能,所以说要慎用。但是在某些特殊的场景下面它是很有用的,排查问题:redis-cli monitor | grep key。
3.2 订阅发布机制【了解】
Redis 发布订阅(pub/sub)是进程间一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。Redis 客户端可以订阅任意数量的频道。相关的命令如下:
序号 | 命令及描述 |
---|---|
1 | PUBLISH channel message 将信息发送到指定的频道。 |
2 | SUBSCRIBE channel [channel …] 订阅给定的一个或多个频道的信息。 |
3 | UNSUBSCRIBE [channel [channel …]] 指退订给定的频道 |
开启两个客户端,A客户端如下: | |
![]() | |
B客户端如下: | |
![]() | |
Redis 发布订阅(pub/sub)是进程间一种消息通信模式,工作里面一般使用 MQ。 |
3.3 Redis的持久化【面试】
Redis 的高性能是由于其将所有数据都存储在了内存中,为了使 Redis 在重启之后仍能保证数据不丢失,需要将数据 从内存中同步到硬盘(文件)中,这一过程就是持久化。Redis 支持两种方式的持久化,一种是 RDB 方式,一种是 AOF 方式。可以单独使用其中一种或将二者结合使用。
RDB 持久化机制:
RDB持久化是指在 指定的时间间隔内 将内存中的数据集快照写入磁盘。这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为 dump.rdb。 这种方式是默认 已经开启了,不需要配置。
在 redis.conf 配置文件中有如下配置:
其中,上面配置的是 RDB 方式数据持久化时机:
关键字 | 时间(秒) | key修改数量 | 解释 |
---|---|---|---|
save | 900 | 1 | 每900秒(15分钟)至少有1个key发生变化,则dump内存快照 |
save | 300 | 10 | 每300秒(5分钟)至少有10个key发生变化,则dump内存快照 |
save | 60 | 10000 | 每60秒(1分钟)至少有10000个key发生变化,则dump内存快照 |
这里面的三个时机哪个先满足都会执行快照操作。 |
AOF 持久化机制:
AOF 持久化机制会将每一个收到的写命令都通过 write 函数追加到文件中,默认的文件名是 appendonly.aof。 这种方式默认是没有开启的,要使用时候需要配置。
然后重新启动 Redis 服务,在开启 AOF 持久化机制后,默认会在目录下产生一个 appendonly.aof 文件,如下:
配置详解如下:
关键字 | 持久化时机 | 解释 |
---|---|---|
appendfsync | always | 每执行一次更新命令,持久化一次 |
appendfsync | everysec | 每秒钟持久化一次 |
appendfsync | no | 不持久化 |
小结:
- 优点:RDB 是一个非常紧凑(compact)的文件,它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份,RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快(因为其文件要比AOF的小),RDB 的性能要比 AOF 更好。缺点:RDB的持久化不够及时(一定时间间隔),可能会存在数据丢失,RDB 持久化时如果文件过大可能会造成服务器的阻塞,停止客户端请求。
- 优点:AOF 的持久性更加的耐久(可以每秒或每次操作保存一次),AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。AOF 是增量操作。对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积,根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB。
选择:如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,选择 RDB 持久化。如果对数据的完整性要求比较高,选择 AOF。
第四章 Jedis(重点)
Redis 不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如 Java、Python、C、C#、C++、php、Node.js、Go等。 在官方网站里列一些 Java 的客户端,有 Jedis、Redisson、Jredis、JDBC-Redis等,其中官方推荐使用 Jedis 和 Redisson。 在企业中用的最多的就是 Jedis,Jedis 同样也是托管在 github 上,说白了Jedis就是使用Java操作Redis的客户端(工具包)。地址:https://github.com/xetorthio/jedis。
方法 | 解释 |
---|---|
new Jedis(host, port) | 创建jedis对象,参数host是redis服务器地址,参数port是redis服务端口 |
set(key,value) | 设置字符串类型的数据 |
get(key) | 获得字符串类型的数据 |
hset(key,field,value) | 设置哈希类型的数据 |
hget(key,field) | 获得哈希类型的数据 |
lpush(key,values) | 设置列表类型的数据 |
lpop(key) | 列表左面弹栈 |
rpop(key) | 列表右面弹栈 |
sadd(String key, String… members) | 设置set类型的数据 |
zrange(String key, long start, long end) | 获得在某个范围的元素列表 |
del(key) | 删除key |
4.1 单连接方式
我们需要借助于第三方 jar 包 jedis 来操作,首先在 idea 中创建 maven 项目
然后在项目 bd_jredis 中的 pom.xml 文件中添加 jedis 依赖,如下:
<dependencies>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.3</version>
</dependency>
</dependencies>
代码如下:
package com.amo;
import redis.clients.jedis.Jedis;
public class RedisSingle {
public static void main(String[] args) {
/**
* 记得redis机器的防火墙要关闭,IP地址要写机器的内网地址
*/
//1.获取jedis连接
Jedis jedis = new Jedis("192.168.61.103", 6379);
//jedis.auth("admin");
//2.向redis中添加数据,key=name value=amo
jedis.set("name", "amo");
//3.从redis中查询key=imooc的value的值
String value = jedis.get("name");
System.out.println(value);
//4.关闭jedis连接
jedis.close();
}
}
4.2 连接池方式
jedis 连接资源的创建与销毁是很消耗程序性能,所以 jedis 为我们提供了 jedis 的池化技术,jedisPool 在创建时初始化一些连接资源存储到连接池中,使用 jedis 连接资源时不需要创建,而是从连接池中获取一个资源进行 redis 的操作,使用完毕后,不需要销毁该 jedis 连接资源,而是将该资源归还给连接池,供其他请求使用。使用连接池的方式操作 redis,代码如下:
package com.amo;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisPool {
public static void main(String[] args) {
//创建连接池配置对象
JedisPoolConfig poolConfig = new JedisPoolConfig();
//连接池中最大空闲连接数
poolConfig.setMaxIdle(10);
//连接池中创建的最大连接数
poolConfig.setMaxTotal(100);
//创建连接的超时时间
poolConfig.setMaxWaitMillis(2000);
//表示从连接池中获取连接的时候会先测试一下连接是否可用,这样可以保证取出的连接都是可用的
poolConfig.setTestOnBorrow(true);
//获取jedis连接池
JedisPool jedisPool = new JedisPool(poolConfig, "192.168.61.103", 6379);
//从jedis连接池中取出一个连接
Jedis jedis = jedisPool.getResource();
String value = jedis.get("name");
System.out.println(value);
//注意:此处的close方法有两层含义
//1:如果jedis是直接创建的单连接,此时表示直接关闭这个连接
//2:如果jedis是从连接池中获取的连接,此时会把这个连接返回给连接池
jedis.close();
//关闭jedis连接池
jedisPool.close();
}
}
4.3 案例:提取 RedisUtils 工具类
基于 redis 连接池的方式提取 RedisUtils 工具类,代码如下:
package com.amo;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisUtils {
//私有化构造函数,禁止new
private RedisUtils() {
}
private static JedisPool jedisPool = null;
//获取连接 多线程的时候会出问题,所以加上synchronized
public static synchronized Jedis getJedis() {
if (jedisPool == null) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(10);
poolConfig.setMaxTotal(100);
poolConfig.setMaxWaitMillis(2000);
poolConfig.setTestOnBorrow(true);
jedisPool = new JedisPool(poolConfig, "192.168.61.103", 6379);
}
return jedisPool.getResource();
}
//向连接池返回连接
public static void returnResource(Jedis jedis) {
jedis.close();
}
}
使用工具类代码如下:
package com.amo;
import redis.clients.jedis.Jedis;
public class TestRedisUtils {
public static void main(String[] args) {
//获取连接
Jedis jedis = RedisUtils.getJedis();
String value = jedis.get("name");
System.out.println(value);
//向连接池返回连接
RedisUtils.returnResource(jedis);
}
}
pipeline 管道: 针对批量操作数据或者批量初始化数据的时候使用,效率高,Redis 的 pipeline 功能在命令行中没有实现,在Java客户端(jedis)中是可以使用的,它的原理是这样的:
不使用管道的时候,我们每执行一条命令都需要和 redis 服务器交互一次,使用管道之后,可以实现一次提交一批命令,这一批命令只需要和 redis 服务器交互一次,所以就提高了性能。这个功能就类似于 mysql 中的 batch 批处理。初始化10万条数据,使用普通方式一条一条添加和使用管道批量初始化进行对比分析,代码如下:
package com.amo;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
public class PipelineOp {
public static void main(String[] args) {
//1:不使用管道
Jedis jedis = RedisUtils.getJedis();
long start_time = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
jedis.set("a" + i, "a" + i);
}
long end_time = System.currentTimeMillis();
System.out.println("不使用管道,耗时:" + (end_time - start_time));
//2:使用管道
Pipeline pipelined = jedis.pipelined();
start_time = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipelined.set("b" + i, "b" + i);
}
pipelined.sync();
end_time = System.currentTimeMillis();
System.out.println("使用管道,耗时:" + (end_time - start_time));
RedisUtils.returnResource(jedis);
}
}
执行结果如下图所示:
从这可以看出来,针对海量数据的初始化,管道可以显著提高初始化性能。
第五章 Python 操作 Redis(重点)
5.1 准备工作
在开始之前,请确保已经安装好了 Redis 并能正常运行,安装方式可以参考上文。除了安装好 Redis 数据库外,还需要安装好 redis-py 库,即用来操作 Redis 的 Python 包,可以使用 pip 进行安装,安装命令如下:
pip install redis
安装好 Redis 数据库和 redis-py 库之后,便可以开始下文的学习了。
补充:解析器 PythonParser (Python实现默认)、HiredisParser (C语言实现)
帮助文档:https://pypi.org/project/redis/
5.2 连接 Redis 数据库
redis-py 库提供 Redis 和 StrictRedis 两个类,用来实现 Redis 命令对应的操作。StrictRedis 类实现了绝大部分官方的 Redis 命令,参数也一一对应,例如 set 方法就对应 Redis 命令的 set 方法。而 Redis 类是 StrictRedis 的子类,其主要功能是向后兼容旧版本库里的几个方法。为了实现兼容,Redis 类对方法做了改写,例如将 lrem 方法中 value 和 num 参数的位置互换,这和 Redis 命令行的命令参数是不一致的。官方推荐使用 StrictRedis 类,所以本文后续也使用 StrictRedis 类的相关方法作为演示。
连接 Redis 数据库示例代码如下:
# -*- coding: utf-8 -*-
# @Time : 2023-03-18 17:16
# @Author : AmoXiang
# @File : 1.建立连接.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
import redis
def get_connection():
""" 直接连接到redis """
# connection = redis.Redis(host='localhost', port=6379, db=1)
connection = redis.StrictRedis(host='localhost', port=6379, db=1)
return connection
def get_connection_by_pool():
""" 使用连接池 """
pool = redis.ConnectionPool(host='localhost', port=6379, db=1, max_connections=20)
# connection = redis.Redis(connection_pool=pool)
connection = redis.StrictRedis(connection_pool=pool)
return connection
def get_connection_by_url():
# redis://[:password]@host:port/db
url = "redis://localhost:6379/0"
pool = redis.ConnectionPool.from_url(url)
connection = redis.StrictRedis(connection_pool=pool)
return connection
def close_connection(conn):
""" 关闭连接 """
conn.close()
if __name__ == '__main__':
# connection = get_connection()
# connection.set('key', 'value')
# close_connection(connection)
# connection2 = get_connection_by_pool()
# result = connection2.set('key2', 'value2')
# print(result)
# connection2.close()
# close_connection(connection2)
connection3 = get_connection_by_url()
result = connection3.set('key2', 'value2')
print(result)
# connection2.close()
close_connection(connection3)
5.3 字符串操作 (String)
参考代码如下:
# -*- coding: utf-8 -*-
# @Time : 2023-03-18 17:36
# @Author : AmoXiang
# @File : 2.字符串操作.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
import json
import redis
class BaseRedisConnection(object):
def __init__(self):
pool = redis.ConnectionPool(host='localhost', port=6379, db=1, max_connections=20,
# 自动进行结果的转换
decode_responses=True)
self.conn = redis.StrictRedis(connection_pool=pool)
def __del__(self):
""" 关闭连接 """
try:
self.conn.close()
except Exception as e:
print(e)
class TestString(BaseRedisConnection):
"""
set : 设置值
get : 获取值
mset : 设置多个键值对
mget : 获取多个键值对
append : 添加字符串
incr/decr: 增加/减少 1
"""
def test_set(self):
"""set 设置值"""
user1 = self.conn.set('user1', 'Amy')
print(user1)
user2 = self.conn.set("user2", '李四')
print(user2)
# 10秒后自动删除
user3 = self.conn.set('user3', 'user3', ex=10)
print(user3)
def test_get(self):
""" get 获取值 """
user1 = self.conn.get('user1')
print('user1: ', user1)
# 如果没有设置参数: decode_responses=True 则需要手动的进行结果转换
# user2 = self.conn.get('user2').decode('utf-8')
user2 = self.conn.get('user2')
print('user2: ', user2)
user3 = self.conn.get('user3')
print('user3: ', user3) # key在redis中不存在则返回None
user4 = self.conn.get('user4')
print('user4: ', user4) # key在redis中不存在则返回None
def test_mset(self):
""" mset 设置多个键值对 """
d = {
'user5': 'Bob',
'user6': 'Bobx'
}
result = self.conn.mset(d)
print(result)
def test_mget(self):
""" mget 获取多个键值对 """
result = self.conn.mget(['user1', 'user5', 'user7'])
print(result)
def test_incr(self):
""" incr/decr 增加/减少1 """
result = self.conn.incr('score')
print(result)
score = self.conn.get('score')
print('score:', score)
def test_del(self):
result = self.conn.delete('user1')
print(result)
def register(self, username, password, nickname):
"""
模拟用户注册
:param username: 用户名
:param password: 密码
:param nickname: 昵称
:return:
"""
key = f'user:{username}'
user_info = {
'username': username,
'password': password,
'nickname': nickname,
}
value = json.dumps(user_info)
result = self.conn.set(key, value)
print('result', result)
def login(self, username, password):
"""
模拟用户登录
:param username: 用户名
:param password: 密码
:return:
"""
key = f'user:{username}'
value = self.conn.get(key)
print(value)
if value is None:
print('用户不存在')
return None
user_info = json.loads(value)
print(user_info['username'])
print(user_info['password'])
print(user_info['nickname'])
if user_info['password'] != password:
print('密码输入不正确')
return None
print('登录成功')
def main():
str_obj = TestString()
# str_obj.test_set()
# str_obj.test_get()
# str_obj.test_mset()
# str_obj.test_mget()
# str_obj.test_incr()
str_obj.test_del()
# str_obj.register('lili', '123456', '丽丽')
# str_obj.login('lili', '12')
# str_obj.login('lili', '123456')
# str_obj.login('lili2', '123456')
if __name__ == '__main__':
main()
5.4 Python 操作列表(List)
参考代码如下:
# -*- coding: utf-8 -*-
# @Time : 2023-03-18 18:24
# @Author : AmoXiang
# @File : 3.列表操作.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
import redis
class BaseRedisConnection(object):
def __init__(self):
pool = redis.ConnectionPool(host='localhost', port=6379, db=1, max_connections=20,
# 自动进行结果的转换
decode_responses=True)
self.conn = redis.StrictRedis(connection_pool=pool)
def __del__(self):
""" 关闭连接 """
try:
self.conn.close()
except Exception as e:
print(e)
class TestList(BaseRedisConnection):
"""
lpush/rpush : 从左/右插入数据
lrange : 获取指定长度的数据
ltrim : 截取一定长度的数据
lpop/rpop :移除最左/右的成员并返回
lpushx/rpushx : key存在的时候才插入数据,不存在时不做任何处理
"""
def test_push(self):
""" lpush/rpush -- 从左/右插入数据 """
# t = ['Amy3', 'Bob5']
# result = self.conn.rpush('user_list', *t)
result = self.conn.lpush('user_list', 'Amy', 'Bob')
print(result)
user_list = self.conn.lrange('user_list', 0, -1)
print(user_list)
def test_pop(self):
""" lpop/rpop --移除最左/右的元素并返回 """
result = self.conn.lpop('user_list')
print(result)
user_list = self.conn.lrange('user_list', 0, -1)
print(user_list)
def test_llen(self):
""" 列表长度 """
result = self.conn.llen('user_list')
print(result)
def main():
list_obj = TestList()
# list_obj.test_push()
# list_obj.test_pop()
list_obj.test_llen()
if __name__ == '__main__':
main()
5.5 Python 操作散列(Hash)
参考代码如下:
# -*- coding: utf-8 -*-
# @Time : 2023-03-18 19:34
# @Author : AmoXiang
# @File : 4.散列操作.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
import redis
class BaseRedisConnection(object):
def __init__(self):
pool = redis.ConnectionPool(host='localhost', port=6379, db=1, max_connections=20,
# 自动进行结果的转换
decode_responses=True)
self.conn = redis.StrictRedis(connection_pool=pool)
def __del__(self):
""" 关闭连接 """
try:
self.conn.close()
except Exception as e:
print(e)
class TestHash(BaseRedisConnection):
"""
hset/hget :设置/获取散列值
hmset/hmget : 设置/获取多对散列值
hsetnx : 如果散列已经存在,则不设置
hkeys/hvals : 返回所有Keys/Values
hlen : 返回散列包含域(field)的数量
hdel : 删除散列指定的域(field)
hexists : 判断是否存在
"""
def test_hset(self):
""" hset/hget --设置/获取散列值 """
result = self.conn.hset('stu:0001', 'name', '李丽')
print(result)
# 插入之前判断是否已经存在
exists = self.conn.hexists('stu:0001', 'name')
print('exists:', exists)
# 如果已经存在,则不插入
result = self.conn.hsetnx('stu:0002', 'name', '王丽')
print('res:', result)
def test_hmset(self):
""" hmset/hmget -- 设置/获取多对散列值 """
m = {
'name': 'Bob',
'age': 21,
'grade': 98
}
# Redis.hmset() is deprecated. Use Redis.hset() instead.
# result = self.conn.hmset('stu:0003', m)
result = self.conn.hset('stu:0003', mapping=m)
print(result)
keys = self.conn.hkeys('stu:0003')
print('keys:', keys)
def test_hdel(self):
""" hdel : 删除散列指定的域(field) """
length = self.conn.hlen('stu:0003')
print('length:', length)
result = self.conn.hdel('stu:0003', 'age')
print('res:', result)
length = self.conn.hlen('stu:0003')
print('length2:', length)
def register(self, username, password, nickname):
"""
模拟用户注册
:param username: 用户名
:param password: 密码
:param nickname: 昵称
:return:
"""
user_info = {
'username': username,
'password': password,
'nickname': nickname
}
key = f'user:{username}'
result = self.conn.hset(key, mapping=user_info)
print(result)
def get_user_info(self, username):
""" 获取用户信息 """
key = f'user:{username}'
user = self.conn.hmget(key, 'username', 'password', 'nickname')
# user = self.conn.hmget(key, 'username')
print(user)
print('username:', user[0])
def main():
hash_obj = TestHash()
# hash_obj.test_hset()
# hash_obj.test_hmset()
# hash_obj.test_hdel()
# hash_obj.register('wangwu', '123456', '王五')
hash_obj.get_user_info('wangwu')
if __name__ == '__main__':
main()
5.6 Python 操作集合(Set)
参考代码如下:
# -*- coding: utf-8 -*-
# @Time : 2023-03-18 19:48
# @Author : AmoXiang
# @File : 5.集合操作.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
import redis
class BaseRedisConnection(object):
def __init__(self):
pool = redis.ConnectionPool(host='localhost', port=6379, db=1, max_connections=20,
# 自动进行结果的转换
decode_responses=True)
self.conn = redis.StrictRedis(connection_pool=pool)
def __del__(self):
""" 关闭连接 """
try:
self.conn.close()
except Exception as e:
print(e)
class TestSet(BaseRedisConnection):
"""
sadd/srem : 添加/删除成员
sismember :判断是否为set的一个成员
smembers :返回该集合的所有成员
sdiff : 返回一个集合与其它集合的差异
sinter : 返回几个集合的交集
sunion : 返回几个集合的并集
"""
def test_sadd(self):
""" sadd -- 添加成员 """
result = self.conn.sadd('zoo1', 'Dog', 'Cat')
print(result)
animals = ['Monkey', 'Panda', 'Dog']
result = self.conn.sadd('zoo1', *animals)
print(result)
members = self.conn.smembers('zoo1')
print(members)
def test_srem(self):
""" srem -- 删除成员 """
result = self.conn.srem('zoo1', 'Dog')
print(result)
members = self.conn.smembers('zoo1')
print(members)
def course_analysis(self):
""" 课程分析 """
# 1. 自然科学
science_stu_list = ['Stu003', 'Stu022', 'Stu021', 'Stu012', 'Stu014', ]
self.conn.sadd('science', *science_stu_list)
# 2. 大学英语
english_stu_list = ['Stu001', 'Stu021', 'Stu011', 'Stu012', 'Stu004', ]
self.conn.sadd('english', *english_stu_list)
result = self.conn.sinter('science', 'english')
print(result)
def main():
set_obj = TestSet()
set_obj.test_sadd()
# set_obj.test_srem()
# set_obj.course_analysis()
if __name__ == '__main__':
main()
5.7 Python 操作有序集合(zSet)
参考代码如下:
# -*- coding: utf-8 -*-
# @Time : 2023-03-18 19:50
# @Author : AmoXiang
# @File : 6.有序集合操作.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
import redis
class BaseRedisConnection(object):
def __init__(self):
pool = redis.ConnectionPool(host='localhost', port=6379, db=1, max_connections=20,
# 自动进行结果的转换
decode_responses=True)
self.conn = redis.StrictRedis(connection_pool=pool)
def __del__(self):
""" 关闭连接 """
try:
self.conn.close()
except Exception as e:
print(e)
class TestzSet(BaseRedisConnection):
"""
zadd/zrem : 添加(修改)/删除成员
zcard/zcount :统计成员的数量
zscore :某个成员的分数(score)
zrange/zrevrange :按升序/降序查看成员
zrank/zrevrank :按升序/降序查看成员排名
"""
def test_zadd(self):
""" zadd -- 添加成员 """
rank = {
'李明': 3.25,
'王浩': 3.21,
'张皓文': 4.02,
'吴文杰': 4,
'赵宇': 3.53,
'林峰': 2.5
}
result = self.conn.zadd('swimming', rank)
print(result)
count = self.conn.zcount('swimming', 0, 100)
print('count:', count)
def test_zrem(self):
""" zrem -- 删除成员 """
result = self.conn.zrem('swimming', '林峰')
print(result)
count = self.conn.zcount('swimming', 0, 100)
print('count:', count)
def rank_analysis(self):
""" 比赛结果分析 """
result = self.conn.zrange('swimming', 0, -1)
print(result)
# 前3名
result = self.conn.zrange('swimming', 0, 2)
print(result)
def main():
set_obj = TestzSet()
# set_obj.test_zadd()
# set_obj.test_zrem()
set_obj.rank_analysis()
if __name__ == '__main__':
main()
至此今天的学习就到此结束了,笔者在这里声明,笔者写文章只是为了学习交流,以及让更多学习数据库的读者少走一些弯路,节省时间,并不用做其他用途,如有侵权,联系博主删除即可。感谢您阅读本篇博文,希望本文能成为您编程路上的领航者。祝您阅读愉快!
好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
如果我的博客对你有帮助、如果你喜欢我的博客内容,请点赞
、评论
、收藏
一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
编码不易,大家的支持就是我坚持下去的动力。点赞后不要忘了关注
我哦!