淘系一面
1. 分布式
1.1. redis实现分布式锁
1.1.1. redis set
SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]
- EX second :设置键的过期时间为second秒。 SET key value EX second效果等同于SETEX key second value 。
- PX millisecond :设置键的过期时间为millisecond毫秒。 SET key value PX millisecond效果等同于PSETEX key millisecond value 。
- NX :只在键不存在时,才对键进行设置操作。 SET key value NX效果等同于SETNX key value 。
- XX :只在键已经存在时,才对键进行设置操作。
返回值:
SET 在设置操作成功完成时,才返回OK。如果设置了NX或者XX ,但因为条件没达到而造成设置操作未执行,那么命令返回空批量回复(NULL Bulk Reply)。
大致思想:
- SET lock currentTime+expireTime EX 600 NX,使用set设置lock值,并设置过期时间为600秒,如果成功,则获取锁
- 获取锁后,如果该节点掉线,则到过期时间ock值自动失效
- 释放锁时,使用del删除lock键值
使用redis单机来做分布式锁服务,可能会出现单点问题,导致服务可用性差,因此在服务稳定性要求高的场合,官方建议使用redis集群(例如5台,成功请求锁超过3台就认为获取锁),来实现redis分布式锁。详见RedLock。
优点:
- 性能高,redis可持久化,也能保证数据不易丢失
- redis集群方式提高稳定性
缺点:
- 使用redis主从切换时可能丢失部分数据
出处:
https://www.php.cn/redis/424497.html
1.1.2. redisson
RLock rlock = redisson.getLock("mylock");
redisson.lock();
redisson.unlock();
redisson支持redis单实例、redis哨兵、redis cluster、redis master-slave等各种部署架构。
为什么采用lua脚本:
因为一大坨复杂的业务逻辑,可以通过封装在lua脚本中发送给redis,保证这段复杂业务逻辑执行的原子性。
- 加锁机制
- 锁互斥机制
- watch dog自动延期机制
- 可重入加锁机制
- 释放锁机制
出处:https://www.cnblogs.com/AnXinliang/p/10019389.html
1.2. 不使用zookeeper、redis,如何实现分布式锁
1.3. redis集群数据一致性
redis 并不能保证数据的强一致性,原因是 reids 集群是主节点通过异步复制的方式把客户的数据异步写到从节点(注意:Redis 集群可能会在将来提供同步写的方法)。例如客户端向主节点B写入一条命令,主节点B先向客户端回复命令状态,然后主节点再将数据复制给他的从节点 B1持久化到磁盘中。
1.4. redis的缓存穿透
一个缓存和数据库中都没有的数据被命中,这就导致每次请求都会直击数据库,而且还得不到任何结果,此时缓存就失去了意义。
- 缓存空值:可以为这些key对应的值设置为null 丢到缓存里面去。后面再出现查询这个key 的请求的时候,直接返回null 。
- 布隆过滤器:这种方案可以加在第一种方案中,在缓存之前在加一层 BloomFilter ,在查询的时候先去 BloomFilter 去查询 key 是否存在,如果不存在就直接返回,存在再走查缓存 -> 查 DB。
1.5. 布隆过滤器存在的问题
首先布隆过滤器存在一些失误率,可能不存在的key在经过布隆过滤器时也显示存在。
如何选择:
针对于一些恶意攻击,攻击带过来的大量key 是不存在的,那么我们采用第一种方案就会缓存大量不存在key的数据。
此时我们采用第一种方案就不合适了,我们完全可以先对使用第二种方案进行过滤掉这些key。
针对这种key异常多、请求重复率比较低的数据,我们就没有必要进行缓存,使用第二种方案直接过滤掉。
而对于空数据的key有限的,重复率比较高的,我们则可以采用第一种方式进行缓存。
1.6. 缓存预热的方案
2. ElasticSearch
2.1. ElasticSearch 如何保证海量数据情况下的高可用
首先基于的是Lucene,迄今为止,最先进,性能最好,功能最全的搜索引擎库。ES是基于Lucene的开源搜索引擎,以Lucene为核心进行封装,提供相应的Restful API,使用起来更简单。
- 在数据进入后,es默认创建5个分片(0,1,2,3,4),集群状态为yellow。es会自动同步各个分片和其副本分片的数据。
- 为了支持全文检索,es中会维护一个叫做“invertedindex”(也叫逆向索引)的表,表内包含了所有文档中出现的所有单词,同时记录了这个单词在哪个文档中出现过。
2.2. 倒序索引
逆向索引是不可更改的,一旦它被建立了,里面的数据就不会再进行更改。这样做就带来了以下几个好处:
- 没有必要给逆向索引加锁,因为不允许被更改,只有读操作,所以就不用考虑多线程导致互斥等问题。
- 索引一旦被加载到了缓存中,大部分访问操作都是对内存的读操作,省去了访问磁盘带来的io开销。
- 因为逆向索引的不可变性,所有基于该索引而产生的缓存也不需要更改,因为没有数据变更。
- 使用逆向索引可以压缩数据,减少磁盘io及对内存的消耗。
2.3. 分片
3. Java 集合类
3.1. Iterator正向遍历,说一个可以双向遍历的Iterator(ListIterator)
ListIterator可以双向遍历,同时支持add、set、remove操作。
3.2. Java 中不可变的集合
Java 9 中 List 和 Set 接口使用 of() 方法创建一个空或者非空的不可变 List 或 Set 对象。Map 分别有两个方法用于创建不可变 Map 对象和不可变 Map.Entry 对象:of() 和 ofEntries()。
List immutableList = List.of(“one”,“two”,“three”);
不可变对象有很多优点,包括:
- 当对象被不可信的库调用时,不可变形式是安全的;
- 不可变对象被多个线程调用时,不存在竞态条件问题
- 不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率(分析和测试细节);
- 不可变对象因为有固定不变,可以作为常量来安全使用。
JDK也提供了Collections.unmodifiableXXX方法把集合包装为不可变形式,但我们认为不够好:
- 笨重而且累赘:不能舒适地用在所有想做防御性拷贝的场景;
- 不安全:要保证没人通过原集合的引用进行修改,返回的集合才是事实上不可变的;
- 低效:包装过的集合仍然保有可变集合的开销,比如并发修改的检查、散列表的额外空间,等等。
Java中的不可变集合,我们换个方式理解!!! - 程序零世界 - 博客园 (cnblogs.com)
3.3. 设计一个不可变集合,需要防止反射和序列化
反射修改不可变集合UnmodifiableList_howroad的博客-CSDN博客
如果自己设计:单例模式的实现方式及如何有效防止防止反射和反序列化 - 叫我鹏爷 - 博客园 (cnblogs.com)
4. JVM
4.1. Xms、Xmn 、Xss 作用
JVM系列三:JVM参数设置、分析 - redcreen - 博客园 (cnblogs.com)
Xms:初始堆大小
Xmx:最大堆大小
Xmn:年轻代大小
4.2. 异常的整体结构
Exception和Error都是继承了Throwable类,在java中只有Throwable类型的实例才可以被抛出(throw)或者捕获(catch),他是异常处理机制的基本组成类型。
- Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应的处理。
- Error是指正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序(比如JVM自身)处于非正常状态,不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError之类,都是Error的子类。
- Exception又分为可检查(checked)异常和不可检查(unchecked)异常,可检查异常在源码里必须显示的进行捕获处理,这里是编译期检查的一部分。前面我们介绍的不可查的Error,是Throwable不是Exception。
- 不可检查异常就是所谓的运行时异常,类似NullPointerException,ArrayIndexOutOfBoundsExceptin之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译器强制要求。
NoClassDefFoundError和ClassNotFoundException有什么区别,这也是一个经典的入门题目。简单总结就是,NoClassDefFoundError发生在编译时对应的类可用,而运行时在Java的classpath路径中,对应的类不可用导致的错误。
捕获异常的两个基本原则:
- 尽量不要捕获类似Exception这样的通用异常,而是应该捕获特定异常。
- 不要生吞(swallow)异常。这一异常处理中特别注意的事情,很可能会导致非常难以诊断的诡异情况。
Exception和Error的区别_上善若水-CSDN博客
4.3. OutOfMemoryError、StackOverflowError,栈空间满了会报什么错
OutOfMemoryError异常:
- 程序计数器:无
- Java虚拟机栈: 如果虚拟机栈可扩展,扩展时无法申请到足够内存
- 本地方法栈:与Java虚拟机栈相同
- 方法区(运行时常量池):方法区无法满足内存分配需求(常量池无法申请到内存)
- 直接内存:内存区域总和大于物理内存总和
StackOverflowError异常:
- 程序计数器:无
- Java虚拟机栈:线程请求的栈深度大于虚拟机所允许的深度
- 本地方法栈:与Java虚拟机栈相同
- Java堆:无
- 方法区:无
- 直接内存:无
5. DB
5.1. InnoDB 和 MyISAM 哪个性能更好
MyISAM与InnoDB两者之间区别与选择,详细总结,性能对比 - 杨少侠-ing - 博客园 (cnblogs.com)
5.2. SQL 排他锁
# 排他锁
begin ;
select * from myuser where id = 1 for update ;
commit ;
# 共享锁
begin ;
select * from myuser where id = 1 lock in share mode ;
commit ;
5.3. 设计一个SQL,保证此SQL执行时,其他SQL无法操作当前行数据(包括Select)
就是加 排他锁 就可以。
6. 网络
6.1. 401 和 403 区别
- 401:Unauthorized 未授权,客户端在访问请求的资源之前,对自己进行认证。
- 403:Forbidden 拒绝访问,资源不可用,服务器理解客户的请求,但拒绝处理它。
6.2. 301 和 302 区别
- 301:适合永久重定向
- 302:用来做临时跳转
302重定向只是暂时的重定向,搜索引擎会抓取新的内容而保留旧的地址,因为服务器返回302,所以,搜索引擎认为新的网址是暂时的。而301重定向是永久的重定向,搜索引擎在抓取新的内容的同时也将旧的网址替换为了重定向之后的网址。
状态码301和302的区别 - Wayne-Zhu - 博客园 (cnblogs.com)