Cache

原创
2018/03/18 22:25
阅读数 14

缓存穿透 

给不存在的key设个空值

缓存雪崩

过期时间加随机值

缓存击穿

永远不过期

https://blog.csdn.net/zeb_perfect/article/details/54135506

 

Redis

Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作。

优点:

  • 性能极高 – 数据存在内存中,操作时间复杂度O(1),读速度110000次/s,写速度81000次/s ,比memcached快很多
  • 数据类型丰富 – Redis支持二进制的 String, List(string类型的双向链表), Hash(string类型的k-v表), Set(无序集合, 通过hash table实现) 及 Zset(Sorted Set, 有序集合,每个元素有一个score,元素不能重复,但score可以重复。通过跳表实现,可用来进行快速排序) 数据类型操作,而memcached只支持字符串
  • 原子性 – Redis的所有单个操作都是原子性的(对数据的更改要么全执行,要么全不执行),多个操作支持事务,非原子性,通过MULTI和EXEC指令包起来
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性
  • 单个value最大支持1G,而memcached只支持1M
  • redis可以持久化数据,而memcached不行

缺点:

  • 数据库容量受物理内存限制,不能做海量数据高性能读写

特性:

  • redis是单线程的,利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销

 

原理:
1. 完全基于内存
2. IO多路复用:单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减少了线程上下文切换
3. Redis采用了单线程的模型,保证了每个操作的原子性,也减少了线程上下文切换;所有命令进入一个队列等待执行
4. hash结构,读取快;使用特殊数据结构存储数据,加快读取
5. 自己实现事件分离器,效率高

 

持久化

Redis持久化有RDB和AOF两种机制:
RDB:Redis DataBase,以指定的时间间隔,用bgsave数据集快照的方式存储到dump.rdb。bgsave:fork and cow,fork指redis通过创建子进程来进行bgsave操作;cow指的是copy on write,子进程把快照写入一个临时dump文件,写入完成后用新rdb文件替换掉老的文件。
    1. 数据不安全,写入有时间间隔
    2. 相对于数据集大时,比AOF的启动效率更高
    3. 性能最大化,fork子进程来完成写操作,让主进程继续处理命令
AOF:Append Only File,以日志的方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复数据。
aof日志sync属性:如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。
    1. 数据安全
    2. 文件会比RDB形式的文件大,数据集大的时候,比RDB启动效率低
    3. 运行效率低于RDB

 

Redis集群

Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务
Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。多master + 读写分离 + 高可用

 

Redis Sentinal

1. slave与master建立连接,然后发送sync命令。
2. master都会启动一个后台进程,将数据库快照保存到文件中,同时master主进程会开始收集新的写命令并缓存。
3. 后台进程完成写文件后,master就发送文件给slave,slave将文件保存到硬盘上,再加载到内存中,接着master就会把缓存的命令转发给slave,后续master将收到的写命令发送给slave。
** master同步数据时是非阻塞式的,可以接收用户的读写请求。然而在slave端是阻塞模式的,slave在同步master数据时,并不能够响应客户端的查询。

好处(从服务器是只读的,可以一主多从)
1. 主服务器进行读写时,会转移到从读,减轻服务器压力
2. 热备份 主从都可以设置密码,也可以密码不一致

Redis的主从架构,如果master发现故障了,还得手动将slave切换成master继续服务,手动的方式容易造成失误,导致数据丢失,这时需要哨兵自动将slave切换成master。
哨兵的作用:
1、监控redis进行状态,包括master和slave
2、当master down机,能自动将slave切换成master

 

Redis Cluster

Redis Cluster是一种服务器Sharding技术,3.0版本开始正式提供。
分布式数据库首要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整个数据的一个子集。常见的分区规则有哈希分区和顺序分区。Redis Cluster采用哈希分区规则。常见的哈希分区有以下几种:

  • 节点取余分区
  • 一致性哈希分区
  • 虚拟槽分区

Redis Cluster中,Sharding采用slot虚拟槽的概念,一共分成16384个槽。对于每个进入Redis的键值对,key根据哈希函数映射到0 ~ 16383,计算公式:slot = CRC16(key)&16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。当动态添加或减少node节点时,需要将16384个槽做个再分配,槽中的键值也要迁移。当然,这一过程,在目前实现中,还处于半自动状态,需要人工介入。
Redis集群,要保证16384个槽对应的node都正常工作,如果某个node发生故障,那它负责的slots也就失效,整个集群将不能工作。为了增加集群的可访问性,官方推荐的方案是将node配置成主从结构,即一个master主节点,挂n个slave从节点。这时,如果主节点失效,Redis Cluster会根据选举算法从slave节点中选择一个上升为主节点,整个集群继续对外提供服务。这非常类似前篇文章提到的Redis Sharding场景下服务器节点通过Sentinel监控架构成主从结构,只是Redis Cluster本身提供了故障转移容错的能力。
Redis Cluster的新节点识别能力、故障判断及故障转移能力是通过集群中的每个node都在和其它nodes进行通信,这被称为集群总线(cluster bus)。它们使用特殊的端口号,即对外服务端口号加10000。例如如果某个node的端口号是6379,那么它与其它nodes通信的端口号是16379。nodes之间的通信采用特殊的二进制协议。
对客户端来说,整个cluster被看做是一个整体,客户端可以连接任意一个node进行操作,就像操作单一Redis实例一样,当客户端操作的key没有分配到该node上时,Redis会返回转向指令,指向正确的node。
 

 

发布订阅模式

dubbo的redis注册中心基于此实现
风险:消费者下线时会丢失消息,建议用rabbitmq等专业队列。

 

事务

开始事务:MULTI
触发事务:EXEC
Redis命令是原子性的,但事务不是原子性的,事务中某条命令失败不会导致回滚或中止,后面的命令继续执行。

 

安全

Redis有密码,没账户
设置密码:CONFIG set requirepass "123"
查询密码:CONFIG get requirepass
验证密码:AUTH password

 

客户端连接

客户端 socket 会被设置为非阻塞模式,服务端采用非阻塞多路复用模型
为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法
创建一个可读的文件事件用于监听这个客户端 socket 的数据发送

最大连接数
默认10000,可在redis.conf设置
启动:redis-server --maxclients 100000

 

set命令

key
value
nxxx:NX(key不存在时才set)XX(key存在时才set)
expx:EX(秒) PX(毫秒)
expire time:超时时间

set成功返回OK,命令错误返回 (error)错误原因

 

keys命令:

扫出指定模式的key列表,redis的单线程的,keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重。

key大量超时

如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在超时时间上加一个随机值,使得过期时间分散一些。

 

Pipeline:

可以一次发送多个命令,并按顺序执行、返回结果,节省RTT(Round Trip Time)。
打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并不是打包的命令越多越好。
pipeline中发送的每个command都会被server立即执行,如果执行失败,将会在此后的相应中得到信息;也就是pipeline并不是表达“所有command都一起成功”的语义;但是如果pipeline的操作被封装在事务中,那么将有事务来确保操作的成功与失败。

        String key = "pipeline-test";  
        String old = jedis.get(key);  
        Pipeline p1 = jedis.pipelined();  
        p1.incr(key);  
        p1.incr(key);  
        //结束pipeline,并开始从相应中获得数据  
        List<Object> responses = p1.syncAndReturnAll();

 

Transactions:

Redis提供了简单的“事务”能力,MULTI,EXEC,DISCARD,WATCH/UNWATCH指令用来操作事务。
    1) MUTIL:开启事务,此后所有的操作将会添加到当前链接的事务“操作队列”中。
    2) EXEC:提交事务
    3) DISCARD:取消事务,记住,此指令不是严格意义上的“事务回滚”,只是表达了“事务操作被取消”的语义,将会导致事务的操作队列中的操作不会被执行,且事务关闭。
    4) WATCH/UNWATCH:“观察”,这个操作也可以说是Redis的特殊功能,但是也可说是Redis不能提供“绝对意义上”的事务能力而增加的一个“补充特性”(比如事务隔离,多事务中操作冲突解决等)。

        String key = "transaction-key";  
        jedis.set(key, "20");  
        jedis.watch(key);//注册key,此后key将会被监控,如果在事务执行前被修改,则导致事务被DISCARD。  
        jedis.incr(key);//此key被修改,即使是自己,也会导致watch在事务中执行失效  
        jedis.unwatch();//取消注册  
        jedis.watch(key);//重新注册,在重新注册前,必须unwatch  
        Transaction tx = jedis.multi();//开启事务  
        tx.incr(key);  
        tx.incr(key);  
        tx.incr(key);  
        List<Object> result = tx.exec(); 

 

Redis实现分布式锁

分布式锁特性:
    1. 互斥性,任一时间只能有一个客户端持有锁
    2. 不会死锁,持有锁的客户端断连后,保证其他客户端后续能正常获取锁
        死锁:是指多个进程竞争同一个资源造成的一种阻塞现象
    3. 容错性,redis个别节点挂掉也能提供锁

实现:
    SET lock_key random_value NX PX 30000
        lock_key:分布式锁的key
        random_value:随机值,如UUID,DEL时校验,避免锁超时后误删别的客户端的锁
        NX:不存在时才设置key的值,保证任一时间只能有一个客户端持有锁
        PX:设置超时时间,避免因客户端挂掉使锁无法释放,导致死锁
        30000:超时时间
        **:必须保证客户端的任务在超时时间内完成

 

Redis异步队列

使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。blpop,在没有消息的时候,阻塞等待消息

 

Redis分布式计数

可用incr进行分布式计数,必须预先设key并把值设为0或别的数字,数字大小限制为有符号的long型
数字以String形式存储
incr方法返回数字新值

 

Redis, DB数据同步

读:先读Redis,后读DB,缓存设超时时间
写:先写DB,后更新Redis
强一致性不建议使用缓存

 

 

展开阅读全文
打赏
0
0 收藏
分享

作者的其它热门文章

加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部