您的位置: 首页 - 站长

ps海报素材网站wordpress 路由器

当前位置: 首页 > news >正文

ps海报素材网站,wordpress 路由器,永久免费企业网站建设,濮阳市做网站说在前面 在40岁老架构师尼恩的#xff08;50#xff09;读者社区中#xff0c;经常有小伙伴#xff0c;需要面试美团、京东、阿里、 百度、头条等大厂。 下面是一个5年小伙伴成功拿到通过了京东一面面试#xff0c;并且最终拿到offer#xff0c;月薪40K。 现在把面试…说在前面 在40岁老架构师尼恩的50读者社区中经常有小伙伴需要面试美团、京东、阿里、 百度、头条等大厂。 下面是一个5年小伙伴成功拿到通过了京东一面面试并且最终拿到offer月薪40K。 现在把面试真题和参考答案收入咱们的宝典大家看看收个优质Offer需要学点啥 当然对于中高级开发来说这些面试题也有参考意义。 小伙伴说光代码漂亮不够 面试还得会吹。 这里把题目以及小伙伴的吹牛逼的方式方法经过整理和梳理之后收入咱们的《尼恩Java面试宝典》 V95版本供后面的小伙伴参考提升大家的 3高 架构、设计、开发、吹牛水平。 《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF请到公号【技术自由圈】取 文章目录 说在前面京东一面1、说一说 JVM内存模型JVM内存结构Java内存模型图 2、CMS和G1有什么区别? 什么时候发生垃圾回收 然后说说大致的回收过程?完整的GC流程什么时候发生垃圾回收对象存活判断方法垃圾回收算法 3、对于数据的一致性是怎么保证的4、Redis集群有没有了解过主从和选举是怎样的一、Redis高可用集群架构二、主从数据同步三、选主机制四、过期内存淘汰策略 5、看你们公司使用的是MySQL你们使用的是哪种存储引擎为什么MyISAM和InnoDB的区别6、mysql索引的底层数据结构是什么为什么选择这种数据结构7、说说什么情况下索引失效8、手写代码设计一个分布式自增id生成服务9、有没有了解过网络安全问题常见的网络攻击有哪些原理是什么可以怎么解决一、XSS 跨站脚本攻击二、CSRF 跨站请求伪造三、DDoS 分布式拒绝服务攻击四、SQL 注入 10、平时在开发接口或者设计项目的时候如何保证安全性的11、使用Redis集群时可能会存在什么问题一、数据一致性问题二、性能问题三、可用性问题四、负载均衡问题五、网络延迟问题六、节点故障问题七、安全问题 12、有没有了解过cap和base原则一、CAP理论二、BASE 理论 13、zk是如何保证一致性的一ZAB协议Zookeeper原子消息广播协议二、选主三、选主后的数据同步四、事务操作 14、你如何设计一个能抗住大流量的系统说说设计方案15、有没有了解过缓存策略有哪些一、Cache Aside旁路缓存策略二、Read/Write Through读穿 / 写穿策略三、Write Back写回策略总结 参考文献尼恩说在最后 京东一面 1、说一说 JVM内存模型 小伙伴从以下2个维度去 “吹牛” 吹到 面试官 “口水直流不能自已” 第一个维度JVM内存结构第二个维度Java内存模型图 JVM内存结构 程序计数器当前线程所执行的字节码的行号指示器用于记录正在执行的虚拟机字节指令地址线程私有。 Java虚拟栈存放基本数据类型、对象的引用、方法出口等线程私有。 Native方法栈和虚拟栈相似只不过它服务于Native方法线程私有。 Java堆java内存最大的一块所有对象实例、数组都存放在java堆GC回收的地方线程共享。 方法区存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据等。即永久带回收目标主要是常量池的回收和类型的卸载各线程共享 Java内存模型图 Java内存模型规定了所有的变量都存储在主内存中每条线程还有自己的工作内存线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝线程对变量的所有操作都必须在工作内存中进行而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。 尼恩说明 由于篇幅限制这里对JVM内存结构、Java内存模式的介绍没有做展开 有关JVM内存结构、Java内存模式的详细介绍可以参考《尼恩Java面试宝典》中JVM面试专题的内存模型部分 2、CMS和G1有什么区别? 什么时候发生垃圾回收 然后说说大致的回收过程? 小伙伴从以下6个维度去 “吹牛” 吹到 面试官 “口水直流不能自已” 首先说说垃圾回收的本质其次CMS和G1有什么区别再次完整的GC流程再次 什么时候发生垃圾回收再次对象存活判断方法最后垃圾回收算法 垃圾回收的本质 垃圾回收是Java程序执行自动内存管理的过程。当Java程序在JVM上运行时将在堆上创建对象这是专用于该程序的内存的一部分。最终将不再需要某些对象。垃圾收集器找到这些未使用的对象并将其删除以释放内存。 CMS和G1有什么区别 CMS(Concurrent Mark Sweep)收集器标记-清除算法 老年代并行收集器以获取最短回收停顿时间为目标的收集器具有高并发、低停顿的特点追求最短GC回收停顿时间。 G1(Garbage First)收集器标记-整理算法 Java堆并行收集器G1收集器是JDK1.7提供的一个新收集器G1收集器基于“标记-整理”算法实现也就是说不会产生内存碎片。此外G1收集器不同于之前的收集器的一个重要特点是G1回收的范围是整个Java堆(包括新生代老年代)而前六种收集器回收的范围仅限于新生代或老年代。 区别 CMS收集器是老年代的收集器可以配合新生代的Serial和ParNew收集器一起使用G1收集器收集范围是老年代和新生代不需要结合其他收集器使用CMS收集器以最小的停顿时间为目标的收集器G1收集器可预测垃圾回收的停顿时间CMS收集器是使用“标记-清除”算法进行的垃圾回收容易产生内存碎片G1收集器使用的是“标记-整理”算法进行了空间整合降低了内存空间碎片。 完整的GC流程 在JVM中一次完整的GC流程如下 标记垃圾对象清除垃圾对象将存活的对象复制到另一个区域清空原区域。 JVM中的垃圾回收分为三个阶段Minor GC、Full GC和G1 GC。 Minor GC是针对新生代的垃圾回收器主要清理Eden区和Survivor区的垃圾Full GC是对整个堆空间进行垃圾回收包括新生代和老年代G1 GC是一种基于Region的垃圾回收器它将堆空间划分为不同的Region,每个Region都有一个根节点当某个Region的大小达到一定阈值时就会触发一次GC。 什么时候发生垃圾回收 当对象对当前使用这个对象的应用程序变得不可触及的时候这个对象就可以被回收了。 垃圾回收不会发生在永久代如果永久代满了或者是超过了临界值会触发完全垃圾回收(Full GC)。 如果你仔细查看垃圾收集器的输出信息就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。 对象存活判断方法 Java中判断对象存活的方法有以下两种算法: 引用计数算法给对象添加一个引用计数器每当有一个地方引用它时计数器值就1;当引用失效时计数器值就-1;任何时刻计数器为0的对象就是不可能再被使用的对象。这种算法虽然简单但是有个致命的缺点就是不能适用于相互引用的情况。可达性分析算法通过从根对象开始向下搜索直到找到一个无法直接或间接访问到的对象为止。这个过程称为“可达性分析”如果一个对象不可达则说明它已经不再使用可以被回收。 垃圾回收算法 Java中常见的三种垃圾收集算法是标记-清除算法(Mark-Sweep)、复制算法(Copying)和分代收集算法(Generational Collection)。 标记-清除算法这种算法首先会标记出所有活动对象然后将它们从内存中移除。接下来算法会遍历整个堆空间将未被标记的对象进行回收。这种算法的缺点是会产生内存碎片。复制算法这种算法将堆空间分为两个区域Eden区和Survivor区。新创建的对象首先分配到Eden区中然后经过多次复制和清除后才会被移动到Survivor区中。当Survivor区空间不足时就会触发一次Full GC来进行垃圾回收。这种算法的优点是可以避免内存碎片。分代收集算法这种算法将堆空间分为三个区域新生代、老年代和永久代。新生代又分为Eden区、Survivor区和From区。这种算法的优点是可以更好地利用CPU缓存提高程序运行效率。 3、对于数据的一致性是怎么保证的 小伙伴说他是参考《尼恩Java面试宝典》中 Redis 专题中的数据一致性问题及方案去吹的 面试官非常满意 4、Redis集群有没有了解过主从和选举是怎样的 小伙伴从以下7个维度去 “吹牛” 吹到 面试官 “口水直流不能自已” 首先说说Redis高可用集群架构 两个小的维度一主从哨兵模式两个小的维度二Cluster集群模式 再次说说两种主从数据同步方式 两个小的维度一全量复制两个小的维度二增量复制 第三说说说说两种选主机制 两个小的维度一主从哨兵模式选主两个小的维度二Cluster集群模式选主 最后嘚瑟一下过期内存淘汰策略 一、Redis高可用集群架构 Redis高可用集群有两种分别是主从哨兵模式和集群模式 1.主从哨兵模式 其中一台服务器作为master服务器提供读写服务配置多台从服务器从服务器只提供只读服务同时配置多台sentinel也即是哨兵哨兵的作用是可以监控master节点如果master宕机可以从从服务器中选举出一台作为master服务器。 哨兵模式客户端连接哨兵集群即可获得master服务器的信息。此时客户端并不会做读写分离也就是所有读写都由master服务器处理这里相当于从服务器只作为主服务器的数据备份。如果master发生故障切换到其他从服务器哨兵会把新的master服务器地址告知客户端。 jedis和RedisTemplate都没有实现读写分离。如果需要可以分别建立master服务器连接池和slave服务器连接池并严格区分读写操作路由到需要使用的连接池。需要注意的是Redis主从复制是异步的可能存在小概率数据不一致的问题。 2.Cluster集群模式 在主从哨兵模式所有的写操作都是由master处理这在性能上可能会出现瓶颈。Redis3.0后推出了集群模式可以实现水平扩展配置多台的master服务器处理读写请求。 集群模式下看似于将一个大的主从架构拆分成多个主从架构的服务器群具有复制高可用和分片的特性。不需要哨兵也可以实现节点故障移除和master选举功能。性能和高可用性均优于哨兵模式但需要更多的服务器。可以从公司业务的并发量和成本等角度考量选择哪种模式。 Redis Cluster集群模式默认将所有的数据划分为16384个slot槽每个master节点均匀负责一部的槽位。 通常会对key值使用crc16算法进行hash得到一个整数值然后使用这个整数值对16384进行取模来得到具体的槽位。 Cluster集群模式下Redis默认从服务是不分担读请求只作为备注和故障转移。但有读请求到达从服务器会重定向到主服务器处理。 二、主从数据同步 Redis主从数据同步大致分为两种全量复制和增量复制。 全量复制 增量复制 一般情况下主从断开连接后会进行全量复制但Redis2.8后开始支持部分数据的复制。 master和从服务器第一次连接时会进行全量复制同时master和所有的slave都会维护一个复制数据的偏移量offset和master的进程id。 如果从服务器断开重连后会比较偏移量是否太旧或者master进程id是否变更了如果这样则会进行一次全量复制否则会进行部分复制把offset之后的数据同步给从服务器。 三、选主机制 1.主从哨兵模式 这种模式下是有哨兵监控master服务器状态并实现故障转移。一旦master服务器宕机则哨兵会从剩下的从服务器中选举一条作为新的master节点。这里有几个概念: 主观下线: 哨兵会定期向主服务器发送心跳包检测是否正常如果超过配置文件中sentinel down-after-milliseconds mymaster 配置的时间没有收到主服务器的回复则这个哨兵认为主服务下线。 客观下线: 一个哨兵把master记为主观下线并不代表master就一定下线了此时要向其他哨兵确认master是否真的下线如果超过sentinel monitor mymaster 配置的数量一般为哨兵数量/2 1哨兵认为master下线则记为客观下线。 哨兵选举master服务器过程 先从哨兵中选举出一个leader并由这个leader选举出新的master过滤故障节点从剩余的节点中按照下列规则选出master优先选择slave-priority最大的从节点作为主节点其次选择数据偏移量最大的节点选择runid最小的从节点 2.集群模式 slave发现自己的master下线后会广播故障转移信息到其他master节点master接收到slave故障转移请求后首先会检测请求的合法性然后发送响应ack给slave,每轮投票master只会响应一次一旦一个slave接收超过半数master的ack后则被选中成为新的master否则会进行下一轮的投票 四、过期内存淘汰策略 被动删除客户端get 请求某个key时会判断是否过期如果过期了则会清楚主动删除redis定期扫描一批key,检查是否过期如果过期则清楚内存淘汰策略当redis内存不足以容纳更多的key时则会触发内存淘汰策略可以在配置文件配置。常用有lru(最久没访问)lfu(访问频率最低) random(随机)同时可以配置针对所有的key,还是设置了过期时间的key执行淘汰。 5、看你们公司使用的是MySQL你们使用的是哪种存储引擎为什么MyISAM和InnoDB的区别 小伙伴说他是参考《尼恩Java面试宝典》中Mysql 面试专题中的Mysql原理、MyISAM和InnoDB的对比去吹的 6、mysql索引的底层数据结构是什么为什么选择这种数据结构 小伙伴从以下6个维度去 “吹牛” 吹到 面试官 “口水直流不能自已” 首先索引的底层数据结构其次时间复杂度对比再次为什么索引采用B树而不采用Hash这种结构再次为什么不用二叉树再次为什么不用红黑二叉树最后为什么没有采用B树或则说B-树 索引的底层数据结构有 树Tree准确的说是B树Hash 时间复杂度对比 Hash的时间O(1) Tree的O(logn) 为什么索引采用B树而不采用Hash这种结构 Hash这种结构对与获取单条记录时的查询效率是要比B树效率要高的但是对与数据的范围查找效率就很低特别是对于数据量大的时候10万 甚至百万级别的数据。 为什么低它是根据字段值算出获取一个hash值然后根据hash值找到在索引表中查找出hash值对应的数据行它的索引没有按顺序存储 B树是一种多路平衡树一个横向存放多个节点并且它的非叶子节点都只存放指向子节点的索引指针不存放数据。所有非叶子节点都存放一个节点在叶子节点上并且这个叶子节点是一个有序的列表结构。 当我们查一个范围数据时通过B树搜索 多路一课树每个节点最多可以拥有大于2个子节点的树 平衡树 一颗树左子树和右子树保持相对平衡不会出现一边层级特别多一边层级特别少的情况 为什么不用二叉树 二叉树为非平衡树如果是有序递变的数据很有可能就会变成左边树层级过高或则右边树的层级过高甚至是退化成链表。 层级过高为什么就不能用了呢 假设是1,2,3,4,5,6这样的数据如果我们要找到底部6这个数据就要进行6次IO加载磁盘数据进内存索引是在磁盘文件中以表的形式存在的才能找到。如果按一次IO10ms计算60ms也不算什么。但是假设是100万呢1001000010ms也就是10000s换算成分钟也就是167所以这种结构肯定是不行的。 为什么不用红黑二叉树 红黑二叉树一种平衡树虽然它不会出现有一树的层级过高的情况但是它还是没有从根本上解决树的层级问题随着数据量增大树的层级会越来越高。要遍历叶子节点的数据IO消耗还是过高。 为什么没有采用B树或则说B-树 B树虽然虽然通过多路平衡树解决了树的高度问题但是它对与访问数据的查找效率还是低下的对于数据访问在非叶子节点和叶子节点都有的范围它不仅非叶子节点的遍历还需要做叶子节点的遍历。 总体来说选择B树是因为它具有以下优点 查询效率高B树的搜索性能非常接近二叉树而且它的查询效率更高因为它的每个节点都包含很多关键字可以支持范围查询。空间利用率高B树的每个节点都包含很多关键字所以它的空间利用率更高可以支持更多的索引。适用于外部存储B树的节点之间的距离比较大所以它更适用于外部存储可以减少内存开销。 因此B树是一种非常适合用于MySQL索引的底层数据结构它可以提供高效、稳定、可靠的查询性能。 7、说说什么情况下索引失效 小伙伴说他是参考《尼恩Java面试宝典》中Mysql 面试专题中的索引失效面试题去吹的 面试官非常满意 8、手写代码设计一个分布式自增id生成服务 设计一个分布式自增id生成服务的步骤如下 选择合适的分布式ID生成算法例如Snowflake、Apache Skywalking等。设计分布式ID生成服务的架构包括节点的部署、负载均衡、数据同步等。实现分布式ID生成服务的代码包括生成ID的算法、节点的连接和数据同步等。部署分布式ID生成服务并进行测试和性能优化。 下面是一个简单的Java实现 public class DistributedIdGenerator {private static final int PARTITION_ID 1; // 分区IDprivate static final int NODE_ID 1; // 节点IDprivate static final int SEQUENCE 1; // 序列号private static final long SEQUENCE_ROOT 1L 32; // 序列号根节点private static final long MAX_ID SEQUENCE_ROOT 1 - 1L 32; // 最大IDprivate static final long TIMESTAMP System.currentTimeMillis() / 1000; // 时间戳private static final long MACHINE_ID UUID.randomUUID().getMostSignificantBits(); // 机器IDprivate static final int SCALE 10; // 位数private static final int SHIFT 22; // 位移private static final int PARTITION_SIZE 1 SCALE; // 分区大小private static final DistributedIdGenerator instance new DistributedIdGenerator();private static int sequence 0; // 当前节点的序列号private static long lastId SEQUENCE_ROOT; // 上一次生成的IDprivate static MapInteger, Long partitionMap new HashMap(); // 分区ID和最大ID的映射private DistributedIdGenerator() {// 初始化分区ID和节点IDpartitionMap.put(PARTITION_ID, SEQUENCE_ROOT);partitionMap.put(NODE_ID, SEQUENCE_ROOT);}public static synchronized long generateId() {// 获取当前节点的序列号sequence (sequence 1) SEQUENCE_ROOT;if (sequence 0) {// 如果序列号溢出则从上一次生成的ID开始sequence partitionMap.get(PARTITION_ID);if (sequence SEQUENCE_ROOT) {// 如果分区ID溢出则从最大ID开始sequence partitionMap.get(NODE_ID);if (sequence SEQUENCE_ROOT) {// 如果节点ID溢出则从最大ID开始sequence SEQUENCE_ROOT;}}}// 获取当前时间的毫秒数long now System.currentTimeMillis() / 1000;// 计算IDlong id ((now - TIMESTAMP) SHIFT) | (MACHINE_ID SCALE) | (sequence SEQUENCE_SHIFT) | lastId;// 如果ID溢出则从最大ID开始if (id MAX_ID) {id SEQUENCE_ROOT;}// 更新上一次生成的IDlastId id;// 更新分区ID和节点ID的映射partitionMap.put(PARTITION_ID, partitionMap.get(PARTITION_ID) PARTITION_SIZE);partitionMap.put(NODE_ID, partitionMap.get(NODE_ID) PARTITION_SIZE);// 返回IDreturn id;} }这个实现使用了Snowflake算法通过获取当前时间的毫秒数、机器ID和序列号来生成ID。在生成ID时首先获取当前节点的序列号如果序列号溢出则从上一次生成的ID开始。如果分区ID和节点ID都溢出则从最大ID开始。最后计算出ID并更新上一次生成的ID和分区ID和节点ID的映射。 9、有没有了解过网络安全问题常见的网络攻击有哪些原理是什么可以怎么解决 小伙伴从以下4个维度去 “吹牛” 吹到 面试官 “口水直流不能自已” 首先XSS 跨站脚本攻击其次CSRF 跨站请求伪造再次DDoS 分布式拒绝服务攻击再次SQL 注入 一、XSS 跨站脚本攻击 XSS 攻击是指攻击者通过在受信任的网页上注入恶意脚本使得脚本在用户浏览器中执行从而窃取用户敏感信息、劫持会话或执行其他恶意行为。XSS 攻击的原理是利用网页中的漏洞将攻击者的恶意脚本注入到网页中然后当用户浏览该网页时浏览器会执行该恶意脚本。 解决 XSS 攻击的方法包括 输入验证对用户输入的数据进行有效性验证过滤掉无效或恶意的输入。输出过滤在输出用户输入数据到网页上之前对数据进行过滤去掉其中的恶意脚本代码。安全编码使用安全的编码方式避免在网页中嵌入恶意脚本。浏览器安全设置设置浏览器的安全选项禁用或限制脚本执行阻止 XSS 攻击。 二、CSRF 跨站请求伪造 CSRF 攻击是指攻击者通过伪造用户的请求向网站服务器发送恶意请求从而操纵用户账户或执行其他恶意行为。CSRF 攻击的原理是利用网站应用程序中的漏洞绕过网站的安全验证机制以用户的身份执行恶意请求。 解决 CSRF 攻击的方法包括 添加 token在请求中添加一个随机生成的 token服务器验证该 token 是否合法以判断请求是否来自合法用户。采用 POST 方法使用 POST 方法提交表单因为 POST 方法不能被缓存从而避免 CSRF 攻击。限制 HTTP 方法对网站应用程序进行安全配置限制允许使用的 HTTP 方法禁止使用 GET、POST 等可被伪造的方法。 三、DDoS 分布式拒绝服务攻击 DDoS 攻击是指攻击者通过控制大量僵尸主机向目标主机发送海量流量从而瘫痪目标主机或使其服务不可用。DDoS 攻击的原理是通过大量流量消耗目标主机的带宽和资源使其无法正常提供服务。 解决 DDoS 攻击的方法包括 流量清洗使用流量清洗设备对入侵流量进行过滤和清洗阻断攻击流量。负载均衡使用负载均衡设备将攻击流量分发到多个服务器上减轻目标服务器的压力。扩大带宽增加网络带宽提高网络容量使攻击流量无法占据主导地位。关闭不必要服务关闭不必要的服务减少攻击目标从而降低攻击效果。 四、SQL 注入 SQL 注入攻击是指攻击者通过在 Web 应用程序的输入框中注入恶意 SQL 语句从而获取未授权的访问权限或窃取敏感数据。SQL 注入攻击的原理是利用应用程序中的漏洞将恶意 SQL 语句插入到 SQL 查询语句中从而操纵数据库。 输入验证对用户输入的数据进行有效性验证过滤掉无效或恶意的输入。可以使用正则表达式、字符串匹配等方法对输入数据进行过滤确保输入数据的合法性和准确性。参数化查询使用参数化查询避免将用户输入的数据直接拼接到 SQL 查询语句中。参数化查询可以将用户输入的数据与 SQL 语句分离从而避免 SQL 注入漏洞。在 Java 中可以使用 PreparedStatement 语句进行参数化查询。预编译语句使用预编译语句进行预处理可以将 SQL 语句和参数分离从而避免 SQL 注入漏洞。预编译语句可以在第一次执行时编译成机器码后续执行时直接使用机器码执行提高了执行效率。在 Java 中可以使用 PreparedStatement 语句进行预编译。Mybatis 映射语句在 Mybatis 映射语句中使用#{xxx}而不是${}来表示参数。这样可以避免将用户输入的数据直接拼接到 SQL 查询语句中从而避免 SQL 注入漏洞。在 Mybatis 中可以使用#{param1}, #{param2}等语法来表示参数。访问控制对数据库的访问进行严格的权限控制禁止未经授权的用户访问敏感数据。可以使用数据库的访问控制机制如 ACL、RLS 等对数据库的访问进行限制。此外可以在应用程序中对用户进行身份验证和授权确保只有授权用户可以访问敏感数据。数据加密对敏感数据进行加密防止数据泄露即使被攻击者窃取也无法获取明文数据。可以使用加密算法如 AES、SSL 等对敏感数据进行加密。在 Java 中可以使用 Java 加密算法或第三方加密库进行加密。 10、平时在开发接口或者设计项目的时候如何保证安全性的 小伙伴从以下8个维度去 “吹牛” 吹到 面试官 “口水直流不能自已” 签名和加密确保数据在传输过程中不被篡改或窃取可以使用签名和加密技术。签名可以确保数据的完整性和真实性而加密可以确保数据的保密性。常用的签名算法有 SHA-256、SHA-3 等加密算法有 AES、RSA 等。IP 检测和限流通过检测请求的 IP 地址可以实现访问控制和限流。例如可以限制某个 IP 地址的请求次数或限制某个 IP 地址的访问权限。这可以帮助防止 DDoS 攻击和其他恶意行为。接口幂等性确保接口在多次调用下不会产生副作用可以实现接口的幂等性。例如在处理支付接口时可以确保同一个订单多次支付不会产生重复扣款。特殊字符过滤通过过滤特殊字符可以防止 XSS 和 SQL 注入攻击。例如可以过滤掉单引号、双引号、斜杠等特殊字符避免攻击者通过注入恶意代码来执行攻击。防止 CSRF 攻击CSRF 攻击是一种利用用户浏览器发起恶意请求的攻击方式。为了防止 CSRF 攻击可以使用 token 技术即在客户端生成一个随机的 token在请求时将其发送给服务器服务器验证 token 的合法性从而确保请求是合法的。防止XSS攻击可以使用过滤器来过滤掉一些可能会导致XSS攻击的特殊字符或代码例如反斜杠、单引号等。SQL注入的攻击可以使用预编译语句或者参数化查询来防止SQL注入的攻击从而保护数据库的数据安全。输入验证在接收用户输入时需要对输入进行验证确保输入的格式和内容是合法的。例如可以验证输入的数字范围、字符长度等避免因为输入不合法而导致的安全漏洞。 11、使用Redis集群时可能会存在什么问题 小伙伴从以下7个维度去 “吹牛” 吹到 面试官 “口水直流不能自已” 首先数据一致性问题其次性能问题再次可用性问题再次负载均衡问题再次网络延迟问题再次节点故障问题最后安全问题 一、数据一致性问题 在 Redis 集群中当多个节点同时进行写操作时可能会导致数据不一致。例如当节点 A 和节点 B 同时尝试向同一个 key 写入数据时如果节点 A 先写入成功而节点 B 的后写入操作失败则导致该 key 的数据在节点 A 和节点 B 上不一致。为了解决这个问题可以使用 Redis 集群的主节点来协调多个节点之间的数据写入操作确保数据的一致性。 解决方法 使用主节点进行写操作协调所有写操作必须经过主节点协调由主节点分配节点进行写操作并确保所有节点写操作的顺序和一致性。这样可以保证数据的一致性但会增加主节点的负担降低性能。使用分布式锁在写操作之前节点需要获取分布式锁以确保在该节点完成写操作之前其他节点无法进行写操作。这样可以避免多个节点同时进行写操作导致的数据不一致问题。然而分布式锁可能会带来性能瓶颈因为锁的竞争可能会导致节点之间的延迟。 二、性能问题 在使用 Redis 集群时可能会出现性能瓶颈。例如当集群中的某个节点出现网络延迟或负载过高时可能会导致整个集群的性能下降。此外由于 Redis 集群需要进行数据同步和协调因此可能会增加额外的延迟和开销从而影响集群的性能。 解决方法 增加节点数量通过增加节点数量来提高集群的性能和吞吐量。更多的节点意味着更多的计算和存储资源可以更好地支持并发访问和数据处理。优化网络拓扑结构通过优化网络拓扑结构例如使用高速网络、增加网络带宽、使用负载均衡器等来提高集群的性能和吞吐量。优化 Redis 配置通过调整 Redis 的配置参数例如调整缓存大小、调整持久化策略、优化数据库文件等来提高集群的性能和吞吐量。 三、可用性问题 在使用 Redis 集群时如果某个节点出现故障或宕机可能会导致整个集群的不可用。 解决方法 需要使用 Redis 集群的高可用性机制来确保集群的可用性例如使用多个节点进行数据冗余和备份以便在节点故障或宕机时能够自动切换到备用节点。 可以通过以下两种方式来实现 数据冗余备份在 Redis 集群中可以将数据备份到多个节点上以确保在节点故障或宕机时仍有其他节点可以提供数据服务。故障转移在 Redis 集群中可以使用故障转移机制将故障节点的服务切换到其他节点上以确保集群的可用性。故障转移可以通过主节点检测节点状态或者通过心跳机制实现。 四、负载均衡问题 在Redis集群中每个节点都需要承担一定的负载如果负载不均衡可能会导致某些节点的负载过高影响系统的性能。 解决方法 可以使用Redis Cluster来解决负载均衡问题。Redis Cluster会根据节点的负载情况自动地将负载较高的节点上的数据转移到负载较低的节点上从而实现负载均衡。 五、网络延迟问题 在Redis集群中节点之间的通信需要通过网络进行如果网络延迟过高可能会导致数据的延迟影响系统的性能。 解决方法 可以使用Redis Cluster来解决网络延迟问题。Redis Cluster会根据节点之间的网络延迟情况自动地将数据转移到网络延迟较低的节点上从而降低数据的延迟。 六、节点故障问题 在Redis集群中如果某个节点发生故障可能会导致数据的不一致性或者系统的不可用性。 解决方法 可以使用Redis Cluster来解决节点故障问题。Redis Cluster会自动地将故障节点上的数据转移到其他节点上保证数据的一致性和系统的可用性。 七、安全问题 在Redis集群中数据的安全性也是一个问题如果节点的安全性受到攻击可能会导致数据的泄露或者被篡改。因此在使用Redis集群时需要注意数据的安全性并采取相应的安全措施。 解决方法 可以使用Redis Cluster来解决安全问题。Redis Cluster会对节点的安全性进行监控并采取相应的安全措施例如限制节点的访问权限、加密数据等从而保证数据的安全性。 12、有没有了解过cap和base原则 小伙伴从以下8个维度去 “吹牛” 吹到 面试官 “口水直流不能自已” 首先CAP理论其次CAP理论的 一致性(CConsistency)再次CAP理论的 可用性(AAvailability)再次CAP理论的 分区容错性PPartition tolerance再次BASE 理论再次BASE 理论的 Basically Available再次BASE 理论的 Soft State最后BASE 理论的 Eventually Consistent 一、CAP理论 CAP理论作为分布式系统的基础理论指的是在一个分布式系统中 Consistency一致性、 Availability可用性、Partition tolerance分区容错性这三个要素最多只能同时实现两点。 一致性(CConsistency) 一致性是指数据在多个副本之间能否保持一致的特性。例如一个数据在某个分区节点更新之后在其他分区节点读出来的数据也是更新之后的数据。 可用性(AAvailability) 可用性是指系统提供的服务必须一直处于可用的状态对于用户的每一个操作请求总是能够在有限的时间内返回结果。这里的重点是有限时间内和返回结果。 分区容错性P:Partition tolerance: 分布式系统在遇到任何网络分区故障的时候仍然需要能够保证对外提供满足一致性和可用性的服务。 选择说明CA放弃分区容错性加强一致性和可用性其实就是传统的单机数据库的选择AP放弃一致性分区容错性和可用性这是很多分布式系统设计时的选择CP放弃可用性追求一致性和分区容错性网络问题会直接让整个系统不可用 二、BASE 理论 BASE 理论 是对CAP中AP的一个扩展对于我们的业务系统我们考虑牺牲一致性来换取系统的可用性和分区容错性。BASE是Basically Available(基本可用)Soft state软状态,和 Eventually consistent最终一致性三个短语的缩写。 Basically Available 基本可用通过支持局部故障而不是系统全局故障来实现的。如将用户分区在 5 个数据库服务器上一个用户数据库的故障只影响这台特定主机那 20% 的用户其他用户不受影响。 Soft State 软状态状态可以有一段时间不同步 Eventually Consistent 最终一致最终数据是一致的就可以了而不是时时保持强一致。 13、zk是如何保证一致性的 小伙伴从以下4个维度去 “吹牛” 吹到 面试官 “口水直流不能自已” 首先ZAB协议Zookeeper原子消息广播协议其次选主再次选主后的数据同步再次事务操作 一ZAB协议Zookeeper原子消息广播协议 zookeeper实现数据一致性的核心是ZAB协议Zookeeper原子消息广播协议。该协议需要做到以下几点 1集群在半数以下节点宕机的情况下能正常对外提供服务 2客户端的写请求全部转交给leader来处理leader需确保写变更能实时同步给所有follower及observer 3leader宕机或整个集群重启时需要确保那些已经在leader服务器上提交的事务最终被所有服务器都提交确保丢弃那些只在leader服务器上被提出的事务并保证集群能快速恢复到故障前的状态。 Zab协议有两种模式 崩溃恢复选主数据同步和消息广播事务操作。 任何时候都需要保证只有一个主进程负责进行事务操作而如果主进程崩溃了就需要迅速选举出一个新的主进程。主进程的选举机制与事务操作机制是紧密相关的。 下面详细讲解这三个场景的协议规则从细节去探索ZAB协议的数据一致性原理。 二、选主 leader选举是zk中最重要的技术之一也是保证分布式数据一致性的关键所在。当集群中的一台服务器处于如下两种情况之一时就会进入leader选举阶段——服务器初始化启动、服务器运行期间无法与leader保持连接。 选举阶段集群间互传的消息称为投票投票Vote主要包括二个维度的信息ID、ZXID ID 被推举的leader的服务器ID集群中的每个zk节点启动前就要配置好这个全局唯一的ID。ZXID 被推举的leader的事务ID 该值是从机器DataTree内存中取的即事务已经在机器上被commit过了。 节点进入选举阶段后的大体执行逻辑如下 1设置状态为LOOKING初始化内部投票Vote (id,zxid) 数据至内存并将其广播到集群其它节点。节点首次投票都是选举自己作为leader将自身的服务ID、处理的最近一个事务请求的ZXIDZXID是从内存数据库里取的即该节点最近一个完成commit的事务id及当前状态广播出去。然后进入循环等待及处理其它节点的投票信息的流程中。 2循环等待流程中节点每收到一个外部的Vote信息都需要将其与自己内存Vote数据进行PK规则为取ZXID大的若ZXID相等则取ID大的那个投票。若外部投票胜选节点需要将该选票覆盖之前的内存Vote数据并再次广播出去同时还要统计是否有过半的赞同者与新的内存投票数据一致无则继续循环等待新的投票有则需要判断leader是否在赞同者之中在则退出循环选举结束根据选举结果及各自角色切换状态leader切换成LEADING、follower切换到FOLLOWING、observer切换到OBSERVING状态。 算法细节可参照FastLeaderElection.lookForLeader()主要有三个线程在工作选举线程主动调用lookForLeader方法的线程通过阻塞队列sendqueue及recvqueue与其它两个线程协作、WorkerReceiver线程选票接收器不断获取其它服务器发来的选举消息筛选后会保存到recvqueue队列中。zk服务器启动时开始正常工作不停止以及WorkerSender线程选票发送器会不断地从sendqueue队列中获取待发送的选票并广播至集群。WorkerReceiver线程一直在工作即使当前节点处于LEADING或者FOLLOWING状态它起到了一个过滤的作用当前节点为LOOKING时才会将外部投票信息转交给选举线程处理如果当前节点处于非LOOKING状态收到了处于LOOKING状态的节点投票数据外部节点重启或网络抖动情况下说明发起投票的节点数据跟集群不一致这时当前节点需要向集群广播出最新的内存Vote(idzxid)落后节点收到该Vote后会及时注册到leader上并完成数据同步跟上集群节奏提供正常服务。 三、选主后的数据同步 选主算法中的zxid是从内存数据库中取的最新事务id事务操作是分两阶段的提出阶段和提交阶段leader生成提议并广播给followers收到半数以上的ACK后再广播commit消息同时将事务操作应用到内存中。 follower收到提议后先将事务写到本地事务日志然后反馈ACK等接到leader的commit消息时才会将事务操作应用到内存中。可见选主只是选出了内存数据是最新的节点仅仅靠这个是无法保证已经在leader服务器上提交的事务最终被所有服务器都提交。比如leader发起提议P1,并收到半数以上follower关于P1的ACK后在广播commit消息之前宕机了选举产生的新leader之前是follower未收到关于P1的commit消息内存中是没有P1的数据。而ZAB协议的设计是需要保证选主后P1是需要应用到集群中的。这块的逻辑是通过选主后的数据同步来弥补。 选主后节点需要切换状态leader切换成LEADING状态后的流程如下 1重新加载本地磁盘上的数据快照至内存并从日志文件中取出快照之后的所有事务操作逐条应用至内存并添加到已提交事务缓存commitedProposals。这样能保证日志文件中的事务操作必定会应用到leader的内存数据库中。 2获取learner发送的FOLLOWERINFO/OBSERVERINFO信息并与自身commitedProposals比对确定采用哪种同步方式不同的learner可能采用不同同步方式DIFF同步、TRUNCDIFF同步、SNAP同步。这里是拿learner内存中的zxid与leader内存中的commitedProposalsmin、max比对如果zxid介于min与max之间但又不存在于commitedProposals中时说明该zxid对应的事务需要TRUNC回滚如果 zxid 介于min与max之间且存在于commitedProposals中则leader需要将zxid1~max 间所有事务同步给learner这些内存缺失数据很可能是因为leader切换过程中造成commit消息丢失learner只完成了事务日志写入未完成提交事务未应用到内存。 3leader主动向所有learner发送同步数据消息每个learner有自己的发送队列互不干扰。同步结束时leader会向learner发送NEWLEADER指令同时learner会反馈一个ACK。当leader接收到来自learner的ACK消息后就认为当前learner已经完成了数据同步同时进入“过半策略”等待阶段。当leader统计到收到了一半已上的ACK时会向所有已经完成数据同步的learner发送一个UPTODATE指令用来通知learner集群已经完成了数据同步可以对外服务了。 细节可参照Leader.lead() 、Follower.followLeader()及LearnerHandler类。 四、事务操作 ZAB协议对于事务操作的处理是一个类似于二阶段提交过程。 针对客户端的事务请求leader服务器会为其生成对应的事务proposal并将其发送给集群中所有follower机器然后收集各自的选票最后进行事务提交。 流程如下图 ZAB协议的二阶段提交过程中移除了中断逻辑事务回滚所有follower服务器要么正常反馈leader提出的事务proposal要么就抛弃leader服务器。follower收到proposal后的处理很简单将该proposal写入到事务日志然后立马反馈ACK给leader也就是说如果不是网络、内存或磁盘等问题follower肯定会写入成功并正常反馈ACK。leader收到过半follower的ACK后会广播commit消息给所有follower并将事务应用到内存follower收到commit消息后会将事务应用到内存。 ZAB协议中多次用到“过半”设计策略 该策略是zk在A可用性与C一致性间做的取舍也是zk具有高容错特性的本质。相较分布式事务中的2PC二阶段提交协议的“全量通过”ZAB协议可用性更高牺牲了部分一致性能在集群半数以下服务宕机时正常对外提供服务。 14、你如何设计一个能抗住大流量的系统说说设计方案 小伙伴说他是参考《尼恩Java面试宝典》中架构设计面试专题 吹的 面试官非常满意 15、有没有了解过缓存策略有哪些 小伙伴从以下3个维度去 “吹牛” 吹到 面试官 “口水直流不能自已” 首先Cache Aside旁路缓存策略其次Read/Write Through读穿 / 写穿策略再次Write Back写回策略 一、Cache Aside旁路缓存策略 我们可以在更新数据时不更新缓存而是删除缓存中的数据在读取数据时发现缓存中没了数据之后再从数据库中读取数据更新到缓存中。 缓存读写过程 这个策略就是我们使用缓存最常见的策略Cache Aside 策略也叫旁路缓存策略这个策略数据以数据库中的数据为准缓存中的数据是按需加载的。它可以分为读策略和写策略 读策略的步骤是 从缓存中读取数据 如果缓存命中则直接返回数据 如果缓存不命中则从数据库中查询数据 查询到数据后将数据写入到缓存中并且返回给用户。 写策略的步骤是 更新数据库中的记录 删除缓存记录。 注意 Cache Aside 存在的最大的问题是当写入比较频繁时缓存中的数据会被频繁地清理这样会对缓存的命中率有一些影响。如果你的业务对缓存命中率有严格的要求那么可以考虑两种解决方案 一种做法是在更新数据时也更新缓存只是在更新缓存前先加一个分布式锁因为这样在同一时间只允许一个线程更新缓存就不会产生并发问题了。当然这么做对于写入的性能会有一些影响另一种做法同样也是在更新数据时更新缓存只是给缓存加一个较短的过期时间这样即使出现缓存不一致的情况缓存的数据也会很快过期对业务的影响也是可以接受。 二、Read/Write Through读穿 / 写穿策略 这个策略的核心原则是用户只与缓存打交道由缓存和数据库通信写入或者读取数据。这就好比你在汇报工作的时候只对你的直接上级汇报再由你的直接上级汇报给他的上级你是不能越级汇报的。 Write Through 的策略是这样的先查询要写入的数据在缓存中是否已经存在如果已经存在则更新缓存中的数据并且由缓存组件同步更新到数据库中如果缓存中数据不存在我们把这种情况叫做“Write Miss写失效”。 一般来说我们可以选择两种“Write Miss”方式一个是“Write Allocate按写分配”做法是写入缓存相应位置再由缓存组件同步更新到数据库中另一个是“No-write allocate不按写分配”做法是不写入缓存中而是直接更新到数据库中。 在 Write Through 策略中我们一般选择“No-write allocate”方式原因是无论采用哪种“Write Miss”方式我们都需要同步将数据更新到数据库中而“No-write allocate”方式相比“Write Allocate”还减少了一次缓存的写入能够提升写入的性能。 Read Through 策略就简单一些它的步骤是这样的先查询缓存中数据是否存在如果存在则直接返回如果不存在则由缓存组件负责从数据库中同步加载数据。 下面是 Read Through/Write Through 策略的示意图 Read/Write Through策略示意图 Read Through/Write Through 策略的特点是由缓存节点而非用户来和数据库打交道在我们开发过程中相比 Cache Aside 策略要少见一些原因是我们经常使用的分布式缓存组件无论是 Memcached 还是 Redis 都不提供写入数据库或者自动加载数据库中的数据的功能。而我们在使用本地缓存的时候可以考虑使用这种策略比如说在上一节中提到的本地缓存 Guava Cache 中的 Loading Cache 就有 Read Through 策略的影子。 我们看到 Write Through 策略中写数据库是同步的这对于性能来说会有比较大的影响因为相比于写缓存同步写数据库的延迟就要高很多了。那么我们可否异步地更新数据库这就是我们接下来要提到的“Write Back”策略。 三、Write Back写回策略 这个策略的核心思想是在写入数据时只写入缓存并且把缓存块儿标记为“脏”的。而脏块儿只有被再次使用时才会将其中的数据写入到后端存储中。 需要注意的是在“Write Miss”的情况下我们采用的是“Write Allocate”的方式也就是在写入后端存储的同时要写入缓存这样我们在之后的写请求中都只需要更新缓存即可而无需更新后端存储了我将 Write back 策略的示意图放在了下面 Write Back 写回策略示意图 如果使用 Write Back 策略的话读的策略也有一些变化了。 我们在读取缓存时如果发现缓存命中则直接返回缓存数据。如果缓存不命中则寻找一个可用的缓存块儿如果这个缓存块儿是“脏”的就把缓存块儿中之前的数据写入到后端存储中并且从后端存储加载数据到缓存块儿如果不是脏的则由缓存组件将后端存储中的数据加载到缓存中最后我们将缓存设置为不是脏的返回数据就好了。 Write Back 读策略示意图 其实这种策略不能被应用到我们常用的数据库和缓存的场景中它是计算机体系结构中的设计比如我们在向磁盘中写数据时采用的就是这种策略。无论是操作系统层面的 Page Cache还是日志的异步刷盘亦或是消息队列中消息的异步写入磁盘大多采用了这种策略。因为这个策略在性能上的优势毋庸置疑它避免了直接写磁盘造成的随机写问题毕竟写内存和写磁盘的随机 I/O 的延迟相差了几个数量级呢。 但因为缓存一般使用内存而内存是非持久化的所以一旦缓存机器掉电就会造成原本缓存中的脏块儿数据丢失。所以你会发现系统在掉电之后之前写入的文件会有部分丢失就是因为 Page Cache 还没有来得及刷盘造成的。 当然你依然可以在一些场景下使用这个策略在使用时我想给你的落地建议是你在向低速设备写入数据的时候可以在内存里先暂存一段时间的数据甚至做一些统计汇总然后定时地刷新到低速设备上。比如说你在统计你的接口响应时间的时候需要将每次请求的响应时间打印到日志中然后监控系统收集日志后再做统计。但是如果每次请求都打印日志无疑会增加磁盘 I/O那么不如把一段时间的响应时间暂存起来经过简单的统计平均耗时每个耗时区间的请求数量等等然后定时地批量地打印到日志中。 总结 Cache Aside 是我们在使用分布式缓存时最常用的策略你可以在实际工作中直接拿来使用。推荐使用Read/Write Through 和 Write Back 策略需要缓存组件的支持所以比较适合你在实现本地缓存组件的时候使用Write Back 策略是计算机体系结构中的策略不过写入策略中的只写缓存异步写入后端存储的策略倒是有很多的应用场景。 参考文献 尼恩的系统架构知识图谱一张价值10w的系统架构知识图谱 www.processon.com/view/link/60fb9421637689719d246739 尼恩的秒杀系统的架构 www.processon.com/view/link/61148c2b1e08536191d8f92f 尼恩说在最后 在尼恩的50读者社区中很多、很多小伙伴需要进大厂、拿高薪。 尼恩团队会持续结合一些大厂的面试真题给大家梳理一下学习路径看看大家需要学点啥 前面用多篇文章给大家介绍阿里、百度、字节、滴滴的真题 《太猛了靠“吹牛”过顺丰一面月薪30K》 《炸裂了…京东一面索命40问过了就50W》 《问麻了…阿里一面索命27问过了就60W》 《百度狂问3小时大厂offer到手小伙真狠》 《饿了么太狠面个高级Java抖这多硬活、狠活》 《字节狂问一小时小伙offer到手太狠了》 《收个滴滴Offer从小伙三面经历看看需要学点啥》 这些真题都会收入到 史上最全、持续升级的 PDF电子书 《尼恩Java面试宝典》。 本文收录于 《尼恩Java面试宝典》 V95版。 基本上把尼恩的 《尼恩Java面试宝典》吃透大厂offer很容易拿到滴。 另外下一期的 大厂面经更加精彩具体可以参见文末公号。 《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》PDF请到下面公号【技术自由圈】取↓↓↓