1. 关系型与非关系型数据库对比?
关系型数据库是依据关系模型(一对一、一对多、多对多)来创建的数据库。 优点:支持复杂查询,事务回滚
非关系型模型:列模型,键值对模型(memcache,redis),文档类模型(mongodb)。 优点:不需要解析性能高,耦合低可扩展性好。
2. redis优点:
1.数据存在内存中速度快。类似HashMap,查找和操作的时间复杂度都是O(1)。
2.支持丰富数据类型,string,linked-list(lpop是线程不安全的,可使用blpop),set,zset(sorted sets),hash,bitMap(位图,本质字符串但可以对位进行操作),GEO(地理位置),hyperloglogs(基数统计,基数数量大时耗费的空间很小)
3.支持事务(不会回滚),操作都是原子性
4.丰富的特性,缓存,过期时间等。
3. redis淘汰策略:
maxmemory <bytes> 最大物理内存,超出则启用redis淘汰策略
maxmemory-policy noeviction 淘汰策略
volatile-lru:从已设置过期的数据集中挑选最近最少使用的淘汰
volatile-ttr:从已设置过期的数据集中挑选将要过期的数据淘汰
volatile-random:从已设置过期的数据集中任意挑选数据淘汰
allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
allkeys-random:从数据集中任意挑选数据淘汰
noenviction:禁止淘汰数据
4. redis哈希槽:
Redis 集群有16384个哈希槽(一个节点可能有一个或多个槽,因此最大节点个数:2^14 = 16384)
通过公式:crc16(key) mod 16384 决定key放置在哪个槽上。
优点:实现简单,容易增删节点,不用停掉redis服务。新增时只需要把其他节点的某些哈希槽挪到新节点即可。删除反之
5. 缓存穿透(缓存和db中都没有):
1. 布隆过滤器(对一个key进行多种hash算法,将结果存贮在一亿位上。对于新key只需要判断每种hash算法结果位上是否有值)
2. 缓存空对象(有效期略短)
6. 缓存击穿(热点数据失效后,大量访问)
1. 热点数据永不失效
2. 线程加锁
7. 缓存雪崩
1. 失效时间加个随机值
2. 热点数据永不失效
3. 热点数据均匀分布在不同的 Redis 库(高可用性Redis集群,防止宕机)
8. 分布式锁
setnx加锁,del删除锁,expire设置过期时间防止无法释放造成的死锁
9. redis异步队列:
定时任务。list 结构,rpush 生产消息,lpop 消费消息。当 lpop 没有消息的时候,要适当 sleep 再重试(空轮询拉高客户端的CPU)
10. 持久化
rdb:快照,保存在dump.rdb中(默认方式)
aof:增量,保存redis命令到appendonly.aof中
11. 缓存和数据库一致性解决方案:
情况1:先删除缓存再写入数据库。写入时,其他进程进来读取了老数据写入了缓存。此时缓存中为脏数据。
情况2:先写入数据库再删除缓存。如果写入成功后,删除缓存前服务器宕机了。此时缓存中也为脏数据。
方案1:延时双删 + 有效期设置:a.先删除缓存,b.再写入数据库,c.休眠500毫秒,d.再删除缓存。
方案2:异步更新缓存:mysql binlog增量订阅消费+消息队列+增量数据更新到redis(阿里的canal框架)
12. 如何理解redis单线程非阻塞?
单线程:避免了不必要的上下文切换和竞争条件。
非阻塞IO:IO多路复用,Redis采用epoll做为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接,读写,关闭都转换为时间,不在I/O上浪费过多的时间。
13.主从配置:
从服务配置:slaveof <masterip> <masterport>
14. 哨兵
作用:1.监测主数据库和从数据库是否正常运行。2.当主数据库出现故障的时候,自动将一个从库转换为主库
步骤:哨兵监控也是有集群的,会有多个哨兵进行监控,当判断发生故障的哨兵达到一定数量的时候才进行修复。一个健壮的部署至少需要三个哨兵实例。
1.每个Sentinel 1s/次 向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令
2.如果实例距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 所指定的值, 则这个实例会被 Sentinel 标记为主观下线
3.如果一个 Master 被标记为主观下线,则正在监视这个 Master 的所有 Sentinel 要以每秒一次的频率确认 Master 的确进入了主观下线状态
4.当有足够数量的 Sentinel(≥配置文件指定值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线
5.在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有 Master,Slave 发送 INFO 命令(1.发现 slave 节点,2.确定主从)
6.当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10s/次 改为 1s/次
7.若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。
15. 性能与调优:
1.Redis异步尽量不要用,因为Redis延迟本身很小,大概在100us-200us。另外Redis是单线程的,异步任务切换的耗时比网络耗时还要大
2.在Linux上多实例部署,实例个数等于处理器个数,各实例最大内存直接为本机物理内存,避免单个实例内存撑爆
3.把海量数据(10亿+)根据key哈希(Crc16/Crc32)存放在多个实例上(哈希槽),读写性能成倍增长。
4.采用二进制序列化,而非常见的Json序列化。
5.Redis的主要性能瓶颈是序列化、网络带宽和内存大小,滥用时处理器也会达到瓶颈。
6.Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件。如果数据比较重要,在某个Slave上开启AOF备份数据,策略设置为每秒同步一次。
7.主从复制不要用图状结构,使用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
16. redis zset跳跃表:
17. Redis,MongoDB,Memcached 对比:
18. redis LRU算法实现:
LRU的经典实现:
hashMap + 双向Linkedlist
Redis的近似LRU实现:
redis嫌弃LinkedList占用的空间太大。在redisObjct中存储了对象最后一次被命令程序访问的时间。
当需要移除Key时,随机取N个(配置项maxmemory-samples,默认为5)Key的空闲时间与缓冲池(默认有16个key会按照空闲时间排好序)里的空闲时间最小的key作对比。如果大于则加入到缓冲池中。同时淘汰的时候会从pool中选择空闲时间最大的key淘汰掉。
19. pipeline:
每个命令的执行时间 = 客户端到服务器的时间+命令排队的时间+命令真正执行时间+结果从服务器到客户端的时间。第一个和第四个消耗的时间总和称为RTT
不使用管道的时候,批量操作1000条数据所需要的时间是:1000次RTT时间+1000次命令执行时间
使用pipeline操作一次传递命令时,所需要的时间是1次RTT时间+1000次命令执行时间:
网友评论