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是单线程的?为什么单线程还能这么快

原文:https://www.cnblogs.com/panchanggui/p/12597920.html

  • Redis是基于内存的,内存的读写速度非常快;
  • Redis是单线程的,避免了不必要的上下文切换和竞争条件;
  • Redis使用多路复用技术,可以处理并发的连接。非阻塞I/O内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间。

为什么Redis是单线程的?

  1. 官方答案
    因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。
  2. 性能指标
    关于Redis的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求。
  3. 详细原因

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实现分布式锁

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脚本知识点。

几种实现分布式锁的方式对比:https://www.cnblogs.com/heyanan/p/13591217.html
基于注解的redis实现分布式锁:https://blog.csdn.net/tianyaleixiaowu/article/details/90036180

原文地址:https://www.cnblogs.com/itlihao/p/14969705.html