首先HashMap是Map的一个实现类,而Map存储形式是键值对(key,value)的。可以看成是一个一个的Entry。Entry所存放的位置是由key来决定的。
Map中的key是无序的且不可重复的,所有的key可以看成是一个set集合,如果出现Map中的key如果是自定义类的对象,则必须重写hashCode和equals方法,因为如果不重写,使用的是Object类中的hashCode和equals方法,比较的是内存地址值不是比内容。
Map中的value是无序的可重复的,所有的value可以看成是Collection集合,Map中的value如果是自定义类的对象必须重写equals方法。
至于要重写hashCode和equals分别做什么用,拿hashMap底层原理来说:
当我们向HashMap中存放一个元素(k1,v1),先根据k1的hashCode方法来决定在数组中存放的位置。
如果这个位置没有其它元素,将(k1,v1)直接放入Node类型的数组中,这个数组初始化容量是16,默认的加载因子是0.75,也就是当元素加到12的时候,底层会进行扩容,扩容为原来的2倍。如果该位置已经有其它元素(k2,v2),那就调用k1的equals方法和k2进行比较二个元素是否相同,如果结果为true,说明二个元素是一样的,用v1替换v2,如果返回值为false,二个元素不一样,就用链表的形式将(k1,v1)存放。
不过当链表中的数据较多时,查询的效率会下降,所以在JDK1.8版本后做了一个升级,就是当链表中的元素达到8时,会将链表替换成红黑树,来提高查找效率。因为对于搜索,插入,删除操作多的情况下,使用红黑树的效率要高一些。
原因是因为红黑树是一种特殊的二叉查找树,二叉查找树所有节点的左子树都小于该节点,所有节点的右子树都大于该节点,就可以通过大小比较关系来进行快速的检索。
在红黑树上插入或者删除一个节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树,可以通过左旋和右旋,使这颗树重新成为红黑树。红黑树的5条性质(根节点是黑色,每个节点是黑色或者是红色,每个叶子节点是黑色,如果一个节点是红色它的子节点必须是黑色的,从一个节点到该节点的子孙外部节点的所有路径上包含相同数目的黑点)
而且像这种二叉树结构比较常见的使用场景是Mysql二种引擎的索引,Myisam使用的是B树,InnoDB使用的是B+树。
首先B树它的每个节点都是Key.value的二元组,它的key都是从左到右递增的排序,value中存储数据。这种模式在读取数据方面的性能很高,因为有单独的索引文件,Myisam的存储文件有三个.frm是表的结构文件,.MYD是数据文件,.MYI是索引文件。不过Myisam也有些缺点它只支持表级锁,不支持行级锁也不支持事务,外键等,所以一般用于大数据存储。
然后是InnoDB,它的存储文件相比Myisam少一个索引文件,它是以ID为索引的数据存储,数据现在都被存在了叶子结点,索引在非叶结点上。而这些节点分散在索引页上。在InnoDB里,每个页默认16KB,假设索引的是8B的long型数据,每个key后有个页号4B,还有6B的其他数据,那么每个页的扇出系数为16KB/(8B+4B+6B)≈1000,即每个页可以索引1000个key。在高度h=3时,s=1000^3=10亿!!也就是说,InnoDB通过三次索引页的I/O,即可索引10亿的key,而非叶节点这一行存储的索引,数量就多了,I/O的次数就少了。而Myisam在每个节点都存储数据和索引,这样就减少了每页存储的索引数量。而且InnoDB它还支持行级,表级锁,也支持事务,外键.
另外对于HashMap实际使用过程中还是会出现一些线程安全问题:
HashMap是线程不安全的,在多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,而且会抛出并发修改异常,导致原因是并发争取线程资源,修改数据导致的,一个线程正在写,一个线程过来争抢,导致线程写的过程被其他线程打断,导致数据不一致。
为了应对hashmap在并发环境下不安全问题可以使用,ConcurrentHashMap大量的利用了volatile,CAS等技术来减少锁竞争对于性能的影响。
在JDK1.7版本中ConcurrentHashMap避免了对全局加锁,改成了局部加锁(分段锁),分段锁技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。不过这种结构的带来的副作用是Hash的过程要比普通的HashMap要长。
所以在JDK1.8版本中CurrentHashMap内部中的value使用volatile修饰,保证并发的可见性以及禁止指令重排,只不过volatile不保证原子性,使用为了确保原子性,采用CAS(比较交换)这种乐观锁来解决。
CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。
如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。CAS是通过无限循环来获取数据的,若果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。
volatile有三个特性:可见性,不保证原子性,禁止指令重排。
可见性:线程1从主内存中拿数据1到自己的线程工作空间进行操作(假设是加1)这个时候数据1已经改为数据2了,将数据2写回主内存时通知其他线程(线程2,线程3),主内存中的数据1已改为数据2了,让其他线程重新拿新的数据(数据2)。
不保证原子性:线程1从主内存中拿了一个值为1的数据到自己的工作空间里面进行加1的操作,值变为2,写回主内存,然后还没有来得及通知其他线程,线程1就被线程2抢占了,CPU分配,线程1被挂起,线程2还是拿着原来主内存中的数据值为1进行加1,值变成2,写回主内存,将主内存值为2的替换成2,这时线程1的通知到了,线程2重新去主内存拿值为2的数据。
禁止指令重排:首先指令重排是程序执行的时候不总是从上往下执行的,就像高考答题,可以先做容易的题目再做难的,这时做题的顺序就不是从上往下了。禁止指令重排就杜绝了这种情况。
(一般面试官开始问你会从java基础问起,一问大多数会问到集合这一块,而集合问的较多的是HashMap,这个时候你就可以往这些方向带着面试官问你,而且扩展的深度也够,所以上面的干货够你说个十来分钟吧,第一个问题拿下后,面试官心里至少简单你的基础够扎实,第一印象分就留下了)
AOP:面向切面编程。
即在一个功能模块中新增其他功能,比方说你要下楼取个快递,你同事对你说帮我也取一下呗,你就顺道取了。在工作中如果系统中有些包和类中没有使用AOP,例如日志,事务和异常处理,那么就必须在每个类和方法中去实现它们。代码纠缠每个类和方法中都包含日志,事务以及异常处理甚至是业务逻辑。在一个这样的方法中,很难分清代码中实际做的是什么处理。AOP所做的就是将所有散落各处的事务代码集中到一个事务切面中。
场景
又或者你去访问数据库,而你不想管事务(太烦),所以,Spring在你访问数据库之前,自动帮你开启事务,当你访问数据库结束之后,自动帮你提交/回滚事务!
异常处理你可以开启环绕通知,一旦运行接口报错,环绕通知捕获异常跳转异常处理页面。
动态代理
SpringAOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。它的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。如果目标类没有实现接口,那么SpringAOP会选择使用CGLIB来动态代理目标类。CGLIB是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
IOC:依赖注入或者叫做控制反转。
正常情况下我们使用一个对象时都是需要newObject()的。而ioc是把需要使用的对象提前创建好,放到spring的容器里面。
所有需要使用的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。DI(依赖注入)其实就是IOC的另外一种说法,其实它们是同一个概念的不同角度描述。
场景:
正常情况下我们使用一个对象时都是需要newObject()的。而ioc是把需要使用的对象提前创建好,放到spring的容器里面。需要使用的时候直接使用就行,而且可以设置单例或多例,非常灵活。
我们在service层想调用另外一个service的方法,不需要去new了,直接把它交给spring管理,然后用注解的方式引入就能使用。
IOC三种注入方式
(1)XML:Bean实现类来自第三方类库,例如DataSource等。需要命名空间等配置,例如:context,aop,mvc。
(2)注解:在开发的类使用@Controller,@Service等注解
(3)Java配置类:通过代码控制对象创建逻辑的场景。例如:自定义修改依赖类库。
什么是事务?
事务是访问并可能更新数据库中各种数据项的一个程序执行单元。
Spring事务与数据库事务关系?
Spring的事务是对数据库的事务的封装,最后本质的实现还是在数据库,假如数据库不支持事务的话,Spring的事务是没有作用的。所以说Spring事务的底层依赖MySQL的事务,Spring是在代码层面利用AOP实现,执行事务的时候使用TransactionInceptor进行拦截,然后处理。本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
属性(特性)A(原子性):要么全部完成,要么完全不起作用C(一致性):一旦事务完成(不管成功还是失败),业务处于一致的状态,而不会是部分完成,部分失败。I(隔离性):多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。D(持久性):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,事务的结果被写到持久化存储器中。
什么叫事务传播行为?
传播,至少有两个东西,才可以发生传播。单体不存在传播这个行为。事务传播行为就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
Spring支持7中事务传播行为propagation_required(需要传播):当前没有事务则新建事务,有则加入当前事务propagation_supports(支持传播):支持当前事务,如果当前没有事务则以非事务方式执行propagation_mandatory(强制传播):使用当前事务,如果没有则抛出异常propagation_nested(嵌套传播):如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行需要传播行为。propagation_never(绝不传播):以非事务的方式执行,如果当前有事务则抛出异常propagation_requires_new(传播需要新的):新建事务,如果当前有事务则把当前事务挂起propagation_not_supported(不支持传播):以非事务的方式执行,如果当前有事务则把当前事务挂起
数据库事务的隔离级别
数据库事务的隔离级别有4个,由低到高依次为Readuncommitted、Readcommitted、Repeatableread、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
√:可能出现×:不会出现
注意:我们讨论隔离级别的场景,主要是在多个事务并发的情况下,因此,接下来的讲解都围绕事务并发。Readuncommitted读未提交公司发工资了,领导把20000元打到廖志伟的账号上,但是该事务并未提交,而廖志伟正好去查看账户,发现工资已经到账,是20000元整,非常高兴。可是不幸的是,领导发现发给廖志伟的工资金额不对,是16000元,于是迅速回滚了事务,修改金额后,将事务提交,最后廖志伟实际的工资只有16000元,廖志伟空欢喜一场。
出现上述情况,即我们所说的脏读,两个并发的事务,“事务A:领导给廖志伟发工资”、“事务B:廖志伟查询工资账户”,事务B读取了事务A尚未提交的数据。当隔离级别设置为Readuncommitted时,就可能出现脏读,如何避免脏读,请看下一个隔离级别。
Readcommitted读提交廖志伟拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把廖志伟工资卡的2000元转到另一账户,并在廖志伟之前提交了事务,当廖志伟扣款时,系统检查到廖志伟的工资卡已经没有钱,扣款失败,廖志伟十分纳闷,明明卡里有钱,为何…
出现上述情况,即我们所说的不可重复读,两个并发的事务,“事务A:廖志伟消费”、“事务B:廖志伟的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。当隔离级别设置为Readcommitted时,避免了脏读,但是可能会造成不可重复读。大多数数据库的默认级别就是Readcommitted,比如SqlServer,Oracle。如何解决不可重复读这一问题,请看下一个隔离级别。
Repeatableread重复读当廖志伟拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),廖志伟的老婆就不可能对该记录进行修改,也就是廖志伟的老婆不能在此时转账。这就避免了不可重复读。廖志伟的老婆工作在银行部门,她时常通过银行内部系统查看廖志伟的信用卡消费记录。有一天,她正在查询到廖志伟当月信用卡的总消费金额(selectsum(amount)fromtransactionwheremonth=本月)为80元,而廖志伟此时正好在外面胡吃海喝后在收银台买单,消费1000元,即新增了一条1000元的消费记录(inserttransaction…),并提交了事务,随后廖志伟的老婆将廖志伟当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,廖志伟的老婆很诧异,以为出现了幻觉,幻读就这样产生了。当隔离级别设置为Repeatableread时,可以避免不可重复读,但会出现幻读。注:MySQL的默认隔离级别就是Repeatableread。
Serializable序列化Serializable是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。
Spring注解:
SpringMVC注解
Mybatis注解:(偷个懒,不使用表格了,嘻嘻)
SpringBoot注解:
SpringMVC的工作原理:
SpringBoot框架的优点:
MyBatis框架的优点:
一、业务场景介绍
先来给大家说一个业务场景,假设咱们现在开发一个电商网站,要实现支付订单的功能,流程如下:
针对上述流程,我们需要有订单服务、库存服务、仓储服务、积分服务。整个流程的大体思路如下:
至此,整个支付订单的业务流程结束
下图这张图,清晰表明了各服务间的调用过程:
好!有了业务场景之后,咱们就一起来看看SpringCloud微服务架构中,这几个组件如何相互协作,各自发挥的作用以及其背后的原理。
二、SpringCloud核心组件:Eureka
咱们来考虑第一个问题:订单服务想要调用库存服务、仓储服务,或者积分服务,怎么调用?
咱们来看看下面的这张图,结合图来仔细剖析一下整个流程:
如上图所示,库存服务、仓储服务、积分服务中都有一个EurekaClient组件,这个组件专门负责将这个服务的信息注册到EurekaServer中。说白了,就是告诉EurekaServer,自己在哪台机器上,监听着哪个端口。而EurekaServer是一个注册中心,里面有一个注册表,保存了各服务所在的机器和端口号
这时如果订单服务想要调用库存服务,不就可以找自己本地的EurekaClient问一下库存服务在哪台机器?监听哪个端口吗?收到响应后,紧接着就可以发送一个请求过去,调用库存服务扣减库存的那个接口!同理,如果订单服务要调用仓储服务、积分服务,也是如法炮制。
总结一下:
三、SpringCloud核心组件:Feign
现在订单服务确实知道库存服务、积分服务、仓库服务在哪里了,同时也监听着哪些端口号了。但是新问题又来了:难道订单服务要自己写一大堆代码,跟其他服务建立网络连接,然后构造一个复杂的请求,接着发送请求过去,最后对返回的响应结果再写一大堆代码来处理吗?
这是上述流程翻译的代码片段,咱们一起来看看,体会一下这种绝望而无助的感受!!!
友情提示,前方高能:
看完上面那一大段代码,有没有感到后背发凉、一身冷汗?实际上你进行服务间调用时,如果每次都手写代码,代码量比上面那段要多至少几倍,所以这个事压根儿就不是地球人能干的。
既然如此,那怎么办呢?别急,Feign早已为我们提供好了优雅的解决方案。来看看如果用Feign的话,你的订单服务调用库存服务的代码会变成啥样?
看完上面的代码什么感觉?是不是感觉整个世界都干净了,又找到了活下去的勇气!没有底层的建立连接、构造请求、解析响应的代码,直接就是用注解定义一个FeignClient接口,然后调用那个接口就可以了。人家FeignClient会在底层根据你的注解,跟你指定的服务建立连接、构造请求、发起靕求、获取响应、解析响应,等等。这一系列脏活累活,人家Feign全给你干了。
那么问题来了,Feign是如何做到这么神奇的呢?很简单,Feign的一个关键机制就是使用了动态代理。咱们一起来看看下面的图,结合图来分析:
四、SpringCloud核心组件:Ribbon
说完了Feign,还没完。现在新的问题又来了,如果人家库存服务部署在了5台机器上,如下所示:
这下麻烦了!人家Feign怎么知道该请求哪台机器呢?
此外,Ribbon是和Feign以及Eureka紧密协作,完成工作的,具体如下:
对上述整个过程,再来一张图,帮助大家更深刻的理解:
五、SpringCloud核心组件:Hystrix
在微服务架构里,一个系统会有很多的服务。以本文的业务场景为例:订单服务在一个业务流程里需要调用三个服务。现在假设订单服务自己最多只有100个线程可以处理请求,然后呢,积分服务不幸的挂了,每次订单服务调用积分服务的时候,都会卡住几秒钟,然后抛出—个超时异常。
咱们一起来分析一下,这样会导致什么问题?
上面这个,就是微服务架构中恐怖的服务雪崩问题,
如下图所示:
如上图,这么多服务互相调用,要是不做任何保护的话,某一个服务挂了,就会引起连锁反应,导致别的服务也挂。比如积分服务挂了,会导致订单服务的线程全部卡在请求积分服务这里,没有一个线程可以工作,瞬间导致订单服务也挂了,别人请求订单服务全部会卡住,无法响应。
但是我们思考一下,就算积分服务挂了,订单服务也可以不用挂啊!为什么?
现在问题分析完了,如何解决?
这时就轮到Hystrix闪亮登场了。Hystrix是隔离、熔断以及降级的一个框架。啥意思呢?说白了,Hystrix会搞很多个小小的线程池,比如订单服务请求库存服务是一个线程池,请求仓储服务是一个线程池,请求积分服务是一个线程池。每个线程池里的线程就仅仅用于请求那个服务。
打个比方:现在很不幸,积分服务挂了,会咋样?
当然会导致订单服务里那个用来调用积分服务的线程都卡死不能工作了啊!但由于订单服务调用库存服务、仓储服务的这两个线程池都是正常工作的,所以这两个服务不会受到任何影响。
这个时候如果别人请求订单服务,订单服务还是可以正常调用库存服务扣减库存,调用仓储服务通知发货。只不过调用积分服务的时候,每次都会报错。但是如果积分服务都挂了,每次调用都要去卡住几秒钟干啥呢?有意义吗?当然没有!所以我们直接对积分服务熔断不就得了,比如在5分钟内请求积分服务直接就返回了,不要去走网络请求卡住几秒钟,这个过程,就是所谓的熔断!
那人家又说,兄弟,积分服务挂了你就熔断,好歹你干点儿什么啊!别啥都不干就直接返回啊?没问题,咱们就来个降级:每次调用积分服务,你就在数据库里记录一条消息,说给某某用户增加了多少积分,因为积分服务挂了,导致没增加成功!这样等积分服务恢复了,你可以根据这些记录手工加一下积分。这个过程,就是所谓的降级。
为帮助大家更直观的理解,接下来用一张图,梳理一下Hystrix隔离、熔断和降级的全流程:
六、SpringCloud核心组件:Zuul
说完了Hystrix,接着给大家说说最后一个组件:Zuul,也就是微服务网关。这个组件是负责网络路由的。不懂网络路由?行,那我给你说说,如果没有Zuul的日常工作会怎样?
假设你后台部署了几百个服务,现在有个前端兄弟,人家请求是直接从浏览器那儿发过来的。打个比方:人家要请求一下库存服务,你难道还让人家记着这服务的名字叫做inventory-service?部署在5台机器上?就算人家肯记住这一个,你后台可有几百个服务的名称和地址呢?难不成人家请求一个,就得记住一个?你要这样玩儿,那真是友谊的小船,说翻就翻!
七、总结:
最后再来总结一下,上述几个SpringCloud核心组件,在微服务架构中,分别扮演的角色:
以上就是我们通过一个电商业务场景,阐述了SpringCloud微服务架构几个核心组件的底层原理。
文字总结还不够直观?没问题!我们将SpringCloud的5个核心组件通过一张图串联起来,再来直观的感受一下其底层的架构原理:
五大核心组件讲完了,面试官心中已经知道你对SpringCould的有一定的了解了,但这还不够,你如果讲到这个层面,部分面试官还会继续问,因为你讲解的这些其他面试者也讲过,可能也就你讲的比较细一些,但本质还是和他们差不了太多,有些公司可能集中招人,负责面试的可能就一个,你想想他这一天可以面试多少个人,这个时候你就需要继续拓展其他组件,来突出你的不同了。
CAP定论
BASE
BASE是BasicallyAvailable(基本可用)、Softstate(软状态)和Eventuallyconsistent(最终一致性)三个短语的缩写。是对CAP中AP的一个扩展
1.根据目的来分
根据模式是用来完成什么工作来划分,这种方式可分为创建型模式、结构型模式和行为型模式3种。
2.根据作用范围来分
根据模式是主要用于类上还是主要用于对象上来分,这种方式可分为类模式和对象模式两种。
3.设计模式的功能
简单介绍一个redis?
redis是内存中的数据结构存储系统,一个key-value类型的非关系型数据库,可持久化的数据库,相对于关系型数据库(数据主要存在硬盘中),性能高,因此我们一般用redis来做缓存使用;并且redis支持丰富的数据类型,比较容易解决各种问题,因此redis可以用来作为注册中心,数据库、缓存和消息中间件。Redis的Value支持5种数据类型,string、hash、list、set、zset(sortedset);
String类型:一个key对应一个value
Hash类型:它的key是string类型,value又是一个map(key-value),适合存储对象。
List类型:按照插入顺序的字符串链表(双向链表),主要命令是LPUSH和RPUSH,能够支持反向查找和遍历
Set类型:用哈希表类型的字符串序列,没有顺序,集合成员是唯一的,没有重复数据,底层主要是由一个value永远为null的hashmap来实现的。
zset类型:和set类型基本一致,不过它会给每个元素关联一个double类型的分数(score),这样就可以为成员排序,并且插入是有序的。
你还用过其他的缓存吗?这些缓存有什么区别?都在什么场景下去用?
对于缓存了解过redis和memcache
Memcache和redis的区别:
数据支持的类型:redis不仅仅支持简单的k/v类型的数据,同时还支持list、set、zset、hash等数据结构的存储;memcache只支持简单的k/v类型的数据,key和value都是string类型
可靠性:memcache不支持数据持久化,断电或重启后数据消失,但其稳定性是有保证的;redis支持数据持久化和数据恢复,允许单点故障,但是同时也会付出性能的代价
性能上:对于存储大数据,memcache的性能要高于redis
应用场景:
Redis:适用于对读写效率要求高、数据处理业务复杂、安全性要求较高的系统
对redis的持久化了解不?
redis的持久化方式有两种:
RDB(半持久化方式):按照配置不定期的通过异步的方式、快照的形式直接把内存中的数据持久化到磁盘的一个dump.rdb文件(二进制的临时文件)中,redis默认的持久化方式,它在配置文件(redis.conf)中。
优点:只包含一个文件,将一个单独的文件转移到其他存储媒介上,对于文件备份、灾难恢复而言,比较实用。
缺点:系统一旦在持久化策略之前出现宕机现象,此前没有来得及持久化的数据将会产生丢失
RDB持久化配置:
Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:
save9001#在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
save30010#在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save6010000#在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。
AOF(全持久化的方式):把每一次数据变化都通过write()函数将你所执行的命令追加到一个appendonly.aof文件里面,Redis默认是不支持这种全持久化方式的,需要在配置文件(redis.conf)中将appendonlyno改成appendonlyyes
优点:数据安全性高,对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机问题,也不会破坏日志文件中已经存在的内容;
缺点:对于数量相同的数据集来说,aof文件通常要比rdb文件大,因此rdb在恢复大数据集时的速度大于AOF;
AOF持久化配置:
在Redis的配置文件中存在三种同步方式,它们分别是:
appendfsyncalways#每次有数据修改发生时都会都调用fsync刷新到aof文件,非常慢,但是安全;
appendfsynceverysec#每秒钟都调用fsync刷新到aof文件中,很快,但是可能丢失一秒内的数据,推荐使用,兼顾了速度和安全;
appendfsyncno#不会自动同步到磁盘上,需要依靠OS(操作系统)进行刷新,效率快,但是安全性就比较差;
二种持久化方式区别:
AOF在运行效率上往往慢于RDB,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效;
如果缓存数据安全性要求比较高的话,用aof这种持久化方式(比如项目中的购物车);
如果对于大数据集要求效率高的话,就可以使用默认的。而且这两种持久化方式可以同时使用。
做过redis的集群吗?你们做集群的时候搭建了几台,都是怎么搭建的?
Redis的数据是存放在内存中的,不适合存储大数据,大数据存储一般公司常用hadoop中的Hbase或者MogoDB。redis主要用来处理高并发的,用我们的项目来说,电商项目如果并发大的话,一台单独的redis是不能足够支持我们的并发,这就需要我们扩展多台设备协同合作,即用到集群。
Redis搭建集群的方式有多种,例如:客户端分片、Twemproxy、Codis等,但是redis3.0之后就支持redis-cluster集群,这种方式采用的是无中心结构,每个节点保存数据和整个集群的状态,每个节点都和其他所有节点连接。如果使用的话就用redis-cluster集群。集群这块是公司运维搭建的,具体怎么搭建不是太了解。
我们项目中redis集群主要搭建了6台,3主(为了保证redis的投票机制)3从(高可用),每个主服务器都有一个从服务器,作为备份机。所有的节点都通过PING-PONG机制彼此互相连接;客户端与redis集群连接,只需要连接集群中的任何一个节点即可;Redis-cluster中内置了16384个哈希槽,Redis-cluster把所有的物理节点映射到【0-16383】slot上,负责维护。
redis有事务吗?
Redis是有事务的,redis中的事务是一组命令的集合,这组命令要么都执行,要不都不执行,保证一个事务中的命令依次执行而不被其他命令插入。redis的事务是不支持回滚操作的。redis事务的实现,需要用到MULTI(事务的开始)和EXEC(事务的结束)命令;
缓存穿透
缓存查询一般都是通过key去查找value,如果不存在对应的value,就要去数据库中查找。如果这个key对应的value在数据库中也不存在,并且对该key并发请求很大,就会对数据库产生很大的压力,这就叫缓存穿透
解决方案:
1.对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。
2.将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
缓存雪崩
redis的安全机制(你们公司redis的安全这方面怎么考虑的?)
哨兵机制:
监控:监控主数据库和从数据库是否正常运行;
提醒:当被监控的某个redis出现问题的时候,哨兵可以通过API向管理员或者其他应用程序发送通知;
自动故障迁移:主数据库出现故障时,可以自动将从数据库转化为主数据库,实现自动切换;
具体的配置步骤参考的网上的文档。要注意的是,如果master主服务器设置了密码,记得在哨兵的配置文件(sentinel.conf)里面配置访问密码
有几种实现方式?
优缺点
1.继承Thread类
2.实现Runnable接口
3.实现Callable
4.线程池、实现自动化装配,易于管理,循环利用资源。
代码实现案例:
继承Thread类,并重写里面的run方法classAextendsThread{publicvoidrun(){for(inti=1;i<=100;i++){System.out.println("-----------------"+i);}}}Aa=newA();a.start();实现Runnable接口,并实现里面的run方法classBimplementsRunnable{publicvoidrun(){for(inti=1;i<=100;i++){System.out.println("-----------------"+i);}}}Bb=newB();Threadt=newThread(b);t.start();实现CallableclassAimplementsCallable
在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?
整体上来说Lock是synchronized的扩展版,Lock提供了无条件的、可轮询的(tryLock方法)、定时的(tryLock带参方法)、可中断的(lockInterruptibly)、可多条件队列的(newCondition方法)锁操作。另外Lock的实现类基本都支持非公平锁(默认)和公平锁,synchronized只支持非公平锁,当然,在大部分情况下,非公平锁是高效的选择。
JUC并发包:
ThreadLocal与Lock和Synchronize区别
关于分布式事物我看了有一篇博文感觉写的很好,这里我就引用他的地址:
索引使用的限制条件,sql优化有哪些
。。。。。(待完善中)
关于背面试题说明:对于上面的面试题其实都是一些比较常见的,高频率的题目,能回答上来的有很多人,我相信你是可以做到的,但你聊的真的足够深入吗?讲解的真的够全面吗?拿下面第一题来说,面试官一般都直接问你HashMap实现原理,但是要是换一个问法,比如:影响HashMap性能有哪些因素?HashMap为什么存取效率那么高?如果只是死记硬背总有那么几道题达不上来吧,相信大部分面试者就只会讲一些在网上找到的答案,没有扩展,面试官听到你的回答,其实他已经听过很多遍了,讲出花来在面试官耳朵里也就那么回事,你拿什么和别人拉开差距,所有请不要死记硬背。
关于工作说明:能拿到16k,说明你已经具备了java中级开发的能力了,这一阶段已经不局限于CRUD了,已经可以独立负责一个模块开发,有一定的性能优化能力了,而不是只要面试过关就可以的,你的编码能力,独立开发的能力,对业务理解的能力,和团队沟通的能力要达到相应的水平。
这篇博文仅为参考,背景于2019年年底,上海部分地区。总结:能力多少靠自己,薪资多少看命运。