0. Redis使用场景
- 缓存
- 分布式锁
- 消息队列
- 任务队列
1. 常用命令
为指定key设置过期时间:expire key seconds
设置key对应的value,并设置过期时间:setex key seconds value
查询指定key剩余过期时间:ttl key
返回指定key的value类型:type key
为指定key修改key名称:rename oldkey newkey
setnx key value:设置key和value值,若当前key不存在时,若存在则不执行该操作
2. Redis支持的数据类型
- string
- hash
- list
- set
- sorted set
3. Redis的持久化机制
Redis是一个非关系型的内存数据库,一旦redis重启或断电数据就会丢失[没有持久化的情况下]。
持久化:将redis的数据持久化保存到硬盘上。
Redis提供的持久化机制有如下2种:
- RDB(默认)
- AOF
3.1 RDB
RDB持久化机制是默认开启的持久化机制,一般不需要更改。它会在一定时间内检测key的变化,达到要求就会将内存种的数据集快照写入磁盘,它恢复时是将快照文件直接读取到内存里。
RDB保存的文件时dump.rdb。
触发机制:
- save规则满足的情况下
- 执行flushall命令
- 推出redis也会
3.2 AOF
AOP持久化机制是以独立日志的方式记录每次写命令,并在Redis重启时重新执行AOF文件中的名称来达到恢复数据的目的。
以独立日志的方式:指以日志形式记录每个操作,将Redis运行过程中的所有指令记录下来,redis启动之初就会读取该文件重新构建数据。
AOF保存的是 appendonly.aof 文件。
特点:
- 每一次修改都同步
- 每秒同步一次,可能会丢失一秒的数据
开启AOP持久化机制
将配置文件种的 appendonly 由 no 改为 yes
4. 建议
5. redis删除策略
问题:redis种若有一个key只能存活1小时,那么redis是如何对这个key删除的?
redis有3种删除策略:
- 定期删除 每隔100秒随机抽取3个设置过期时间的key,判断其是否过期,是就删除
- 惰性删除 若定期删除没有随机抽取到那个过期的key,也不怕,惰性删除会在你查询那个key时,先去判断其是否过期,是就删除
- 定时删除 在设置键过期的同时,创建一个定时器,让定时器在键过期时间来临时,执行对键的删除操作。
Redis采用 定期+惰性 的删除策略
6. redis如何保存对象
一般会将对象转换成json字符串,再将其当字符串保存。
7. 什么是缓存穿透,缓存击穿,缓存雪崩
7.1 缓存穿透
缓存穿透:查询一个数据库一定不存在的数据。
7.2 缓存击穿
缓存击穿:指一个key非常热点,扛着大并发几种对其访问,当这个key失效的瞬间,所有请求直接请求到数据库。
7.3 缓存雪崩
缓存雪崩:指一段时间内,缓存集中过期。
7. 为什么Redis是单线程的?为什么单线程还能这么快
原文:
- Redis是基于内存的,内存的读写速度非常快;
- Redis是单线程的,避免了不必要的上下文切换和竞争条件;
- Redis使用多路复用技术,可以处理并发的连接。非阻塞I/O内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间。
为什么Redis是单线程的?
- 官方答案
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。 - 性能指标
关于Redis的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求。 - 详细原因
3.1 不需要各种锁的性能消耗
Redis的数据结构并不全是简单的Key-Value,还有list,hash等复杂的结构,这些结构有可能会进行很细粒度的操作,比如在很长的列表后面添加一个元素,在hash当中添加或者删除一个对象。
这些操作可能就需要加非常多的锁,导致的结果是同步开销大大增加。
总之,在单线程的情况下,就不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗。
3.2 单线程多进程集群方案
单线程的威力实际上非常强大,核心效率也非常高,多线程自然是可以比单线程有更高的性能上限,但是在今天的计算环境中,即使是单机多线程的上限也往往不能满足需要了,需要进一步摸索的是多服务器集群化的方案,这些方案中多线程的技术照样是用不上的。
所以单线程、多进程的集群不失为一个时髦的解决方案。
3.3 CPU消耗
采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU。
但是如果CPU成为Redis瓶颈,或者不想让服务器其他CUP核闲置,那怎么办?
可以考虑多起几个Redis进程,Redis是key-value数据库,不是关系数据库,数据之间没有约束。只要客户端分清哪些key放在哪个Redis进程上就可以了。
7. 什么是分布式锁,如何基于Redis实现分布式锁
https://www.cnblogs.com/zhangshijiezsj/p/14205303.htmlhttps://mp.weixin.qq.com/s/3ndzrH1jb8D1clVIdOvLwA
7.1 单体项目里面锁的使用
举例:
客户下单的时候,我们调用库存中心进行减库存,那我们一般的操作都是:
update store set num = $num where id = $id
但是,当客户量高的时候,出现了一些并发时,就容易出现脏数据问题,就像我们的经典售票案例。
比如 a, b 当前两个事务,查询出来的库存都是 5,a 买了 3 个单子要把库存设置为 2,而 b 买了 1 个单子要把库存设置为 4,那这个时候就会出现 a 会覆盖 b 的更新。
解决办法:
想办法解决脏数据的问题呗,比如让a事务再修改的时候不准其他事务来修改,或者让a事务直到在它修改期间是否有其他事务来改过该数据,是就不就进行更新。
- 乐观锁:假定不会发生数据冲突,所以获取数据的时候不加锁,但是在提交数据的时候,会判断是否被修改过,如果被修改过则不进行数据更新,如果判断没被其他线程修改,则进行数据更新。
- 悲观锁:读取数据之前,假设会发生并发冲突,所以从一开始就为了防止冲突,在获取数据的时候进行加锁,确保自己使用的过程中不会被修改,使用完成之后在进行锁的释放。上锁期间,其他事务不能查询和修改。
Mysql实现乐观锁:在表中加一个version字段,查询数据时得到它的值,更新时加上这个条件。
Mysql实现悲观锁:1) 先关闭mysql的自动提交功能。2)在查询数据时加上"for update"
线程是实现悲观锁:典型的是Synchronized,Lock锁,他们就是悲观锁的体现。
7.2 分布式项目里面锁的使用
在分布式系统中,由于会部署集群,一个应用部署到多台机器上,也就是多个JVM。
如此一来基于JAVA API实现线程的方式就不行了。因为变量会在不同JVM中分配3个内存,加锁控制不了。
那基于数据表的乐观锁或悲观锁呢,能有效不呢:
能有效!!!
基于JAVA API实现的锁,依赖于JVM。
基于MYSQL实现的乐观锁,悲观锁,依赖的是Mysql。
缺点:
当更新的时候,数据被另一个事务修改了,则当前更新不成功。由此可以看出基于mysql的乐观锁适合多读少些的情况,并且具有一定锁表的风险。
悲观锁就更不说了,查询的时候就直接不准其他人访问了。更不适合。
7.3 基于redis实现分布式锁
优点:性能对比ZK和Mysql较好。
缺点:没有mysql实现方便,还涉及lua脚本知识点。
8. SpringBoot整合Redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
redis:
host: localhost
port: 6379
database: 0
# password:
jedis:
# 连接池配置
pool:
max-active: 8 #最大连接数
max-wait: 1ms #连接池最大阻塞等待时间
max-idle: 8 #连接池中的最大空闲连接
min-idle: 0 #连接池中的最小空闲连接