1 redis特点
内存数据库,读写速度快,被应用于缓存。
数据类型丰富,支持事务、持久化、LUA脚本、LRU驱动事件、多种集群方案。
丰富的特性:缓存,消息,过期,自动删除
2 数据类型:
hash:k-v集合,适用于存储对象。由于组合式的压缩,内存利用率更高。
字符串:一个键最大能存512MB
列表:按照插入顺序排序。
异步队列:rpush为生产消息,lpop为消费消息。但是消费者下线时,生产消息会丢失。
set集合:string类型的无序集合,通过哈希表实现,增删查的复杂度是O(1)。
zset:有序集合,且不重复。
3 存储结构:
redis通讯协议RESP格式的命令文本存储。是redis客户端和服务端之前使用的一种通讯协议。特点:实现简单、快速解析、可读性好。
4 redis做缓存的原因
高性能:
a 假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。b 将该用户访问的数据存在缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。c 如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
高并发:
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
分布式缓存:
使用 redis 或 memcached 之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。缺点是需要保持 redis 或 memcached服务的高可用,整个程序架构上较为复杂。
5 单线程redis快速的原因:
1.完全基于内存操作,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,查找和操作的时间复杂度都是O(1)
忽略磁盘操作。
2.数据结构简单,对数据操作也简单
3.采用单线程,避免频繁的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
4.采用I/O多路复用机制,非阻塞。
5.使用底层模型不同,它们之间底层实现方式以及客户端之间的通信的应用协议不一样,redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
6 持久化技术
持久化就是将内存的数据写到磁盘中,防止宕机时内存数据丢失。
持久化策略包括RDB(redis database)快照和AOF(append-only file)日志。
RDB是每隔一段时间对redis进行一次持久化,速度快,性能好,但是数据不完整。核心函数有rdbSave为rdb文件和rdbLoad。
AOF是重新执行命令,数据完整,安全但是速度慢,文件大。主要是调用flushappendonlyfile函数。将aof_buf中的缓存写入到AOF文件,或者是调用fsync、fdatasync函数,将AOF文件保存到磁盘中。
7 架构模式:单机版和分布式
单机版:内存容量有限,处理能力有限,无法高可用。
分布式:主从复制。保证数据相同。无法保证高可用。没有解决master写的压力。
哨兵:分布式系统中监控redis主从服务器,并在主服务器下线时自动进行故障转移。
特性:监控:不断地检查主从服务器是否运作正常。
提醒:当被监控的某个redis服务器出现问题时,哨兵可以通过API向管理员或者其他应用程序发送通知。
自动故障迁移:当一个主服务器不能正常工作时,哨兵会开始一次自动故障迁移操作。
特点:保证高可用、监控各个节点、自动故障迁移;主从模式,切换需要时间,可能丢数据,没有解决master写的压力
集群proxy:
特点:twemproxy,代理服务器,快速的,单线程。
优点:可以支持多种hash算法,失败节点会自动删除,后端sharding分片逻辑对业务透明,业务方的读写方式和操作单个redis一致。
缺点:增加新proxy时,需要维护高可用。不支持故障的自动转移。
需要自己实现failover,扩展性差。手动干预扩容和缩容。
集群codis:
和twemproxy效果一致,但是支持在节点数量改变的情况下,旧节点恢复到新hash节点。
集群直连型:无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他节点连接。
特点:没有中心节点,没有proxy层。
节点间数据共享,可以动态调整数据分布。
可扩展,到1000个节点,节点可动态增加或者删除。
高可用,部分节点不可用时,集群仍然可以使用。slave做备份数据。
实现故障自动failover,节点间通过gossip协议交换状态信息,投票机制完成slave到master的角色提升。
缺点:资源隔离性差,容易互相影响。
数据异步复制,不保证数据的一致性。
hash槽:根据CRC16(key) mod 16384,觉得key-value放入哪个桶(节点)。
8 redis和memcached区别
- redis支持更丰富的数据类型(支持更复杂的应用场景):Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。
- Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中。
- 集群模式:memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 redis 目前是原生支持 cluster 模式的.
- Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的多路 IO 复用模型。
-
Memcached类似于一致性哈希的分布式算法实现分布式存储。
一致性哈希:DHT可以通过减少影响范围的方式,解决增减服务器导致的数据散列问题,解决分布式环境下负载均衡问题。如果存在热点数据,可以通过增添节点的方
9 redis设置过期时间
Redis中有个设置时间过期的功能,即对存储在 redis 数据库中的值可以设置一个过期时间。作为一个缓存数据库,这是非常实用的。如我们一般项目中的 token 或者一些登录信息,尤其是短信验证码都是有时间限制的,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。
我们 set key 的时候,都可以给一个 expire time,就是过期时间,通过过期时间我们可以指定这个 key 可以存活的时间。如果假设你设置了一批 key 只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?
定期删除+惰性删除。
- 定期删除:redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!
- 惰性删除 :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈!
但是仅仅通过设置过期时间还是有问题的。我们想一下:如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了。怎么解决这个问题呢? redis 内存淘汰机制。
10 redis内存淘汰机制
redis 提供 6种数据淘汰策略:
- volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。
4.0版本后增加以下两种:
- volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
- allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key
11 redis事务(不是原子性)
Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。
注意:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
12 缓存雪崩和缓存穿透
缓存雪崩:
当缓存服务器重启或者大量缓存集中在一个时间段失效,失效时会对后端系统带来很大压力,严重时数据库会宕机。
避免:
1.缓存失效后,通过加锁或者队列控制读数据库、写缓存的线程数量。比如某个key只允许一个线程查询数据和写缓存,其他线程等待
2.做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,访问A2,A1缓存失效时间短期,A2为长期
3.不同的key设置不同的过期时间,让缓存失效的时间点尽量均匀。
缓存穿透:
一般的缓存系统是按照key去缓存查询,不存在对应的value时,会去后端系统查询。而恶意的请求会故意查询不存在的key,请求量直接到数据库上,请求量很大时,会对后端系统造成很大压力,就叫缓存穿透。
正常处理流程:
解决:
1.查询结果为空的情况进行缓存过期时间,缓存时间短,最长不超过5分钟,或者key对应的数据插入后清理缓存。
2.使用布隆过滤器。对一定不存在的key进行过滤。把所有可能存在的key放在一个大的bitmap(哈希表)中,查询时通过bitmap过滤,这样避免了对底层存储系统的查询压力。
13 分布式锁
原因:
redis并发竞争key时,多个系统同时对一个 key 进行操作,但是最后执行的顺序和我们期望的顺序不同,这样也就导致了结果的不同!
先使用setnx抢锁,设置超时时间以释放锁、超过一半的redis实例设置成功,就表示加锁完成。
注意:如果不存在 Redis 的并发竞争 Key 问题,不要使用分布式锁,这样会影响性能