⭐️写在前面
这里是温文艾尔の学习之路
🍅文章整理自:黑马程序员-Redis
🙏作者水平欠佳,如果发现任何错误,欢迎批评指正
👍如果对你有帮助,给博主一个免费的点赞以示鼓励把QAQ
👋博客主页🎉 温文艾尔の学习小屋
⭐️更多文章👨🎓请关注温文艾尔主页
🍅文章发布日期:2021.12.16
👋Redis学习之路!
🔎更多文章:
文章目录
- 👋1 NoSql简介
- 👋2 Redis简介
- 👋2.1 Redis的应用
- 🎉2.2 Redis的下载与安装
- 🎉2.2.1 Linux环境下安装Redis
- 2.2.1.1 切换启动方式
- 🌹2.2.2 windows环境下安装Redis
- 👋2.2.1 redis核心文件
- 👋3 Redis的五大数据类型
- 👋3.1 String
- 👋3.1.1 String 类型数据的基本操作
- 👋3.1.2 String类型数据的扩展操作
- 业务场景:
- 解决方案
- 👋string作为数值操作
- 业务场景:
- 解决方案:
- 👋3.1.3 string类型数据操作的注意事项
- 👋3.1.4 综合案例
- 解决方案:
- 👋3.2 hash
- 👋3.2.1 hash类型简介
- 👋3.2.2 hash类型数据的基本操作
- 👋3.2.3 hash类型数据扩展操作
- 👋3.2.4 hash类型数据操作的注意事项
- 👋3.3 list
- 👋3.3.1 list类型数据基本操作
- 👋3.3.2 list类型数据扩展操作
- 业务场景1
- 👋3.3.3 list类型数据操作注意事项
- 👋3.4 set
- 👋3.4.1 set类型数据的基本操作
- 👋3.4.2 set类型数据的扩展操作
- 业务场景1
- 业务分析
- 解决方案
- 业务场景2
- 解决方案
- 👋3.4.3 set类型数据操作的注意事项
- 业务场景1
- 解决方案
- 👋3.5 sorted_set
- 👋3.5.1 sorted_set类型数据的基本操作
- 注意
- 其他操作
- 👋3.5.2 sorted_set 业务场景
- 业务场景1
- 业务分析
- 解决方案
- 👋3.5.3 sorted_set类型数据操作的注意事项
- 👋4 Redis通用命令
- 👋4.1 key通用指令
- 👋4.2 数据库通用指令
- 解决方案
- 👋4.2.1 数据库通用操作
- 👋5 Jedis
- 👋5.1 Jedis简介
- 👋5.2 Jedis书写Hello World
- 👋5.3 Jedis常规操作演示
- 案例
- 案例:需求分析
- 代码实现
- 6.4 Jedis简易工具类
👋1 NoSql简介
即Not-Only SQL(泛指非关系型的数据库),作为关系型数据库的补充
作用
- 应对基于海量用户和海量数据前提下的
数据处理
问题
特征
- 可扩容,可伸缩
- 大数据量下高性能
- 灵活的数据模型
- 高可用
常见的Nosql数据库
Redis
- memcache
- HBase
MongoDB
👋2 Redis简介
Redis是Nosql的一种具体体现
概念:
- Redis(REmote Dlctionary Server)是用
C语言开发
的一个开源的高性能键值对(key-value)数据库
特征
- 数据间没有必然的关联关系
- 内部采用
单线程
机制进行工作 - 高性能。官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s
- 多数据类型支持
- 字符串类型 string
- 列表类型 list
- 散列集合 hash
- 集合类型 set
- 有序集合类型 sorted_set
- 持久化支持。可以进行数据
灾难恢复
👋2.1 Redis的应用
🎉2.2 Redis的下载与安装
🎉2.2.1 Linux环境下安装Redis
有外网的情况下可执行如下命令
wget http://download.redis.io/releases/redis-4.0.0.tar.gz
- 执行对压缩包
解压
的命令
tar -xvf redis-4.0.0.tar.gz
- 进入目录
cd redis-4.0.0/
- 安装 redis
make install
显示下面这张图则代表安装成功
- 进入src目录启动redis
cd src
redis-server
- 复制一个会话,执行redis-cli即可操作redis
- 切换端口启动redis服务,执行以下命令
redis-server --port <端口号>
- 同样启动redis-cli也需要切换端口,执行命令 redis-cli -p <端口号>
2.2.1.1 切换启动方式
我们在开发可以按照不同方式启动redis,切换启动方式
查看redis的conf文件
使用cat命令过滤注释与空白并把文件内容转移
到我们自己的redis-6379.conf中对其进行修改
cat redis.conf | grep -v "#" |grep -v "^$" > redis-6379.conf
进入redis-6379.conf修改文件
vim redis 6379.conf
修改为以下形式
下面我们使用配置文件启动
查看是否启动
> 将redis-6379.conf移动到我们自己创建的conf文件里
>
> 将redis-6379.conf文件复制
修改端口及日志文件名称即可
启动两个服务
🌹启动成功
🌹2.2.2 windows环境下安装Redis
在学习的初级阶段我们先在windows上安装redis,后续再在linux上进行安装
安装链接:
redis安装
👋2.2.1 redis核心文件
- redis-server.exe 服务器启动命令
- redis-cli.exe 命令行客户端
- redis.windows.conf redis核心配置文件
- redis-benchmark.exe 性能测试工具
- redis-check-aof.exe AOF文件修复工具
- redis-check-dump.exe RDB文件检查工具(快照持久化文件)
启动redis:redis-server
启动命令行:redis-cli
👋3 Redis的五大数据类型
redis数据存储格式
- redis自身是一个
Map
,其中所有的数据都是采用key:value的
形式存储 - 数据类型指的是存储的数据的类型,也就是value部分的类型,
key部分永远都是字符串
我们可以和java中的对象类比着看待
👋3.1 String
👋3.1.1 String 类型数据的基本操作
- 添加/修改数据
- set key value
- 获取数据
- get key
- 删除数据
- del key
- 添加/修改多个数据
- mset key1 value1 key2 value2 …
- 获取多个数据
- mget key1 key2…
- 获取数据字符个数
- strlen key
- 追加信息到原始信息后部(如果原始信息存在就追加,否则新建)
- append key value
单数据操作与多数据操作的选择之惑
首先我们需要明白一条指令的执行过程
- set指令发送需要时间
- redis执行需要时间
- 处理完毕返回需要消耗时间
单指令执行3条指令消耗的时间:3x发送+3x处理+3x返回
多指令执行3条指令的执行过程:1x发送+3x处理+1x返回
在数据量较少时,单指令与多指令并没有多少差别
但是一旦数据量上涨,多指令消耗的时间远远少于单指令
👋3.1.2 String类型数据的扩展操作
业务场景:
大型企业级应用中,分表操作是基本操作,使用多张表存储同类型数据,但是对应id必须保证统一性,不能重复。Oracle数据库具有sequence设定,可以解决该问题,但是MYSQL数据库并不具有类似的机制那么如何解决?
解决方案
- 设置
数值数据
增加指定范围
的值- incr key
- incrby key increment
- incrbyfloat key increment
- 设置
数值数据
减少指定范围
的值- decr key
- decrby key increment
👋string作为数值操作
- string在redis内部存储默认就是一个字符串,当遇到增减类操作incr,decr时会
转成数值型
进行计算。 - redis所有操作都是
**原子性**
的,采用单线程处理所有业务,命令是一个一个执行的,因此无需考虑并发带来的数据影响 - 注意:胺树脂进行操作的数据,如果原始数据不能转成数值,或超越了redis数值上限范围,将报错。
9223372036854775807
(java中long型数据最大值,long.MAX_VALUE)
业务场景:
我们为投票的人设置news_id,并为他设置投票倒数时长,时长一到,id擦掉
解决方案:
- 设置数据具有指定的生命周期
- setex key seconds value
- psetex key milliseconds value
- 如果在时间之内又重新设置数据的生命周期,则之前的设置作废,按照新的生命周期来
我们得到结论 redis控制数据的生命周期,通过数据是否失效控制业务行为,适用于所有具有时效性限定控制的操作
👋3.1.3 string类型数据操作的注意事项
- 数据操作不成功的反馈与数据正常操作之间的差异
- 表示运行结果是否成功
- (integer)0->false 失败
- (integer)1->true 成功
- 表示运行结果值
- (integer)3->3 3个
- (integer)1->1 1个
- 数据未获取到
- (nil)等同于null
- 数据最大存储量
- 512MB
- 数值计算最大范围(java中的long的最大值)
- 9223372036854775807
👋3.1.4 综合案例
主页高频访问信息显示控制,例如新浪微博大V主页显示粉丝数与微博数量
解决方案:
- 在redis中为大V用户设定用户信息,以用户主键和属性值作为key,后台设定定时刷新策略即可
表名+主键名+主键值+属性名来作为它的key
- 在redis中以json格式存储大V用户信息,定时刷新(也可以使用hash类型)
- redis应用于各种结构型和非结构型高热度数据访问加速
👋3.2 hash
👋3.2.1 hash类型简介
- 新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
- 需要的存储结构:一个存储空间保存多个键值对数据
- hash类型:底层使用哈希表结构实现数据存储
👋3.2.2 hash类型数据的基本操作
- 添加/修改数据
- hset key field value
- 获取数据
- hget key field
- hgetall key
- 删除数据
- hdel key field1 [field2]
- hdel key field1 [field2]
- 添加/修改多个数据
- hmset key field1 value1 field2 value2
- 获取多个数据
- hmget key field1 field2
- 获取哈希表中字段的数量
- hlen key
- 获取哈希表中是否存在指定的字段
- hexists key field
👋3.2.3 hash类型数据扩展操作
- 获取哈希表中所有字段名或字段值
- hkeys key
- hvals key
- 设置指定字段的数值数据增加指定范围的值
- hincrby key field increment
- hincrbyfloat key field increment
👋3.2.4 hash类型数据操作的注意事项
- 1.hash类型下的value只能存储字符串,不允许存储其他数据类型,不存在嵌套现象。如果数据未获取到,对应的
值为(nil)
- 2.每个hash可以存储2^23-1个键值对
- 3.hash类型十分贴近对象的数据存储形式,并且可以灵活添加对象属性。但hash设计初衷不是为了存储大量对象
而设计的,切记不可滥用,更不可以将hash作为对象列表使用
- 4.hgetall操作可以获取全部属性,如果内部field过多,遍历整体数据效率就会很低,有可能成为数据访问瓶颈
hsetnx key field value
如果当前value存在则不会修改,当前value不存在则进行修改
👋3.3 list
类比java中的linkedlist
list类型
👋3.3.1 list类型数据基本操作
- 添加/修改数据
- lpush key value1 [value2]… (从左边进)
- rpush key value1 [value2]…(从右边进)
- 获取数据
- lrange key start stop(不知道多少个数据时stop设置为-1,负数代表倒数第一个元素)
- lindex key index
- llen key
- 获取并移除数据
- lpop key
- rpop key
- 移除指定数据
- lrem key count value
👋3.3.2 list类型数据扩展操作
在规定时间内获取并移除数据(现在列表里没有不代表将来没有)
- blpop key1 [key2] timeout
- brpop key1[key2] timeout
业务场景1
微信朋友圈点赞,要求按照点赞顺序显示点赞好友信息
其中count代表删除几个列表中的指定元素,例如删除3个a
由此可以看出
- redis应用于具有操作
先后顺序的数据控制
👋3.3.3 list类型数据操作注意事项
- list中保存的数据都是string类型的,数据总容量是有限的,最多
2^32-1
个元素 - list具有
索引
的概念,但是操作数据时通常以队列的形式
进行入队出队操作,或以栈的形式
进行入栈出栈操作 - 获取全部数据操作结束索引设置为-1
- list可以对数据进行
分页
操作,通常第1页的信息来自于list,第2页及更多的信息通过数据库的形式
加载
业务场景1
解决方案
每台服务器上的信息都由redis进行统计,保证了信息时间按顺序进行展示
由此我们可见redis应用于最新消息的展示
👋3.4 set
类比java中的set
- 新的存储要求:存储大量的数据,在查询方面提供更高的效率
- 需要的存储结构:能够保存大量的数据,高效的内部存储机制,便于查询
- set类型:与hash存储结构完全相同,仅存储键,不存储值(nil),并且
值是不允许重复的
为什么不用list的原因: list内部的数据结构是链表,查询慢,增删快,虽然能存取大量数据,但是读取效率低下
set在存储空间中的分布
👋3.4.1 set类型数据的基本操作
- 添加数据
- sadd key member1 [member2]
- 获取全部数据
- smembers key
- 删除数据
- srem key member1 [member2]
- 获取集合数据总量
- scard key
- 判断集合中是否包含指定数据
- sismember key member
- sismember key member
👋3.4.2 set类型数据的扩展操作
业务场景1
每位用户首次使用今日头条时会设置3项爱好的内容,但是后期为了增加用户的活跃度,兴趣点。必须让用户对其他信息类别产生兴趣,增加客户留存度,如何实现?
业务分析
- 系统分析出各个分类的最新或最热点信息条目并组织成set集合
- 随机挑选其中部分信息
- 配合用户关注信息分类中的热点信息组织成展示的全信息集合
解决方案
- 随机获取集合中指定数量的数据
- srandmember key [count]
- 随机获取集合中的某个数据并将该数据移出集合
- spop key
由此可以得出
- redis应用于随机推荐类信息检索,例如热点歌单推荐,热点新闻推荐,热卖旅游线路,应用app,大v推荐等
业务场景2
推荐共同关注的好友,提高用户留存率
解决方案
交、并、差集图示
求两个集合的交、并、差集
- sinter key1 [key2]
- sunion key1 [key2]
- sdiff key1 [key2]
求两个集合的交、并、差集并存储到指定集合中
- sinterstore destination key1 [key2]
- sunionstore destination key1 [key2]
- sdiffstore destination key1 [key2]
将指定数据从原始集合中移动到目标集合中
- smove source destination member
由此我们可以得到
- redis应用于同类信息的关联搜索、二度关联搜索、深度关联搜索
👋3.4.3 set类型数据操作的注意事项
- set类型
不允许数据重复
,如果添加的数据在set中已经存在,将只保留一份
- set虽然和hash的
存储结构相同
,但是无法启用hash中存储值的空间
业务场景1
集团公司共具有12000名员工,内部OA系统中具有700多个角色,3000多个业务操作,23000多种数据,每位员工具有一个或多个角色,如何快速进行业务操作的权限检验?
这里不考虑其他的,只是用redis作为校验,证明它可以做到这件事,真实开发会有其他框架完成
解决方案
校验思想1:
我们设定一个用户有两个角色,每个角色有2-3个不同权限,将用户权限通过sunionstore存储到 uid:007中,判断权限可以直接判断uid:007中是否存在,存在则有权限,不存在则无权限
校验思想2:
通过sismember 命令判断是否具有该权限
第一种思想是数据存储方提供数据,然后将校验的业务操作放到程序的业务层去进行
第二种思想是直接把要校验的数据放在数据这一端校验,直接把结果给你
第一种形式耦合度较低,分工明确,数据和业务分散开来,而第二种把业务校验的工作糅合到数据中去了
,对于redis,还是提供基础数据而非提供校验结果比较好
由此我们可以得出
redis应用于同类型不重复数据的合并操作
👋3.5 sorted_set
- 新的存储需求:数据排序
有利于数据的有效展示
,需要提供一种可以根据自身特征进行排序的方式 - 需要的存储结构:新的存储模型,可以保存可排序的数据
- sorted_set类型:在set的存储结构基础上添加可排序字段
score便是数据的排序特征
,我们根据他对数据进行排序
👋3.5.1 sorted_set类型数据的基本操作
- 添加数据
- zadd key score1 member1 [score2 member2]
- 获取全部数据
- zrange key start stop [WITHSCORES] (按从小到大的顺序展示)
- zrevrange key start stop [WITHSCORES] (按从大到小的顺序展示)
- 删除数据
- zrem key member [member …]
对上面操作的演示
- 按条件获取数据
- zrangebyscore key min max [WITHSCORES] [LIMIT]
- zrevrangebyscore key max min [WITHSCORES]
- 按条件删除数据
- zremrangebyrank key start stop (按索引删除)
- zremrangebyscore key min max (按范围删除)
查询成绩在50-90之间的数据
查询成绩在0-99之间的前两个数据
按范围删除
按索引删除
注意
min
与max
用于限定搜索查询的条件start
与stop
用于限定查询范围,作用于索引,表示开始和结束索引offset
与count
用于限定查询范围,作用于查询结果,表示开始位置和数据总量
其他操作
- 获取集合数据总量
- zcard key
- zcount key min max
- 集合交、并操作
- zinterstore destination numkeys key [key …]
- zunionstore destination numkeys key [key …]
操作演示:
sorted_set集合交集操作演示
👋3.5.2 sorted_set 业务场景
业务场景1
票选广东十大杰出青年,各类综艺选秀海选投票
各类资源网站TOP10(电影,歌曲,文档,电商,游戏等)
聊天室活跃度统计
业务分析
- 为所有参与排名的资源建立排序依据
解决方案
- 获取数据对应的索引(排名)
- zrank key member
- zrevrank key member
- score值获取与修改
- zscore key member
- zincrby key increment member
我们设置电影aa,bb,cc并对其票数进行排名,并对aa票数进行+1操作
👋3.5.3 sorted_set类型数据操作的注意事项
- score保存的数据存储空间是
64位
,如果是整数范围是-9007199254740992
~9007199254740992
- score保存的数据也可以是一个
双精度的double值
,基于双精度浮点数的特征,可能会丢失精度,使用时候要慎重 - sorted_set底层存储还是
基于set结构的
,因此数据不能重复,如果重复添加相同的数据,score值将被反复覆盖,保留最后一次修改的结果
由此可知
- redis应用于基于时间顺序的数据操作,而不关注具体时间
👋4 Redis通用命令
👋4.1 key通用指令
key是一个字符串,通过key获取redis中保存的数据
-
删除指定key
- del key
-
获取key是否存在
- exists key
-
获取key的类型
- type key
-
为指定key设置有效期
-
expire key seconds
-
pexpire key milliseconds (和第1个区别在于可以设置毫秒级)
-
expireat key timestamp
-
pexpireat key milliseconds-timestamp
-
-
获取key的有效时间
- ttl key (如果一个key不存在,它的返回值为-2,如果key存在但是过了有效期,它的返回值为-1)
- pttl key
-
切换key从时效性转换为永久性
- persist key
-
查询key
- keys pattern
- 查询模式pattern规则
-
为key改名
- rename key newkey
- renamenx key newkey (如果新名称已存在则不修改)
-
对所有key排序
- sort (排list、set、sorted_set中的元素)
-
其他key通用操作
- help @generic
👋4.2 数据库通用指令
key的重复问题
- key是由程序员定义的
- redis在使用过程中,伴随着操作数据量的增加,会出现大量的数据以及对应的key
- 数据不区分种类,类别混杂在一起,极易出现重复或冲突
解决方案
- redis为每个服务提供有16个数据库,编号从0到15
- 每个数据库之间的数据相互独立
👋4.2.1 数据库通用操作
- 切换数据库(不切换则默认使用0号数据库)
- select index
- 其他操作
- quit (退出当前数据库)
- ping (测试服务器是否连通)
- echo message (输出信息)
- 数据移动 (将当前数据库的指定数据移动到目标数据库)
- move key db
- 数据清除
- dbsize (查看当前数据库有多少个key)
- flushdb (清除当前数据)
- flushall (清除所有数据)
👋5 Jedis
👋5.1 Jedis简介
Jedis是java语言与redis进行交互的工具,但是Jedis不是java语言连接redis的唯一渠道,其他渠道还有
- SpringData Redis
- Lettuce
👋5.2 Jedis书写Hello World
第一步:创建Maven工程
第二步:引入依赖
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
第三步:输出helloworld
@Test
public void testJedis(){
//连接redis
Jedis jedis = new Jedis("127.0.0.1", 6379);
//操作redis
jedis.set("name","helloworld");
String name = jedis.get("name");
System.out.println(name);
//关闭连接
jedis.close();
}
👋5.3 Jedis常规操作演示
1.测试list
@Test
public void testList(){
//连接redis
Jedis jedis = new Jedis("127.0.0.1", 6379);
//操作redis
jedis.lpush("list1","a","b","c");
jedis.rpush("list1","x");
List<String> list1 = jedis.lrange("list1", 0, -1);
for (String s : list1) {
System.out.println(s);
}
//关闭连接
jedis.close();
}
2.测试hash
@Test
public void testHash(){
//连接redis
Jedis jedis = new Jedis("127.0.0.1", 6379);
//操作redis
jedis.hset("hash1","a1","a1");
jedis.hset("hash1","a2","a2");
Map<String, String> hash1 =
jedis.hgetAll("hash1");
System.out.println(hash1);
//关闭连接
jedis.close();
}
案例
案例:需求分析
代码实现
package com.wql;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisDataException;
/**
* Description
* User:
* Date:
* Time:
*/
public class service {
private String id;
private int num;
public service(String id,int num){
this.id = id;
this.num = num;
}
//控制单元(控制business的调用)
public void ss(){
//连接redis
Jedis jedis = new Jedis("127.0.0.1", 6379);
//业务操作
String value = jedis.get("compid:"+id);
try {
if (value==null){
jedis.setex("compid:"+id,5,Long.MAX_VALUE-num+"");
}else {
Long val = jedis.incr("compid:"+id);
business(id,num-(Long.MAX_VALUE-val));
}
}catch (JedisDataException e){
System.out.println("使用已经达到次数上限,请升级会员级别");
return;
}finally {
//关闭jedis
jedis.close();
}
}
//业务操作
public void business(String id,Long val){
System.out.println("用户:"+id+"业务操作执行第"+val+"次");
}
}
class MyThread extends Thread{
service sc;
public MyThread(String id,int num){
sc = new service(id,num);
}
@Override
public void run() {
while (true){
sc.ss();
try {
Thread.sleep(300L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Main{
public static void main(String[] args) {
MyThread mt1 = new MyThread("1",10);
MyThread mt2 = new MyThread("2",30);
mt1.start();
mt2.start();
}
}
6.4 Jedis简易工具类
Jdeis本身已经提供了连接池JedisPool
- JedisPool:Jedis提供的连接池技术
- poolConfig:连接池配置对象
- host:redis服务地址
- port:redis服务端口号
package com.wql.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ResourceBundle;
/**
* Description
* User:
* Date:
* Time:
*/
public class JedisUtils {
private static JedisPool jp = null;
private static String host;
private static int port;
private static int maxTotal;
private static int maxIdle;
static {
ResourceBundle rb = ResourceBundle.getBundle("redis");
host = rb.getString("redis.host");
port = Integer.parseInt(rb.getString("redis.port"));
maxTotal = Integer.parseInt(rb.getString("redis.maxTotal"));
maxIdle = Integer.parseInt(rb.getString("redis.maxIdle"));
JedisPoolConfig jpc = new JedisPoolConfig();
jpc.setMaxTotal(maxTotal);
jpc.setMaxIdle(maxIdle);
jp = new JedisPool(jpc,host,port);
}
public static Jedis getJedis(){
return jp.getResource();
}
public static void main(String[] args) {
System.out.println(getJedis());
}
}
这里是温文艾尔の学习之路
🍅文章整理自:黑马程序员-Redis
🙏作者水平欠佳,如果发现任何错误,欢迎批评指正
👍如果对你有帮助,给博主一个免费的点赞以示鼓励把QAQ
👋博客主页🎉 温文艾尔の学习小屋
⭐️更多文章👨🎓请关注温文艾尔主页
🍅文章发布日期:2021.12.16
👋Redis学习之路!
🔎更多文章: