1.Redis概述
1.1Redis介绍
Redis的英文全称为remote dictionary server,翻译过来就是远程字典服务,
1. 字典这个概念在python中很重要是一个键值对类型的数据结构
redis是一个数据库怎么能和键值对牵扯到关系呢?原因是因为redis内置了很多丰富的数据结构,其中大部分核心都是键值对。
2. 那么redis中的remote是什么意思?
remote是遥远的,我们知道mysql是一个客户端/服务器模型的数据库,也就是说我们需要通过客户端的命令来向服务器发出请求获取到数据。而redis也是这样的,它也是一种客户端/服务器架构的设计,我们也分别需要redis的客户端和redis的服务器,remote 本质上指的就是 客户端通过网络连接到 Redis 服务端。
3. Redis 与 MySQL 都是典型的 客户端/服务器架构,但在性能上差距明显:
mysql的运行速度是一只蜗牛那么redis的运行速度就是近地卫星
redis的官网中的介绍:
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。读取延迟在 微秒级
mysql的官网中的介绍:
mysql读取数据的速度是1 千 ~ 1 万 左右。读写延迟在 毫秒级
二者差距大概在100倍左右,其实说是蜗牛和卫星有点夸张,比较准确的是人骑自行车的速度和搭载飞机的速度
4.其中为什么,redis的性能要明显强于mysql呢?
其中的原因是:
redis是"掌管"内存的,而mysql是"掌管"磁盘的
在计算机底层逻辑中,内存的读写速度是要远大于磁盘的,因为磁盘的IO效率低,虽然mysql是有缓存的存在的且MySQL 支持多线程,但是终归还是需要和磁盘打交道不可避免地影响了速度,redis虽然是单线程的模型,但是它基于内存这个特性完美的帮它弥补了单线程所带来的性能低下的缺点。
5.那么redis的数据放在内存中,如果发生意外甚至重启电脑数据不就没了吗?
但是 —— Redis 早就考虑过这个问题,它提供了 持久化机制,把数据写到磁盘里,避免意外全丢。
具体就是以下两种方式
RDB 快照 = 定时拍一张照片存档。
AOF 日志 = 写流水账,随时能回溯。
1.2 Redis特点
开源的,使用C编写,基于内存且支持持久化
高性能的Key-Value的NoSQL数据库
支持数据类型丰富,字符串string,列表list,散列hash,集合set,有序集合sorted set
支持多种编程语言(C C++ Python Java PHP ... )
Redis数据库的诞生是为了解决磁盘IO带来的性能瓶颈
1.3 应用场景
使用Redis来缓存一些经常被用到、或者需要耗费大量资源的内容,通过这些内容放到redis里面,程序可以快速读取这些内容
一个网站,如果某个页面经常会被访问到,或者创建页面时消耗的资源比较多,比如需要多次访问数据库、生成时间比较长等,我们可以使用redis将这个页面缓存起来,减轻网站负担,降低网站的延迟,比如说网站首页等
1.5 Redis附加功能
数据持久化
将内存中数据保存到磁盘中,保证数据安全,方便进行数据备份和恢复
过期键功能
为键设置一个过期时间,让它在指定时间内自动删除
主从同步
Sentinel哨兵
1.6 Redis安装
Ubuntu 上安装 Redis和Rocky Linux 上安装 Redis大致是相同的
Ubuntu / Debian 系列 → 使用
apt
sudo apt update sudo apt install redis-server -y
Rocky Linux / CentOS / RHEL 系列 → 使用
dnf
或yum
sudo dnf install redis -y 或者 sudo yum install redis -y
除此之外,操作 Redis 服务、连接客户端的命令几乎一模一样:
启动服务:
systemctl start redis
停止服务:
systemctl stop redis
重启服务:
systemctl restart redis
查看状态:
systemctl status redis
设置开机自启:
systemctl enable redis
客户端连接:
redis-cli -h 127.0.0.1 -p 6379 -a 密码
唯一要注意的小差别:
Ubuntu 默认安装时配置文件在
/etc/redis/redis.conf
Rocky Linux 默认配置文件在
/etc/redis.conf
所以如果要修改 requirepass
、bind
、supervised
等配置,路径略有不同。
1. 为什么不推荐在 Windows 操作系统 上安装 Redis?
官方不支持
Redis 作者原本是 Linux/Unix 世界的人,他明确说过 Redis 不会支持 Windows 作为生产环境。
虽然微软以前做过一个 Windows 版本的 Redis(叫 Memurai),但那只是实验性的,早就停更了。性能差距大
Redis 是 高性能内存数据库,底层大量用到 epoll/kqueue 这种 Linux 特有的高效 IO 模型。
Windows 上只能用select/poll
这些“老古董”,性能会差几个数量级,就好比让短跑运动员穿拖鞋跑步。运维生态不成熟
Linux 上 Redis 配合systemd
、shell
脚本、docker
、k8s
都是标配。
Windows 下没有这些生态,部署、监控、集群管理都很麻烦。
2. 为什么不用 图形化页面操控 Redis?
Redis 设计哲学
Redis 就是一款“快到飞起”的内存数据库,官方哲学是 简单、轻量、命令行驱动。
多一层 GUI,其实就是多一层负担。运维人员习惯命令行
大多数 DBA/后端工程师习惯在命令行里操作 Redis,写几条脚本就能自动化管理,比鼠标点 GUI 更快。
2 配置文件详解
配置文件所在路径
Rocky Linux在:/etc/redis.conf
Ubuntu在:/etc/redis/redis.conf
修改配置文件之前首先备份,执行命令:cp -p /etc/redis.conf /etc/redis.conf.bak
设置连接密码
# 1.配置文件中对应的配置 requirepass 123456 #其中123456为设置的密码 # 2.修改完成后重启Reids服务 systemctl restart redis # 3.客户端连接测试 redis-cli -h 127.0.0.1 -p 6379 -a 123456 127.0.0.1:6379>PING
设置允许远程连接
# 1.注释掉配置文件中的本地IP地址绑定 bind 127.0.0.1 # 2.关闭保护模式(把yes改为no) protected-mode no # 3.重启Redis服务 systemctl restart redis
Windows连接Rocky Linux的Redis服务
Linux查看IP地址的命令:ifconfig
# 其中:x.x.x.x 是Rocky Linux的IP地址 redis-cli -h x.x.x.x -a 123456
3 数据类型
全局命令:适用于所有数据类型
命令 | 作用 |
---|---|
SELECT number | 选择所在库(number在db0~db15之间) |
KEYS * | 查看KEY |
TYPE key | 查看KEY数据类型 |
DEL key | 删除KEY |
FULSHDB | 清空当前库 |
FLUSHALL | 清除所有库数据 |
3.1 字符串String
字符串、数值 都会转为字符串来存储
3.1.1 字符串
必须掌握
// 1. 设置一个key-value set key value // 2. 获取key的值 get key // 3. key不存在时再进行设置(nx) set key value nx # not exists // 4. 设置过期时间(ex) set key value ex seconds
了解即可
// 同时设置多个key-value mset key1 value1 key2 value2 key3 value3 // 同时获取多个key-value mget key1 key2 key3 // 获取长度 strlen key
3.1.2 数值
必须掌握
// 整数操作 INCRBY key 步长 DECRBY key 步长 INCR key : +1操作 DECR key : -1操作 // 应用场景: 抖音上有人关注你了,是不是可以用INCR呢,如果取消关注了是不是可以用DECR // 浮点数操作: 自动先转为数字类型,然后再进行相加减,不能使用append incrbyfloat key step
3.2 列表List
特性
元素是字符串类型
列表头尾增删快,中间增删慢,增删元素是常态
元素可重复
最多可包含2^32 -1个元素
列表常用命令
# 增 1、从列表头部压入元素 LPUSH key value1 value2 2、从列表尾部压入元素 RPUSH key value1 value2 3、从列表src尾部弹出1个元素,压入到列表dst的头部 RPOPLPUSH src dst 4、在列表指定元素后/前插入元素 LINSERT key after|before value newvalue # 查 5、查看列表中元素 LRANGE key start stop # 查看列表中所有元素: LRANGE key 0 -1 6、获取列表长度 LLEN key # 删 7、从列表头部弹出1个元素 LPOP key 8、从列表尾部弹出1个元素 RPOP key 9、列表头部,阻塞弹出,列表为空时阻塞 BLPOP key timeout 10、列表尾部,阻塞弹出,列表为空时阻塞 BRPOP key timeout # 关于BLPOP 和 BRPOP 1、如果弹出的列表不存在或者为空,就会阻塞 2、超时时间设置为0,就是永久阻塞,直到有数据可以弹出 3、如果多个客户端阻塞再同一个列表上,使用First In First Service原则,先到先服务 11、删除指定元素 LREM key count value count>0:表示从头部开始向表尾搜索,移除与value相等的元素,数量为count count<0:表示从尾部开始向表头搜索,移除与value相等的元素,数量为count count=0:移除表中所有与value相等的值 12、保留指定范围内的元素 LTRIM key start stop LRTIM mylist1 0 2 # 只保留前3条 # 应用场景: 保存微博评论最后500条 LTRIM weibo:comments 0 499 # 改 13、LSET key index newvalue
练习
1、查看所有的键 2、向列表 spider:urls 中以RPUSH放入如下几个元素:01_baidu.com、02_taobao.com、03_sina.com、04_jd.com、05_xxx.com 3、查看列表中所有元素 4、查看列表长度 5、将列表中01_baidu.com 改为 01_tmall.com 6、在列表中04_jd.com之后再加1个元素 02_taobao.com 7、弹出列表中的最后一个元素 8、删除列表中所有的 02_taobao.com 9、剔除列表中的其他元素,只剩前3条
3.3 Hash散列
定义
由field和关联的value组成的键值对
field和value是字符串类型
一个hash中最多包含2^32-1个键值对
优点
每创建一个键,它都会为这个键储存一些附加的管理信息(比如这个键的类型,这个键最后一次被访问的时间等)
键越多,redis数据库在储存附件管理信息方面耗费内存越多,花在管理数据库键上的CPU也会越多
缺点(不适合hash情况)
使用过期键功能:键过期功能只能对键进行过期操作,而不能对散列的字段进行过期操作
基本命令操作
# 1、设置单个字段 HSET key field value HSETNX key field value # 2、设置多个字段 HMSET key field value field value # 3、返回字段个数 HLEN key # 4、判断字段是否存在(不存在返回0) HEXISTS key field # 5、返回字段值 HGET key field # 6、返回多个字段值 HMGET key field filed # 7、返回所有的键值对 HGETALL key # 8、返回所有字段名 HKEYS key # 9、返回所有值 HVALS key # 10、删除指定字段 HDEL key field # 11、在字段对应值上进行整数增量运算 HINCRBY key filed increment # 12、在字段对应值上进行浮点数增量运算 HINCRBYFLOAT key field increment
应用场景:微博好友关注
1、用户ID为key,Field为好友ID,Value为关注时间 user:10000 user:606 20190520 user:10000 user:605 20190521 2、用户维度统计 统计数包括:关注数、粉丝数、喜欢商品数、发帖数 用户为key,不同维度为field,value为统计数 比如关注了5人 HSET user:10000 fans 5 HINCRBY user:10000 fans 1
3.4 集合
特点
无序、去重
元素是字符串类型
最多包含2^32-1个元素
基本命令
# 1、增加一个或者多个元素,自动去重 SADD key member1 member2 # 2、查看集合中所有元素 SMEMBERS key # 3、删除一个或者多个元素,元素不存在自动忽略 SREM key member1 member2 # 4、元素是否存在 SISMEMBER key member # 5、随机返回集合中指定个数的元素,默认为1个 SRANDOMMEMBER key count # 6、返回集合中元素的个数,不会遍历整个集合,只是存储在键当中了 SCARD key # 7、把元素从源集合移动到目标集合 SMOVE source destination member # 8、差集(number1 1 2 3 number2 1 2 4) SDIFF key1 key2 # 9、差集保存到另一个集合中 SDIFFSTORE destination key1 key2 # 10、交集 SINTER key1 key2 SINTERSTORE destination key1 key2 # 11、并集 SUNION key1 key2 SUNIONSTORE destination key1 key2
应用场景
案例: 新浪微博的共同关注
需求: 当用户访问另一个用户的时候,会显示出两个用户共同关注过哪些相同的用户
设计: 将每个用户关注的用户放在集合中,求交集即可
实现:
user001 = {'peiqi','qiaozhi','danni'} user002 = {'peiqi','qiaozhi','lingyang'} user001和user002的共同关注为: SINTER user001 user002 // 结果为: {'peiqi','qiaozhi'}
3.5 有序集合sortedset
特点
有序、去重
元素是字符串类型
每个元素都关联着一个浮点数分值(score),并按照分值从小到大的顺序排列集合中的元素(分值可以相同)
最多包含2^32-1元素
示例
一个保存了水果价格的有序集合
分值 | 2.0 | 4.0 | 6.0 | 8.0 | 10.0 |
---|---|---|---|---|---|
元素 | 西瓜 | 葡萄 | 芒果 | 香蕉 | 苹果 |
一个保存了员工薪水的有序集合
分值 | 6000 | 8000 | 10000 | 12000 | |
---|---|---|---|---|---|
元素 | lucy | tom | jim | jack |
一个保存了正在阅读某些技术书的人数
分值 | 300 | 400 | 555 | 666 | 777 |
---|---|---|---|---|---|
元素 | 核心编程 | 阿凡提 | 本拉登 | 阿姆斯特朗 | 比尔盖茨 |
常用命令
# 在有序集合中添加一个成员 zadd key score member # 查看指定区间元素(升序) zrange key start stop [withscores] # 查看指定区间元素(降序) ZREVRANGE key start stop [withscores] # 删除成员 zrem key member # 增加或者减少分值 zincrby key increment member # 返回元素排名 zrank key member # 返回元素逆序排名 zrevrank key member # 返回集合中元素个数 zcard key # 并集 zunionstore destination numkeys key [weights 权重值] [AGGREGATE SUM|MIN|MAX] # 交集:和并集类似,只取相同的元素 ZINTERSTORE destination numkeys key1 key2 WEIGHTS weight AGGREGATE SUM|MIN|MAX
案例1: 京东商品畅销榜
# 第1天
ZADD mobile-001 5000 'huawei' 4000 'oppo' 3000 'iphone'
# 第2天
ZADD mobile-002 5200 'huawei' 4300 'oppo' 3230 'iphone'
# 第3天
ZADD mobile-003 5500 'huawei' 4660 'oppo' 3580 'iphone'
问题:如何获取三款收集的销量排名?
ZUNIONSTORE mobile-001:003 mobile-001 mobile-002 mobile-003 # 可否?
# 正确
1、ZADD mobile-003 5500 'huawei' 4660 'oppo' 3580 'iphone'
2、ZUNIONSTORE mobile-001:003 mobile-001 mobile-002 mobile-003 AGGREGATE MAX
4.Java-Redis
我们一般使用redis通常需要结合mysql一起来使用,比如某个SSM项目微博管理的首页信息,需要频繁的变更,这时候的数据处理流程大致如下
1.客户端发来请求到controller层,接收到请求后
2.在service层进行业务代码的处理,包括调用mapper存储,使用redis进行缓存
3.在mapper进行数据的保存
在第二步中,比如查询的数据在redis中不存在,需要先去mysql查询,查询后将结果缓存到redis
如果查询的数据在redis中存在,则直接通过redis返回客户端数据
通常叫做 Cache Aside Pattern(旁路缓存模式)
4.1 环境准备,项目集成Redis
1.添加依赖,刷新Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置文件配置数据库连接信息
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=123456
spring.redis.database=0
3.创建Redis配置类 config.RedisConfiguration
package cn.tedu._05weibo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.io.Serializable;
@Configuration
public class RedisConfiguration {
@Bean
public RedisTemplate<String, Serializable> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String,Serializable> redisTemplate = new RedisTemplate<>();
// 设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置字符串序列化器
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.json());
//设置hash的序列化器
redisTemplate.setHashKeySerializer(RedisSerializer.json());
redisTemplate.setHashValueSerializer(RedisSerializer.json());
return redisTemplate;
}
}
4.创建工具类 utils.CacheUtils
package cn.tedu._05weibo.base.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
/**
* Redis的缓存工具类
*/
@Component
public class CacheUtil {
@Autowired
private RedisTemplate<String, Serializable> redisTemplate;
public void set(String key, Serializable value){
redisTemplate.opsForValue().set(key, value);
}
public Serializable get(String key){
return redisTemplate.opsForValue().get(key);
}
public void delete(String key){
redisTemplate.delete(key);
}
public void setExpire(String key, Serializable value, long timeout){
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
}
}
5.微博首页缓存实现
//自动装配缓存工具类[自己创建的工具类,调用方法更方便]
@Autowired
private CacheUtil cacheUtil;
/**
* 2.微博首页列表
*/
@ApiOperation(value = "微博首页列表")
@GetMapping("selectIndex")
public JsonResult selectIndex(){
/*
Redis中首页数据结构:{"weibo_index": List<WeiboIndexVO>}
1.先到Redis中查看是否存在首页缓存数据[get];
2.如果存在,直接返回;
3.如果不存在:
3.1 到MySQL数据库中查询首页数据;
3.2 将查询结果缓存到Redis中[set];
3.3 返回响应;
*/
//ValueOperations<String, Serializable> ops = redisTemplate.opsForValue();
//Serializable weiboIndex = ops.get("weibo_index");
//利用缓存工具类中的工具方法;
Serializable weiboIndex = cacheUtil.get("weibo_index");
if (weiboIndex == null){
// mysql中查询
List<WeiboIndexVO> weiboIndexVOS = weiboMapper.selectIndex();
System.out.println("首页数据来自MySQL数据库");
// 缓存到redis中
//ops.set("weibo_index", (Serializable) weiboIndexVOS, 30, TimeUnit.SECONDS);
// 利用缓存工具类中的方法将首页数据缓存到Redis中
cacheUtil.setExpire("weibo_index", (Serializable) weiboIndexVOS, 30);
// 返回
return JsonResult.ok(weiboIndexVOS);
}
System.out.println("首页数据来自Redis数据库");
return JsonResult.ok(weiboIndex);
}