您现在的位置是:主页 > news > php网站制作教程/南京网站设计公司大全
php网站制作教程/南京网站设计公司大全
admin2025/6/28 2:57:32【news】
简介php网站制作教程,南京网站设计公司大全,wordpress logo,建设企业网站电话是多少文章目录1. 在项目中缓存是如何使用的?缓存如果使用不当会有什么后果?2. redis和memcached有什么区别?redis的线程模型是什么?为什么单线程的redis比多线程的memcached效率要高得多?(为什么redis是单线程的…
文章目录
- 1. 在项目中缓存是如何使用的?缓存如果使用不当会有什么后果?
- 2. redis和memcached有什么区别?redis的线程模型是什么?为什么单线程的redis比多线程的memcached效率要高得多?(为什么redis是单线程的但是还可以支撑高并发?)
- 2.1 redis和memcached有什么区别
- 2.2 redis的线程模型(重要)
- 2.3 为啥redis单线程模型也能效率这么高?
- 3. redis都有哪些数据类型?分别在哪些场景下使用比较合适呢?
- 4. redis的过期策略能介绍一下?要不你再手写一个LRU?
- 5. redis设置过期时间后,是怎么删除掉的?为什么有时候明明应该有大批数据过期了,占用内存却没减少?
- 6. redis 如何通过读写分离来承载读请求QPS超过10万+ ?
- 7. redis replication以及master持久化对主从架构的安全意义?
- 8. redis主从复制原理、断点续传、无磁盘化复制、过期key处理
- 9. redis主从模式下如何保证高可用性?
- 10. 为什么redis哨兵集群只有2个节点无法正常工作?
- 11. redis哨兵主备切换的数据丢失问题:异步复制、集群脑裂
- 12. redis哨兵的多个核心底层原理的深入解析(包含slave选举算法)
- 13. 总结:怎么保证redis是高并发以及高可用的?
- 14. redis如何在保持读写分离+高可用的架构下,还能横向扩容支撑1T+海量数据
- 15. 数据分布算法:hash+一致性hash+redis cluster的hash slot
- 16. redis cluster的核心原理分析:gossip通信、jedis smart定位、主备切换
- 17. 你能说说我们一般如何应对缓存雪崩以及穿透问题吗?
- 18. 高并发场景下的缓存+数据库双写不一致问题分析与解决方案设计
- 19. 你能说说redis的并发竞争问题该如何解决吗?
1. 在项目中缓存是如何使用的?缓存如果使用不当会有什么后果?
用缓存,主要是俩用途,高性能和高并发。
-
缓存是如何实现高性能的
如上图,如果对一个数据有1000次查询,假设一次对数据库的查询耗时800毫秒,不使用缓存的情况下每次都要耗时800ms,用户会感觉比较卡,而使用了缓存,在第一查出来后写入缓存,后面就直接从缓存中读取,除了第一次请求耗时800ms,后面999次都只耗时10ms,加快了性能和响应速度。 -
缓存是如何实现高并发的
常见的缓存问题有下面几个,前三个比较常见
- 缓存与数据库双写不一致
- 缓存雪崩
- 缓存穿透
- 缓存并发竞争
2. redis和memcached有什么区别?redis的线程模型是什么?为什么单线程的redis比多线程的memcached效率要高得多?(为什么redis是单线程的但是还可以支撑高并发?)
2.1 redis和memcached有什么区别
1)Redis相比Memcached来说,拥有更多的数据结构并支持更丰富的数据操作。
2)redis是多线程,memcached是多线程,但是由于是内存的还有IO多路复用,redis也很快。
3)memcached不支持集群(没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据);但是reids目前是原生支持cluster模式的。
2.2 redis的线程模型(重要)
2.3 为啥redis单线程模型也能效率这么高?
1)纯内存操作
2)核心是基于非阻塞的IO多路复用机制
3)单线程反而避免了多线程的频繁上下文切换问题
3. redis都有哪些数据类型?分别在哪些场景下使用比较合适呢?
string、hash、list、set、zset
string就是最基本的set、get的kv缓存
hash是类似map的一种结构,一般就是可以将结构化的数据,比如一个对象给缓存到redis里,然后每次读写缓存的时候,可以就操作hash里的某个字段。
结构如下
key=120
value={"id": 120,"name": "zhangsan","age": 20
}
list是有序列表,使用场景可以为简单的消息队列,通过lpush、rpop之类的进行生产消费消息,也可以用来做一些像微博粉丝的功能
key=weiboid
value=[fensi1,fensi2....]
set是无序集合,有自动去重的功能,当然我们也可以在jvm的内存中使用hashset去重,可是如果是分布式的数据存在于多台机器的,就可以通过redis的set来进行全局去重。
set还可以实现交集、并集、差集的操作,比如交集,可以实现共同粉丝、共同关注、共同好友等功能。
sorted set,排序的set,去重但是可以排序,写进去的时候给一个分数,自动分局分数排序,这个可以玩很多的花样,最大的特色是有个分数可以自定义排序规则,比如想要根据时间对数据排序,那么可以写入进去的时候用某个时间作为分数,人家自动给我们按照时间排序了
可以实现排行榜功能:将每个用户以及对应的什么分数写入进去,zadd board score username,接着zrevrange board 0 99,就可以获取排名前100的用户,zrank board username,可以看到用户在排行榜里的排名。
4. redis的过期策略能介绍一下?要不你再手写一个LRU?
首先要搞清楚,redis是缓存,不是存储,因为内存是有限且宝贵的,磁盘才是廉价且大量的,如果我们redis内存就10G,可是往里面写了20G的数据,自然就要淘汰10G数据。那要干掉哪些数据保留哪些数据呢?当然是干掉不常用的数据,保留常用的数据了。
所以说,缓存是一个最基本的概念,数据是会过期的,要么是我们自己设置过期时间,要么是redis自己给干掉。
redis的过期策略一种是可以设置过期时间,还有一种是内存淘汰机制。
过期时间都知道,下面的问题也有介绍,这里主要介绍下内存淘汰机制。
如果redis的内存占用过多的时候,此时会进行内存淘汰,有如下一些策略:
1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错,这个一般不会使用。。。
2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这是最常用的)
3)allkeys-random:当内存不足以容纳新写入数据时,在键空间,随机移除某个key,这个一般也不会使用。
4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适)
5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key
6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除
手写一个LRU算法(leetcode146)
哈希表+双向链表法
想法:这个问题可以用哈希表,辅以双向链表记录键值对的信息。所以可以在 O(1)O(1) 时间内完成 put 和 get 操作,同时也支持 O(1)O(1) 删除第一个添加的节点。
使用双向链表的一个好处是不需要额外信息删除一个节点,同时可以在常数时间内从头部或尾部插入删除节点。
一个需要注意的是,在双向链表实现中,这里使用一个伪头部和伪尾部标记界限,这样在更新的时候就不需要检查是否是 null 节点。
import java.util.Hashtable;
public class LRUCache {class DLinkedNode {int key;int value;DLinkedNode prev;DLinkedNode next;}private void addNode(DLinkedNode node) {/*** Always add the new node right after head.*/node.prev = head;node.next = head.next;head.next.prev = node;head.next = node;}private void removeNode(DLinkedNode node){/*** Remove an existing node from the linked list.*/DLinkedNode prev = node.prev;DLinkedNode next = node.next;prev.next = next;next.prev = prev;}private void moveToHead(DLinkedNode node){/*** Move certain node in between to the head.*/removeNode(node);addNode(node);}private DLinkedNode popTail() {/*** Pop the current tail.*/DLinkedNode res = tail.prev;removeNode(res);return res;}private Hashtable<Integer, DLinkedNode> cache =new Hashtable<Integer, DLinkedNode>();private int size;private int capacity;private DLinkedNode head, tail;public LRUCache(int capacity) {this.size = 0;this.capacity = capacity;head = new DLinkedNode();// head.prev = null;tail = new DLinkedNode();// tail.next = null;head.next = tail;tail.prev = head;}public int get(int key) {DLinkedNode node = cache.get(key);if (node == null) return -1;// move the accessed node to the head;moveToHead(node);return node.value;}public void put(int key, int value) {DLinkedNode node = cache.get(key);if(node == null) {DLinkedNode newNode = new DLinkedNode();newNode.key = key;newNode.value = value;cache.put(key, newNode);addNode(newNode);++size;if(size > capacity) {// pop the tailDLinkedNode tail = popTail();cache.remove(tail.key);--size;}} else {// update the value.node.value = value;moveToHead(node);}}
}/*** LRUCache 对象会以如下语句构造和调用:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/
复杂度分析
- 时间复杂度:对于 put 和 get 都是 O(1)O(1)。
- 空间复杂度:O(capacity)O(capacity),因为哈希表和双向链表最多存储 capacity + 1 个元素。
5. redis设置过期时间后,是怎么删除掉的?为什么有时候明明应该有大批数据过期了,占用内存却没减少?
redis删除过期数据是采用定期删除+惰性删除
的。
所谓定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。假设redis里放了10万个key,都设置了过期时间,你每隔几百毫秒,就检查10万个key,那redis基本就死了,cpu负载会非常高,消耗在检查过期key了。注意,这里可不是每隔100ms就遍历所有的设置过期时间的key,那样就是一场性能上的灾难。实际上redis是每隔100ms随机抽取一些key来检查和删除的。
但是问题是,定期删除可能会导致很多过期key到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。
所以说有的时候明明应该很多数据过期了,redis占用的内存却没变,那是因为没被删除,要等待再查询这个key的时候,才会懒惰的检查再删除。
通过上面的定期+惰性两种手段,就可以保证过期的key一定会被干掉。
但是实际上这还是有问题的,如果定期删除漏掉了很多过期的key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了怎么办?
答案是:走内存淘汰机制。(上一个问题里的)
6. redis 如何通过读写分离来承载读请求QPS超过10万+ ?
redis单机的瓶颈:
redis通过主从读写分离支撑10万+的高并发:
redis单机的可以承受几万的QPS,可是如果更高并发的话,如要支撑超过10万+的并发量,单机是不够的。
可以通过读写分离来实现,一般来说,对缓存,一般都是用来支撑读高并发的,写的请求是比较少的,可能写请求也就一秒几千,大量的请求都是读,一秒钟几十万次读。
可以通过读写分离:主从架构 -> 读写分离 -> 支撑10万+读qps的架构。
7. redis replication以及master持久化对主从架构的安全意义?
1、 redis replication的核心机制
- 1)redis采用异步方式复制数据到slave节点,不过redis 2.8 开始,slave node会周期性地确认自己每次复制的数据量
- 2)一个master node是可以配置多个slave node的
- 3)slave node也可以连接其他的slave node
- 4)slave node做复制的时候,是不会block master node的正常工作的
- 5)slave node在做复制的时候,也不会block对自己的查询操作,它会用旧的数据集来提供服务;但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了
- 6)slave node主要用来进行横向扩容,做读写分离,扩容的slave node可以提高读的吞吐量
slave,高可用性,有很大的关系
2、master持久化对于主从结构的安全保障的意义
如果采用了主从架构,那么建议必须开启master node的持久化!
不建议用slave node作为master node的数据热备,因为那样的话,如果你关掉master的持久化,可能在master宕机重启的时候数据是空的,然后可能一经过复制,slave node数据也丢了
master -> RDB和AOF都关闭了 -> 全部在内存中
master宕机,重启,是没有本地数据可以恢复的,然后就会直接认为数据是空的
master就会将空的数据集同步到slave上去,所有slave的数据全部清空
100%数据丢失
master节点,必须要使用持久化机制
第二个,master的各种备份方案,要不要做,万一说本地的所有文件丢失了;从备份中挑选一份rdb去恢复master;这样才能确保master启动的时候,是有数据的
即使采用了后续讲解的高可用机制,slave node可以自动接管master node,但是也可能sentinel还没有检测到master failure,master node就自动重启了,还是可能导致上面的所有slave node数据清空故障。
8. redis主从复制原理、断点续传、无磁盘化复制、过期key处理
9. redis主从模式下如何保证高可用性?
-
redis的不可用
-
redis基于哨兵的高可用性
哨兵:
哨兵(sentinel)是redis集群架构中非常重要的一个组件,主要功能如下:
(1)集群监控,负责监控redis master和slave进程是否正常工作
(2)消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
(3)故障转移,如果master node挂掉了,会自动转移到slave node上
(4)配置中心,如果故障转移发生了,通知client客户端新的master地址
哨兵本身也是分布式的,作为一个哨兵集群去运行,互相协同工作
(1)故障转移时,判断一个master node是宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题
(2)即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了
哨兵的核心知识
(1)哨兵至少需要3个实例,来保证自己的健壮性
(2)哨兵+redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性
(3)对于哨兵+redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都惊醒充足的测试和演练
10. 为什么redis哨兵集群只有2个节点无法正常工作?
主要是因为majority机制
11. redis哨兵主备切换的数据丢失问题:异步复制、集群脑裂
两种数据丢失的情况:
主备切换的过程,可能会导致数据丢失
(1)异步复制导致的数据丢失
因为master -> slave的赋值是异步的,所以可能有部分数据还没复制到slave,master就宕机了,此时这些数据就丢失了
(2)脑裂导致的数据丢失
脑裂,指的是,某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着,此时哨兵可能就会认为master宕机了,然后开启选举,将其他slave切换成了master。这个时候,集群里就会有两个master,也就是所谓的脑裂。此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续写向旧master的数据可能也丢失了,因此旧master再次恢复的时候,也被作为一个slave挂到新的master 上去,自己的数据会清空,重新从新的master复制数据。
解决异步复制和脑裂导致的数据丢失
min-slaves-to-write 1
min-slaves-max-lag 10
要求至少有1个slave,数据复制和同步的延迟不能超过10秒
如果说一旦所有的slave,数据复制和同步的延迟都超过了10秒,那么这个时候,master就不会再接受任何请求了
上面两个配置可以减少异步复制和脑裂导致的数据丢失
(1)减少异步复制的数据丢失
有了min-slave-max-lag这个配置,就可以确保说,一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低在可控范围内
(2)减少脑裂的数据丢失
如果一个master出现了脑裂,跟其他slave丢了连接,那么上面两个配置可以确保说,如果不能继续给指定数量的slave发送数据,而且slave超过10秒没有给自己ack消息,那么就直接拒绝客户端的写请求,这样脑裂后的旧master就不会接受client的新数据,也就避免了数据丢失
12. redis哨兵的多个核心底层原理的深入解析(包含slave选举算法)
13. 总结:怎么保证redis是高并发以及高可用的?
14. redis如何在保持读写分离+高可用的架构下,还能横向扩容支撑1T+海量数据
- redis单master结构的容量瓶颈问题
- redis如何通过master横向扩容支撑1T+数据量
解决方案:redis cluster。
redis的集群架构
redis cluster,可以支撑N个redis master node,每个master node都可以挂载多个slave node。
读写分离的架构,对于每个master来说,写就写到master,然后读就从master对应的slave去读。
高可用,因为每个master都有slave节点,那么如果master挂掉,redis cluster这套机制,就会自动将某个slave切换成master。
redis cluster = (多master + 读写分离 + 高可用)
我们只要基于redis cluster去搭建redis集群即可,不需要手工去搭建replication复制+主从结构+读写分离+哨兵集群+高可用。
redis cluster vs replication + sentinel
如果你的数据量很少,主要是承载高并发高性能的场景,比如你的缓存一般就几个G,单机足够了
replication,一个master,多个slave,要几个slave跟要求的读吞吐量有关,然后搭建一个sentinel集群,去保证redis主从架构的高可用性,就可以了。
redis cluster,主要是针对海量数据+高并发+高可用的场景,海量数据,如果你的数据量很大,那么建议就用redis cluster。
15. 数据分布算法:hash+一致性hash+redis cluster的hash slot
- hash算法及弊端
- 一致性hash算法及弊端
- 一致性哈希算法的虚拟节点实现负载均衡
- hash slot算法
16. redis cluster的核心原理分析:gossip通信、jedis smart定位、主备切换
高可用和主备切换原理
17. 你能说说我们一般如何应对缓存雪崩以及穿透问题吗?
-
缓存雪崩现象
-
如何解决缓存雪崩(事前事中事后)
- 事前:redis高可用,主从 + 哨兵,redis cluster,避免全盘崩溃
- 事中:本地ehcache缓存 + hystrix 限流&降级,避免 MySQL 被打死
- 事后:redis持久化,快速回复缓存数据
-
缓存穿透现象及解决方案
还有布隆过滤器??????
18. 高并发场景下的缓存+数据库双写不一致问题分析与解决方案设计
问题出现场景
问题就是在并发访问中,不论是先写库,再删除缓存;还是先删缓存,再写库,都有可能出现数据不一致的情况
1、在并发中是无法保证读写的先后顺序的,如果删掉了缓存还没来得及写库,另一个线程就过来读取发现缓存为空就去数据库读取并写入缓存,此时缓存中为脏数据。
2、如果先写了库,再删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会出现数据不一致情况。
3、如果是redis集群,或者主从模式,写主读从,由于redis复制存在一定的时间延迟,也有可能导致数据不一致。
优化解决方案
双删 + 超时
在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。这样最差的情况是在超时时间内存在不一致,当然这种情况极其少见,可能的原因就是服务宕机。此种情况可以满足绝大多数需求。
当然这种策略要考虑redis和数据库主从同步的耗时,所以在第二次删除前最好休眠一定时间,比如500毫秒,这样毫无疑问又增加了写请求的耗时
通过读取binlog的方式,异步淘汰缓存
好处:业务代码侵入性低,将缓存与数据库不一致的时间尽可能缩小。
binlog介绍:Mysql的binlog日志作用是用来记录mysql内部增删改查等对mysql数据库有更新的内容的记录(对数据库的改动),对数据库的查询select或show等不会被binlog日志记录;主要用于数据库的主从复制以及增量恢复。(binlog也可以设置日志过期时间)
binlog日志模式
Mysql复制主要有三种方式:基于SQL语句的复制(statement-based replication, SBR),基于行的复制(row-based replication, RBR),混合模式复制(mixed-based replication, MBR)。对应的,binlog的格式也有三种:STATEMENT,ROW,MIXED。1、STATEMENT模式(SBR)
每一条会修改数据的sql语句会记录到binlog中。优点是并不需要记录每一条sql语句和每一行的数据变化,减少了binlog日志量,节约IO,提高性能。缺点是在某些情况下会导致master-slave中的数据不一致(如sleep()函数, last_insert_id(),以及user-defined functions(udf)等会出现问题)2、ROW模式(RBR)
不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了,修改成什么样了。而且不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题。缺点是会产生大量的日志,尤其是alter table的时候会让日志暴涨。3、MIXED模式(MBR)
以上两种模式的混合使用,一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog,MySQL会根据执行的SQL语句选择日志保存方式。
此题答案原博:https://blog.csdn.net/Healist/article/details/79341557
19. 你能说说redis的并发竞争问题该如何解决吗?
可以使用分布式锁(zookeeper或redis自带的两种方式),确保同一时间,只能有一个系统实例在操作某个key,别人都不允许读和写。
每次要写之前,先判断一下当前这个value的时间戳是否比缓存里的value的时间戳要更新,如果更新,那么可以写,如果更旧,就不能用旧的数据覆盖新的数据。