redis是完全免费的,遵守BSD协议,是一个高性能的键值数据库,是当前最热门的Nosql(Not Only SQL)数据库之一,也被人们称为数据结构服务器
### redis的应用场景
1**.缓存**(热点数据,例如:热点新闻)、2. 数据共享分布式、3. 分布式锁、4. 全局ID、5. **计数器**(文章计数器,点赞)、6. 限流、7. 位统计、8. **购物车**、9. 用户消息时间线timeline、10. 消息队列、11. 抽奖、12. 点赞、签到、打卡、13. 商品标签、14. 商品筛选、15. 用户关注、推荐模型、16. 排行榜
redis击穿,穿透,雪崩
**击穿**:
指的是**单个key**在缓存中查不到,去数据库查询
解决:1).通过synchronized+双重检查机制:某个key只让一个线程查询,阻塞其它线程
2.设置value永不过期
3.使用互斥锁(mutex key)
互斥锁缺点:
1. 代码复杂度增大
2. 存在死锁的风险
3. 存在线程池阻塞的风险
**雪崩**
雪崩指的是**多个key**查询并且出现**高并发**,缓存中失效或者查不到,然后都去db查询,从而导致db压力突然飙升,从而崩溃。
解决办法:
1.缓存失效后,通过加锁或者队列来控制数据库写缓存的线程数量. 但 加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量
2.不同的key,设置不同的过期时间,让缓存失效的时间尽量均衡
3.做二级缓存
**穿透**
一般是出现这种情况是因为恶意频繁查询才会对系统造成很大的问题: key缓存并且数据库不存在,所以每次查询都会查询数据库从而导致数据库崩溃。
解决办法
使用布隆过滤器
缺点: 1 会存在一定的误判率
2 对新增加的数据无法进行布隆过滤
3 数据的key不会频繁的更改
## 缓存淘汰策略(Redis强化)
redis保存大量数据,如果Redis服务器内存满了,需要淘汰就数据,才能继续保存.
noeviction:该策略为默认策略,数据不删除,内存写满后对客户端返回error信息。
设置过期时间的数据中淘汰
对于设置过期时间的数据,其淘汰时机不止数据达到maxmemory,当数据达到过期时间也会触发淘汰策略。
volatile-random:设置过期时间的数据中随机删除
volatile-ttl:设置过期时间的数据中根据过期时间的先后顺序删除,越早过期的越先被删除。
**volatile-lru**:删除距离上次使用间隔最长。
volatile-lfu:设置过期时间的数据中使用频率少的删除。
所有数据中淘汰
allkeys-lru:所有数据中使用lru算法删除。
allkeys-random:所有数据随机删除。
allkeys-lfu:频率少的删除。
#### Redis持久化
redis将信息保存在内存
内存的特征断电,信息丢失 Redis数据丢失,需要从数据库从新查询所有数据,耗费时间可能很长
Redis本身有新数据,没有和数据库同步且断电,在服务器将Redis中数据保存在当地硬盘上
**持久化应用场景**:秒杀\高频读、低频写\计数器
Redis恢复策略
**RDB**:在默认配置中,Redis将内存数据库快照保存在名字为dump.rdb的二进制文件中。
RDB方式优缺点:
缺点:
RDB每次持久化需要将所有内存数据写入文件,然后替换原有文件,当内存数据量很大的时候,频繁的生成快照会很耗性能。
如果将生成快照的策略设置的时间间隔很大,会导致redis宕机的时候丢失过的的数据。
优点:
应为dump.rdb文件是二进制文件,所以当redis服务崩溃恢复的时候,能很快的将文件数据恢复到内存之中。
**AOF**:为解决RDB方式丢失数据的问题,从1.1版本开始,redis增加了一种更加可靠的方式:AOF持久化方式。
在使用AOF方式时,redis每执行一次修改数据命令,都会将该命令追加到appendonly(啊判的哦累).aof文件中
**redis重启**的时候,会重放appendonly.aof中的命令恢复数据。
#### Redis存储原理
Redis存储机制分成两种**Snapshot和AOF**。 无论是那种机制,Redis都是将数据存储在内存中。 Snapshot工作原理: 是将数据先存储在内存,然后当数据累计达到某些设定的值的时候,就会触发一次DUMP操作,将变化的数据一次性写入数据文件(RDB文件)。
一、什么是[分布式]锁?
要介绍分布式锁,首先要提到与分布式锁相对应的是[线程锁](https://so.csdn.net/so/search?q=线程锁&spm=1001.2101.3001.7020)、进程锁。
线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state)。
进程锁:为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronized等线程锁实现进程锁。
分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。
二、分布式锁的使用场景。
线程间并发问题和进程间并发问题都是可以通过分布式锁解决的,但是强烈不建议这样做!因为采用分布式锁解决这些小问题是非常消耗资源的!分布式锁应该用来解决分布式情况下的多进程并发问题才是最合适的。
有这样一个情境,线程A和线程B都共享某个变量X。
如果是单机情况下(单JVM),线程之间共享内存,只要使用线程锁就可以解决并发问题。
如果是分布式情况下(多JVM),线程A和线程B很可能不是在同一JVM中,这样线程锁就无法起到作用了,这时候就要用到分布式锁来解决。
三、分布式锁的实现(Redis)
分布式锁实现的关键是在分布式的应用服务器外,搭建一个存储服务器,存储锁信息,这时候我们很容易就想到了Redis。首先我们要搭建一个Redis服务器,用Redis服务器来存储锁信息。
在实现的时候要注意的几个关键点:
1、锁信息必须是会过期超时的,不能让一个线程长期占有一个锁而导致死锁;
2、同一时刻只能有一个线程获取到锁。
锁的分类
1.锁的粒度分:标所 行锁
2.锁的类型分:
共享锁:叫做share锁/s锁 (读取数据 :查询操作)
特点:可以给表加,也可以给行数据加,其特点为:给目标数据加上share锁之后允许其他事务所继续对 该数据加share锁,不允许其他事物对该数据加其他锁
排它锁/独占锁:也叫x锁
特点:给数据加排它锁,不允许其他事务继续给该数据加排它锁,同事不允许其他锁事务给该数据加共享锁,适用于写操作(增删改操作)
悲观锁:
当多事务/多线程并发执行时,事务总是悲观的认为,在自己访问数据期间,其他事务一定会并发执行,此时会产生线程安全问题,所以为了保证线程安全,这个事务在访问数据时,立即给数据加锁,从而保证线程安全.
特点:可以保证线程安全,但是并发执行效率低下
synchronized 排它锁都是悲观锁的应用
乐观锁:
在多线程/多事务并发执行中,某个事务总是乐观的认为,在自己执行期间,没有其他事务与之并发,认为不会产生线程安全问题,所以不会给数据加锁;但是确实存在其他事务与之并发执行的情况,确实存在线程安全问题,为了保证线程安全,通过版本号机制或CAS来保证线程安全.1
死锁:
死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
当主服务器中断服务后,可以将一个从服务器升级为主服务器,以便继续提供服务,但是这个过程需要人工手动来操作。为此,Redis2.8 中提供了哨兵工具来实现自动化的系统监控和故障恢复功能。哨兵的作用就是监控 Redis 系统的运行状况,它的功能包括以下两个。
1、监控主服务器和从服务器是否正常运行。
2、主服务器出现故障时自动将从服务器转换为主服务器。
Redis和数据库的数据一致性问题
如果缓存在Redis中存在,即缓存命中,则直接返回数据。
如果Redis中没有对应缓存,则需要直接查询数据库,然后存入Redis,最后把数据返回。
通常情况下,我们会为某个缓存设置一个key值,并针对key值设置一个过期时间,如果被查询的数据对应的key过期了,则直接查询数据库,并将查询得到的数据存入Redis,然后重置过期时间,最后将数据返回
一致性问题
在Redis的key值未过期的情况下,用户修改了个人信息,我们此时既要操作数据库数据,也要操作Redis数据。导致我们现在面临着2种选择:
1. 先操作Redis的数据,在操作数据库的数据。
2. 先操作数据库的数据,在操作Redis的数据。
不论选择哪种处理方式,最理想的情况下,两个操作要么同时成功,要么同时失败,否则就会出现Redis和数据库的数据不一致情况。
方案选择
是删除缓存还是更新缓存?
当数据库的数据发生变化的时候,Redis的数据也需要进行相应的操作,那么这个操作是选择 更新or删除呢?
更新的话调用Redis的set方法,新值替换旧值;直接删除原来的缓存,下次查询直接去数据库读取,然后再更新Redis。
结论:推荐直接使用删除操作。
使用更新操作的话,会面临两种选择
1.先更新缓存,再更新数据库。
2.先更新数据库,再更新缓存。