掌握高并发、高可用架构第三章 分布式

本章介绍分布式架构的底层技术。主要说明面试过程中可能被问到的技术点。

第四节 缓存

缓存穿透 缓存学雪崩 Redis Memcached Guava

1. 缓存使用中的几个问题

缓存穿透:查询缓存和数据库中一定不存在的数据时,每次查询都是直接到数据库的,这种现象称为缓存穿透

此时,如果有大量的请求时,会对数据库造成直接冲击,甚至会导致崩溃

缓存空数据使用BloomFilter(布隆过滤器),即缓存数据库中所有存在的Key

缓存雪崩:缓存服务器宕机了,那么所有查询直接落在数据库,对数据库造成巨大压力

使用缓存集群,增大可用性本地缓存使用Hystrix(熔断器),它通过熔断、降级、限流三个手段来降低雪崩造成的损失持久化机制

热点数据集中失效:本身大批量数据同时失效对Redis服务器也会造成影响,可能会出现慢的情形;再者失效期间,所有的用户请求都会落到数据库上,也会对数据库造成巨大的压力

使用互斥锁,当发现缓存失效后,第一个请求加锁,然后去查询数据库,把数据存入缓存,这个操作只有一次,其他的请求都是阻塞的,查询执行完成后释放锁,此时已经有了缓存数据,也不会对数据库造成压力了对热点数据设置不同的失效时间,加上时间戳2. Redis的数据结构

string

Redis的最基本的数据结构,可以包含任意数据。一个key对应一个string的value。string类型是二进制安全的。每个string的value最大可以512M
支持的方法:set、get、mset、mget、setex、setnx

list

列表,一个简单的字符串列表,按照插入的顺序排序,可以向头部(左边)或尾部(右边)添加数据,底层是链表
支持的方法:lpush、lpushx、lpop、lrange、lset、lindex、llen、rpush、rpop

set

string的无序集合
支持的方法:sadd、scard、sdiff、sinter、sunion、sismember、smembers

zset(sorted set)

sorted set有序集合,也是string的集合,但是有序的
支持的方法:zadd、zcard

hash

hash是一个键值对集合,是一个string类型的key(这里叫做field)和value的映射表,key相当于hash的名字,类似于Java中的Map<String, Map<String, String>>
支持的方法:hset、hget、hdel、hexists、hkeys、hlen、hgetall

3. Redis适合的场景会话缓存(Session Cache)全页缓存(FPC)队列,利用list数据结构排行榜/计时器,利用zset发布/订阅系统4. Redis处理并发竞争问题

Redis采用单进程单线程模式,本身没有锁的概念,其对于多个客户端访问不存在并发的问题

通过jedis客户端进行并发访问时会出现连接超时、数据转换错误、阻塞等,通过以下方式解决:

客户端与Redis通信时,可以对连接进行池化,每个对Redis的读写操作均采用synchronized服务器端可以使用setnx5. Redis设置过期时间在set key的时候,可以指定expire time,也就是过期时间Jedis有单独的expire方法

redis通过定期删除和惰性删除来删除过期的key

定期删除,Redis每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。因为数据量巨大,遍历全部key不现实,太耗CPU,所以是随机的惰性删除,发起查看key时,会查看key是否过期,如果过期则删除

只靠这两种机制还是删除不完全的,此时就需要内存淘汰机制了

6. Redis内存淘汰机制(如何保证热点)volatile-lru:从已设置过期时间的数据集合(server.db[i].expires)挑选最近最少使用的数据淘汰volatile-ttl:从已设置过期时间的数据集合中挑选将要过期的数据淘汰volatile-random:从已设置过期时间的数据集合中随机挑选数据删除allkeys-lru:在键空间中挑选最少使用的数据淘汰(这种策略最常用)allkeys-random:从数据集(server.db[i].dict)中随机淘汰no-eviction:禁止淘汰数据,当写入数据时报错处理

4.0新增

volatile-lfu:从已设置过期时间的集合中挑选最不常用的数据淘汰allkeys-lfu:在键空间中挑选最不常用的数据淘汰7. Redis的持久化策略

支持两种持久化策略:一种是快照(snapshotting,RDB),另一种是只追加文件(append-only file,AOF)

快照持久化(RDB),通过创建快照来存储在内存里的数据在某个时间点的副本;创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器,还可以留在本地

是默认的持久化方式。在redis.conf中有以下配置

save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,就会触发BFSAVE创建快照save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,就会触发BFSAVE创建快照save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,就会触发BFSAVE创建快照AOF(append-only file),AOF的实时性更好,默认没有开启,通过以下设置进行开启

appendonly yes

开启AOF后,每执行一条更改数据的命令,Redis就将该命令写入硬盘中的AOF文件

在redis.conf中有三种不同的AOF配置

appendfsync always #每次更改数据都会写入AOF文件,这会严重降低Redis的性能appendfsync everysec #每秒同步一次,将多个命令同步到磁盘appendfsync no #由操作系统决定何时同步8. Redis事务

通过MULTIEXECWATCH等命令来实现事务功能

9. Redis的分布式锁

通过setnx实现

10. Redis与Memcached的区别Redis支持丰富的数据结构,Memcached仅支持字符串Redis支持持久化,Memcached不支持Redis支持主从、哨兵、Cluster的集群模式,Memcached没有Redis是单线程多路IO复用模型,Memcached是多线程非阻塞IO复用模型11. Redis的集群主从模式

主数据库(master)和从数据库(slave)

主数据库进行读写操作,当数据发生变化时会自动同步到从数据库从数据库是只读的

一个master对应多个slave,一个slave只能有一个master

哨兵模式

哨兵的作用是监控Redis系统的运行情况

监控主从是否正常运行master出现故障时,自动将slave转换为master多哨兵之间也会互相监控

多个哨兵监控一个master

集群模式

只要将每个节点的cluster-enable配置打开即可,每个集群最少三个节点

Memecached

选择Memcached的理由:简单

本地缓存:Ehcache、GuavaRedis和Memcached的区别Redis支持的数据结构更丰富Redis支持持久化,Memcached不支持Memcached没有原生的集群模式,而Redis有主从、哨兵、集群Memcached是多线程、非阻塞IO复用的网络模型,Redis是单线程多路IO复用的网络模型