阿里内推Java一面题目
数据库
1. 使用mysql索引都有哪些原则??索引什么数据结构??B+tree 和 B tree 什么区别??
1、 对于查询频率高的字段创建索引;
2、 对排序、分组、联合查询频率高的字段创建索引;
3、 索引的数目不宜太多
原因:a、每创建一个索引都会占用相应的物理控件;
b、过多的索引会导致insert、update、delete语句的执行效率降低;
4、若在实际中,需要将多个列设置索引时,可以采用多列索引
如:某个表(假设表名为Student),存在多个字段(StudentNo, StudentName, Sex, Address, Phone, BirthDate),其中需要对StudentNo,StudentName字段进行查询,对Sex字段进行分组,对BirthDate字段进行排序,此时可以创建多列索引
index index_name (StudentNo, StudentName, Sex, BirthDate); #index_name为索引名
在上面的语句中只创建了一个索引,但是对4个字段都赋予了索引的功能。
创建多列索引,需要遵循BTree类型,
即第一列使用时,才启用索引。
在上面的创建语句中,只有mysql语句在使用到StudentNo字段时,索引才会被启用。
如: select * from Student where StudentNo = 1000; #使用到了StudentNo字段,索引被启用。
可以使用explain检测索引是否被启用
如:explain select * from Student where StudentNo = 1000;
5、选择唯一性索引
唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。例如,学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。如果使用姓名的话,可能存在同名现象,从而降低查询速度。
6、尽量使用数据量少的索引
如果索引的值很长,那么查询的速度会受到影响。例如,对一个CHAR(100)类型的字段进行全文检索需要的时间肯定要比对CHAR(10)类型的字段需要的时间要多。
7、尽量使用前缀来索引
如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT和BLOG类型的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。
8、删除不再使用或者很少使用的索引.
表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响
B+ tree树索引, B tree, 散列
2.Mysql有哪些存储引擎??都有啥区别??要详细??
InnoDB: 事务型存储引擎, 并且有较高的并发读取频率
MEMORY: 存储引擎,存放在内存中,数据量小, 速度快
Merge:
ARCHIVE: 归档, 有很好的压缩机制
3.设计高并发系统数据库层面该如何设计?? 数据库锁有哪些类型??如何实现??
1. 分库分表: 同样量的数据平均存储在不同数据库相同表(或不同表)中,减轻单表压力,如果还是很大,就可以每个库在分多张表,根据hash取值或者其他逻辑判断将数据存储在哪张表中
2. 读写分离: 数据库原本就有主从数据库之分,查询在从服务器,增删改在主服务器,
3. 归档和操作表区分: 建一张归档表,将历史数据放入,需要操作的表数据单独存储
4. 索引啊之类的创建,对于数据量很大,百万级别以上的单表,如果增删改操作不频繁的话, 可以创建bitMap索引,速度要快得多
1. 共享锁:要等第一个人操作完,释放锁,才能操作
2. 更新锁:解决死锁,别人可以读,但不能操作
3. 排他锁:读写都被禁用
4. 意向锁(xlock): 对表中部分数据加锁,查询时,可以跳过
5. 计划锁: 操作时,别的表连接不了这张表,
4.数据库事务有哪些??
1. 原子性: 所有操作要么全部成功,要么全部失败
2. 一致性: 例如转账,一个事务执行前和执行后必须一致
3. 隔离性: 防止脏读, 重复读问题
4. 持久性: 永久性提交数据库
5. Oracle常用函数有哪些??
Concat: 字符串拼接, 或者 ||
Instr: 指定字符串位置
Length: 长度
Trim: 去空格
Lower: 小写
Upper:大写
Nvl: 判断空
Replace: 替换
Substr: 截取
Floor: 向下取整
To_number:
To_char:
To_date:
Decode: 判断函数等等
6. Sql中哪些情况可能不会走索引??
1. 查询谓词没有使用索引的主要边界,换句话说就是select *,可能会导致不走索引
2. 单键值的b树索引列上存在null值,导致COUNT(*)不能走索引。索引列存在空值
3. 索引列上有函数运算,导致不走索引
4. 隐式类型转换导致不走索引。
5. 表的数据库小或者需要选择大部分数据,不走索引
6. !=或者<>(不等于),可能导致不走索引
7. 表字段的属性导致不走索引,字符型的索引列会导致优化器认为需要扫描索引大部分数据且聚簇因子很大,最终导致弃用索引扫描而改用全表扫描方式,
8. 使用like, in 等, 可能导致不走索引
分库分表
1.如何设计可以动态扩容缩容的分库分表方案?? 2.用过哪些分库表中间件, 有啥优缺点??讲一下你了解的分库分表中间件的底层实现原理?? 3.我现在有一个未分库分表的系统,以后系统需分库分表,如何设计,让未分库分表的系统动态切换到分库分表的系统上?? 4.分布式事务知道么? 你们怎么解决的?? TCC ?? 那若吃醋先网络原因,网络连不通怎么办?? 5.为什么要分库分表?? 6.分布式寻址方式都有哪些算法?? 知道一致性hash吗??手写一下java实现代码??你若userId取摸分片,那我要查一段连续时间里的数据怎么办? 7.如何解决分库分表主键问题??有什么实现方案?
分布式缓存
1.Redis和memcheched 什么区别??为什么单线程的redis比多线程的memched效率要高?? 2.Redis有什么数据类型??都在哪些场景下使用?? 3.redis主从复制是怎么实现的??Redis的集群模式是如何实现的??redis的key是如何寻址的?? 4.使用redis如何设计分布式锁, 使用zk可以么??如何实现??这两种那个效率更高?? 5.知道redis的持久化么?? 都有什么优缺点??具体底层实现呢?? 6.Redis过期策略都有哪些??LRU??写一下java版本的代码??
分布式服务框架
1.说一下dubbo的实现过程??注册中心挂了可以继续通信么?? 2.Zk原理知道吗?Zk都可以干什么??Paxos算法知道吗??说一下原理和实现?? 3.Dubbo支持哪些序列号协议??Hessian??说一下hession的数据结构??PB知道吗??为啥PB效率是最高的?? 4.知道netty么??Netty可以干嘛啊??NIO, BIO, AIO都是什么啊?有什么区别么? 5.Dubbo复制均衡策略和高可用策略都有哪些??动态代理策略呢?? 6.为什么要进行系统拆分啊??拆分不用dubbo可以吗??Dubbo和thrift什么区别?? 7.Mq相关?
java基础相关
1. Hashcode相等,两个类相等么??equals 呢?? 相反呢??
不一定相等,都不一定相当。 反过来(equals相同,hashcode相同,则类相同)可能成立
Equals用来判断两个对象的值是否相等,
==用来判断两个对象的引用地址和值是否相等
2.介绍一下集合框架??
分两种:
Collection: 包含独立元素, 可以重复,无序的, 继承了iterable接口,有一个iterator方法返回迭代器对象, 相关接口有list, queue, set
Map: 包含键值对的元素,key不重复,value允许重复, 实现接口TreeMap, HashMap, LinkedhashMap, concurrentHashMap, hasTable
3.Hashmap , hashtable底层实现什么区别??Hashtable 和concurrenthashMap呢??
hashmap非线程安全的,key和value可以接收nul值
Hashtable线程安全的,不能接受null
单线程下,hashmap比hashtable快得多
concurrentHashMap融合了hashtable和hashmap的优点,写可以多线程写,并且是线程安全的,读线程不受限制
4.Hashmap 和treemap 什么区别??底层数据结构是什么??
Hashmap: 数组方式存储, 线程非安全,可存储null, 无序
Treemap: 基于二叉树实现方式, 线程非安全,不允许null, 存入元素需实现comparable接口, 自定义排序规则。
5.线程池用过么??都有什么参数??底层如何实现的??
核心池大小
线程池最大线程数
保持多久时间会终止
阻塞队列,存储等待执行的任务
;
Java通过Executors可以提供四种线程池
6.Sychnized和lock什么区别??Sychnize 什么情况,情况下是对象锁??什么时候是全局锁??为什么??
1.synchnized 是关键字,在方法出错之后由java虚拟机自动解除锁定,而 Lock属于Api级别的锁,它需要自己在 finally方法后面保证锁的释放。
2.synchnized 不能实现公平锁,而 Lock可以,Lock还可以添加多个监听条件来对锁进行控制,可以中断。
3.synchronized 作用在普通方法上属于对象锁,作用在静态方法,类.class 上面,属于全局锁。对象锁只对同一个对象加锁,作用对象是同一个对象。而类锁是对类加锁,对整个类都有效。
如果锁住的是一般方法就是对象锁,对象锁只会对同一个对象起作用,如果是锁住了static 方法则是全局锁,会对全局对象都管用,如果想在普通方法中使用全局锁需要锁住class对象。
7.ThreadLocal 是什么?? 底层如何实现??写一个例子呗??
ThreadLocal 底层是存储在 线程本地Map里面的一个对象,它跟当前线程绑定,以 ThreadLocal 对象本身为 key,以ThreadLocal里面存的值为值,目的是为了实现线程之间的数据的隔离。
public static final ThreadLocal session = new ThreadLocal();
public static Session getCurrentSession() {
Session s = (Session)session.get();
if(s == null){
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
8.Volitile的工作原理??
实例化一个对象其实可以分为三个步骤:
(1)分配内存空间。
(2)初始化对象。
(3)将内存空间的地址赋值给对应的引用。
多线程情况下,Volitile修饰的变量会被加一个lock指令,这个指令做两件事情,
1.在变量改变之后,会立刻从cpu高速缓存写到内存
2.会通知其他cpu缓存中的该变量的值设置成无效,用到该变量时会到内存中重新读取该变量的值。
Lock指令保证了缓存一致性原理
9.Cas知道么??如何实现的??
CAS(Compare and Swap)即比较并替换,实现并发算法时常用到的一种技术,
三个参数,比较是否相等,当内存值和预期值相等时,才会将第三个值更新,并返回true
10.请用至少四种写法, 写一个单例模式呗??
1. 实例静态类
2. volitile修饰,sychnized修饰
3. 枚举使用(推荐)
JVM
1.请介绍一下jvm内存模型?? 用过什么垃圾回收器?? 都说说吧?? 2.线上发送频繁full gc如何处理?? cpu使用率过高怎么办?? 如何定位问题??如何解决??说一下解决思路和处理方法?? 3.知道字节码么?字节码都有哪些?Integer x =5 , int y = 5 , 比较x == y 都需要经过哪些步骤?? 4.讲讲类加载机制呗??都有哪些类加载器??这写类加载器都加载哪些文件??手写一下类加载demo?? 5.知道osgi么??他是如何实现的?? 6.请问你做过哪些JVM优化??使用什么方法??达到什么效果?? 7.Class.forName(“java.lang.String”)和String.class.getClassLoader().loadClass(“java.lang.String”)有什么区别么??
Spring
1.spring都有哪些机制??AOP底层如何实现的??IOC呢?? 2.Cglib知道么??他和jdk动态代理什么区别??手写一个jdk动态代理吧??
分布式消息队列
1.为什么是用消息队列??消息队列有什么优点和缺点?? 2.如何保证消息队列的高可用性??如何保证消息不被重复消费?? 3.Kafka, activemq , rabbitmq, rocketmq 都有什么优缺点?? 4.如果让你写一个消息队列, 该如何进行架构设计??说一下你的思路
分布式搜索引擎
1.Es的工作过程实现是如何的?? 如何实现分布式的?? 2.Es在数据量很大的情况下(数十亿级别)如何提高查询效率?? 3.Es的查询是一个怎么的工作过程??底层lucence介绍一下??倒排索引知道么??Es和mongdb什么区别啊??都在什么场景下使用啊??
高并发高可用架构设计
1.如何设计一个高并发高可用系统?? 2.如何限流??工程中怎么做的??说一下具体实现?? 3.缓存如何使用的??缓存使用不当会造成什么后果?? 4.如何熔断啊??熔断框架都有哪些??具体实现原理知道吗?? 5.如何降级??如何进行系统拆分??如何数据库拆分??
通信协议
1.说一下TCP/IP四层?? 2.http的工作流程??http1.0, http1.1 , http2.0 具体哪些区别?? 3.Tcp三次握手, 四层分手的工作流程??画一下流程图??为什么不是四次五次或者二次啊?? 4.画一下https的工作原理??具体如何实现的,如何防止被抓包啊??
算法
1.比较简单, 我一个文件, 有45亿个阿拉伯数字, 如何进行去重啊??如何找出最大的那个数啊??
数据结构
1.二叉树和红黑数等??
java集合相关
请简要描述一下J2EE应用中的class loader的层次结构? java1.7 和1.8中,hashmap的区别 mybatis一级缓存,二级缓存,缓存策略 osi七层网络模型,五层网络模型,每次层分别有哪些协议 死锁产生的条件, 以及如何避免死锁,银行家算法,产生死锁后如何解决 如何判断链表有环 虚拟机类加载机制,双亲委派模型,以及为什么要实现双亲委派模型 虚拟机调优参数 拆箱装箱的原理 JVM垃圾回收算法 CMS G1 hashset和hashmap的区别,haspmap的底层实现put操作,扩容机制,currenthashmap如何解决线程安全,1.7版本以及1.8版本的不同md5加密的原理 有多少种方法可以让线程阻塞,能说多少说多少 synchronized和reetrantlock锁 AQS同步器框架,countdowmlatch,cyclebarrier,semaphore,读写锁
阿里内推Java二面题目
B-Tree索引,myisam和innodb中索引的区别 BIO和NIO的应用场景 讲讲threadlocal 数据库隔离级别,每层级别分别用什么方法实现,三级封锁协议,共享锁排它锁,mvcc多版本并发控制协议,间隙锁 数据库索引?B+树?为什么要建索引?什么样的字段需要建索引,建索引的时候一般考虑什么?索引会不会使插入、删除作效率变低,怎么解决? 数据库表怎么设计的?数据库范式?设计的过程中需要注意什么? 共享锁与非共享锁、一个事务锁住了一条数据,另一个事务能查吗? Spring bean的生命周期?默认创建的模式是什么?不想单例怎么办?
阿里内推Java三面题目
高并发时怎么限流 线程池的拒接任务策略 springboot的启动流程 集群、负载均衡、分布式、数据一致性的区别与关系 数据库如果让你来垂直和水平拆分,谁先拆分,拆分的原则有哪些(单表数据量多大拆) 最后谈谈Redis、Kafka、 Dubbo,各自的设计原理和应用场景
6,唤醒一个阻塞的线程 如因为Sleep,wait,join等阻塞,可以使用interrupted exception异常唤醒。
8,redis使用单线程模型,数据顺序提交,redis支持主从模式,mencache只支持一致性hash做分布式;redis支持数据落地,rdb定时快照和aof实时记录操作命令的日志备份,memcache不支持;redis数据类型丰富,有string,hash,set,list, sort set,而memcache只支持简单数据类型;memcache使用cas乐观锁做一致性。
9,Class.forName()将类加载到JVM,还会对类解释,执行static块,而ClassLoader也加载到JVM,但是不会执行static块,并且只有调用了new Instance方法才会调用构造函数。
10,java反射机制。 可以在运行时判断一个对象所属的类,构造一个类的对象,判断类具有的成员变量和方法,调用1个对象的方法。4个关键的类:Class,Constructor,Field,Method。 getConstructor获得构造函数/getDeclardConstructor; getField/getFields/getDeclardFields获得类所生命的所有字段;getMethod/getMethods/getDeclardMethod获得类声明的所有方法,正常方法是一个类创建对象,而反射是1个对象找到1个类。
11,Object类中的方法:clone(),但是使用该方法必须实现Java.lang.Cloneable接口,equal()方法判断引用是否一致,指向同一对象,即相等于==,只有覆写了equals()方法之后,才可以说不同。hashcode(),对象的地址, toString(), finalize()。
12,序列化和反序列化 序列化和反序列化即对象和字节序列间的转化,进程间传送文本图片音频等以二进制传送。JDK中ObjectOuputStream和ObjectInputStream为输出输入流,只有实现SeriaLizable/Externalizable接口的类才能被序列化。如Person对象传递给内存流使用DataConstractJsonSeralizer, MemoryStream stream = new MemoryStream(); DataConstractJsonSeralizer SER = new DataConstractJsonSeralizer(typeof(person)); ser.writeObjectStream(stream, person);显示json输出,StramReader sr = new StreamReader(stream1); sr.ReadToEnd()。
13,讲讲分布式唯一ID。 确定ID存储用64位,1个64位二进制1是这样的00000000.....1100......0101,切割64位,某段二进制表示成1个约束条件,前41位为毫秒时间,后紧接9位为IP,IP之后为自增的二进制,记录当前面位数相同情况下是第几个id,如现在有10台机器,这个id生成器生成id极限是同台机器1ms内生成2的14次方个ID。
分布式唯一ID = 时间戳 << 41位, int类型服务器编号 << 10,序列自增sequence。每个时间戳内只能生成固定数量如(10万)个自增号,达到最大值则同步等待下个时间戳,自增从0开始。将毫秒数放在最高位,保证生成的ID是趋势递增的,每个业务线、每个机房、每个机器生成的ID都是不同的。如39bit毫秒数|4bit业务线|2bit机房|预留|7bit序列号。高位取2016年1月1日1到现在的毫秒数,系统运行10年,至少需要10年x365天x24小时x3600秒x1000毫秒=320x10~9,差不多39bit给毫秒数,每秒单机高峰并发小于100,差不多7bit给每毫秒的自增号,5年内机房小于100台机器,预留2bit给机房,每个机房小于100台机器,预留7bit给每个机房,业务线小于10个,预留4bit给业务线标识。
64bit分布式ID(42bit毫秒+5bit机器ID+12位自增)等
生成分布式ID的方式:A,2个自增表,步长相互隔开 B,时间的毫秒或者纳秒 C,UUID D,64位约束条件(如上)
14,NIO和IO的区别
第一点,NIO少了1次从内核空间到用户空间的拷贝。
ByteBuffer.allocateDirect()分配的内存使用的是本机内存而不是Java堆上的内存,和网络或者磁盘交互都在操作系统的内核空间中发生。allocateDirect()的区别在于这块内存不由java堆管理, 但仍然在同一用户进程内。
第二点,NIO以块处理数据,IO以流处理数据
第三点,非阻塞,NIO1个线程可以管理多个输入输出通道
15,内存泄漏
未对作废数据内存单元置为null,尽早释放无用对象的引用,使用临时变量时,让引用变量在推出活动域后自动设置为null,暗示垃圾收集器收集;程序避免用String拼接,用StringBuffer,因为每个String会占用内存一块区域;尽量少用静态变量(全局不会回收);不要集中创建对象尤其大对象,可以使用流操作;尽量使用对象池,不再循环中创建对象,优化配置;创建对象到单例getInstance中,对象无法回收被单例引用;服务器session时间设置过长也会引起内存泄漏。
16,对象克隆和实现方式
克隆的对象可能包含一些已经修改过的属性,而new1个对象属性都还是初始化时候的值,被复制克隆的类要实现Clonable接口,覆盖clone()方法,访问修饰符为public,方法中调用super.clone()得到所需要的复制方法,类中的属性类也需要实现Clonable接口,覆写clone()方法,并在super中也调用子属性类的clone()复制,才可以实现深拷贝。
或者写到流中序列化的方式来实现,不必考虑引用类型中还包含引用类型,直接用序列化来实现对象的深复制拷贝,即将对象写到流,再从流中读出来,需要实现seriazation接口。
17,redis内存数据上升到一定大小会执行数据淘汰策略,redis提供了6种数据淘汰策略。
LRU:从已设置过期时间的数据集合中挑选最近最少使用的数据淘汰
random:从已设置过期时间的数据中挑选任意数据淘汰
ttl:从已设置过期时间的数据集合中挑选将要过期的数据淘汰。
notenvision:禁止驱逐数据
如mysql中有2千万数据,redis只存储20万的热门数据。LRU或者TTL都满足热点数据读取较多,不太可能超时特点。
redis特点:速度块,O(1),丰富的数据类型,支持事物原子性,可用于缓存,比memecache速度块,可以持久化数据。
常见问题和解决:Master最好不做持久化如RDB快照和AOF日志文件;如果数据比较重要,某分slave开启AOF备份数据,策略为每秒1次,为了主从复制速度及稳定,MS主从在同一局域网内;主从复制不要用图状结构,用单向链表更为稳定 M-S-S-S-S。。。。;redis过期采用懒汉+定期,懒汉即get/set时候检查key是否过期,过期则删除key,定期遍历每个DB,检查制定个数个key;结合服务器性能调节并发情况。
过期淘汰,数据写入redis会附带1个有效时间,这个有效时间内该数据被认为是正确的并不关心真实情况,例如对支付等业务采用版本号实现,redis中每一份数据都维持1个版本号,DB中也维持1份,只有当redis的与DB中的版本一致时,才会认为redis为有效的,不过仍然每次都要访问DB,只需要查询version版本字段即可。
18,异步化,生产接口每秒钟10万并发,消费者用异步慢慢消费。缓存模式空间换时间,把1两亿的数据名单打到缓存。服务降级,把不重要的任务放弃;静态资源离线包下载机制,在wify下会主动提前把静态下载前端层保护可请将用户请求延长,点击后主动给它随机等待2s的时间/2分钟之内不能请求;后端做部分接口的开关,设置超短耗时时间,原来只用5ms的只给20ms。
系统一段时间内会自动重试,重试多次后就认为是失败了,检查支付接口返回该订单的钱,支付操作如果回复错误则回滚扣库存的事务,没返回则会记录进行中pendding状态,结束整个过程,等通知失败/成功,AB系统之间会出现死循环补偿,如B退单不成功,一般就是记录错误日志了。超时每隔一段时间去定时回调服务定时回滚,一定次数还是超时则提示用户联系客服,订单库存可以不会滚,记录状态,如果一直调用支付不成功,则让用户自己去处理联系客服,可以不回滚用户的数据,金额扣了才算真正完成,是一种简单粗暴的做法。
公共配置抽象成存储到zookeeper配置中心或者redis等,DB也存储一份,各应用监听ZK的配置变化,可以建一个配置web管理页面。
19,dubbo用ProxyFactoty代理工厂将HelloServiceImpl封装成1个Inoke执行,即ProxyFactory.getInvoke(ref, (Class)接口,注册URL,解码参数),并将Invoke导出成1个Exporter,包括去注册中心ZK注册服务。Invoke有本地执行的Invoke,远程通信执行的Invoke。
20,每次扣减库存时加上1个请求流水编号,上层请求扣减库存没拿到结果的话,重新查询1次做重试操作,量不大都是加锁处理。减少锁的时间,牺牲幂等性,扣减为DB下地操作,查询扣减和设置合成1步,中间没有网络请求。利用缓存,通过写log记录操作,异步合并日志及更新,重启时cache失效,读log恢复,避免重复提交,写操作不建议重试快速失败。多个商品同时增减库存,可使用订单号做幂等处理,应用层对单个商品减库存,操作排队,商品消息ID路由在1个应用server处理,读本地缓存,失效再redis,DB采用乐观锁,组提交,1次减库存多个订单的购买量。可将同一个key下库存m分为n组k1......kn,每组数为m/n,扣减依次在各组扣减,减少并发冲突。队列装满后关闭队列进入,然后用户轮训自己是否抢到了异步ajax,用户资源队列固定长度。2个队列,1个销售的资源队列放入redis,有另外1个队列用来装抢购的会员的uid。
红包状态正常,并成功将状态改为“已领取”,且消息发送成功,用户端开始消费该消息,如果消费失败/超时,用MQ做重试做幂等,直到成功,每条消息有唯一编号且保证消息处理成功与去重表的日志同时出现。
热点将hot data拆分,分在不同库和不同表,分散热点Data,减轻DB并发更新热点带来RT升高和应用连接超时。SQL在mysql层加以限制,SQL超时/thradrunning到1定值则拒绝SQL执行,一定时间异步将结果写入DB,nginx对IP做限制,可能误杀。
21,SpringAOP,XML配置aop:config,切面aop:aspect切点aop:pointcut,连接切点和通知方法aop:before和aop:after等,注解可以直接使用@before执行方法@after ,@before(“pointcut()”) ,@after("pointcut"), @Aroud("excutete()),@AfteReturning,@AfterThrowing,可作日志事务,权限等待,AOP即通过把具体的类创建对应的 代理类,从代理类来对具体进行操作。
目标实现了接口,默认采用JDK实现AOP,也可以强制使用CGlib来实现AOP,目标没有实现接口的话,则必须采用CGlib,Spring自动在JDK和CGlib切换。如果要求spring强制使用CGlib实现AOP,则可以配置,添加Cglib库。。。jar, Spring配置文件中加入<aop:aspecj-autoproxy proxy-target-Class=true>
22,MyISM采用表级锁,对Myism表读不会阻塞读,会阻塞同表写,对Myism写则会阻塞读和写,即一个线程获得1个表的写锁后,只有持有锁的线程可以对表更新操作,其他线程的读和写都会等待。
InnoDB,采用行级锁,支持事务,例如只对a列加索引,如果update ...where a=1 and b=2其实也会锁整个表, select 使用共享锁,update insert delete采用排它锁,commit会把锁取消,当然select by id for update也可以制定排它锁。
23,实时队列采用双队列模式,生产者将行为记录写入Queue1,worker服务从Queue1消费新鲜数据,如果异常则写入Queue2(主要保存异常数据),RetryWorker会监听Queue2,消费异常数据,如果还未处理成功按照一定的策略等待或者将异常数据再写入Queue2,如果数据发生积压可以调整worker的消费游标,从最新数据重新开始消费,保证了最新data得到处理,中间未处理的一段则可以启动backupWorker指定起止游标在消费完指定区间的数据后,backupWorker会自动停止。
DB降级开关后,可直接写入redis(storm),同时将数据写入一份到Retry队列,在开启DB降级开关后消费Retry队列中的数据,从而把数据写入到mysql中,达到最终一致性。MYSQL切分为分片为2的N次方,例如原来分为两个库d0和d1均放在s0服务器上,s0同时有备机s1,扩容只要几步骤:确保s0到s1服务器同步顺利,没有明显延迟;s0暂时关闭读写权限;确保s1已经完全同步到s0更新;s1开放读写权限;d1的dns由s0切换到s1;s0开放读写权限。
24,DB的特性和隔离级别
4大特性:原子性,一致性,分离性,持久性
隔离级别:
读提交:写事务禁止读
读未提交:写事务允许读
可重复读:写事务禁止读事务,读禁止写
序列化:全部禁止
详细说明:读提交1个事务开始写则全部禁止其他事务访问该行。读未提交1个事务开始写则不允许其他事务同时写,但可以读。可重复读 读事务会禁止写事务,写事物则禁止其他任何事务。序列化性能最低,全部禁止,串行执行。 MYSQL默认的是可重复读。
25,帖子服务、元数据服务、帖子搜索服务,提供索引数据存储,tid和uid查询直接从帖子服务从元数据返回,其他检索查询有帖子搜索服务从索引数据检索并返回,帖子服务增删改查用MQ同步到帖子搜索服务,搜索服务修改索引的数据(索引树,倒排表),索引表t_mapping(tid,uid)。
300亿数据在全量索引库中,数百万一天内修改过的数据在一天库中,50万小时内修改过的数据在小时库中,在update请求时,只会操作最低级别的索引例如小时库。小时库,1小时合并一次,合并到天库,天库一天合并1次,合并到全量库中。
26,讲一下NIO和网络传输
NIO Reactor反应器模式,例如汽车是乘客访问的实体reactor,乘客上车后到售票员处Acceptor登记,之后乘客便可休息睡觉了,到达乘客目的地后,售票员Aceptor将其唤醒即可。持久TCP长链接每个client和server之间有存在一个持久连接,当CCU(用户并发数量)上升,阻塞server无法为每个连接运行1个线程,自己开发1个二进制协议,将message压缩至3-6倍,传输双向且消息频率高,假设server链接了2000个client,每个client平均每分钟传输1-10个message,1个messaged的大小为几百字节/几千字节,而server也要向client广播其他玩家的当前信息,需要高速处理消息的能力。Buffer,网络字节存放传输的地方,从channel中读写,从buffer作为中间存储格式,channel是网络连接与buffer间数据通道,像之前的socket的stream。
27,缓存击透
预加载;
加载DB时同步,其他则等待;
DB端做SQL合并,Queue合并排队处理;
部分缓存设置为永不过期;
先清除缓存,读取数据时候则等待500ms,500ms缓存应该已经加载完成;
采用双key缓存,A1为原始缓存,A2为拷贝缓存;
如果DB为空null则g给redis设置1个NFC空nei容。
28,Dubbo源码使用了哪些设计模式
A,工厂模式,ExtenstionLoader.getExtenstionLoader(Protocol.class).getAdaptiveExtenstion()
B,装饰器模式+责任链,以provider的调用链为例,具体调用链代码是在protocolFilterWrapper的buildInvokeChain完成的,将注解中含有group=provider的Filter实现,调用顺序为EchoFilter -> ClassLoaderFilter -> GenericFilter -> ContextFilter -> ExceptionFilter -> TimeoutFilter -> MonitorFilter -> TraceFilter。装饰器模式和责任链混合使用,Echo是回声测试请求,ClassLoaderFilter则只是在其主功能上添加了功能。
C,观察者模式,provider启动时需要与注册中心交互,先注册自己的服务,再订阅自己的服务,订阅时采用了观察者模式,注册中心每5s定时检查是否有服务更新,有更新则向服务提供者发送1个notify消息后即可运行NotifyListener的notity方法,执行监听器方法。
D,动态代理模式。 扩展JDK的ExtensionLoaderdeAdaptive实现,根据调用阶段动态参数决定调用哪个类,生成代理类的代码是ExtensionLoader的createAdaptiveExtenstionClassLoader方法。
29,平衡二叉树,左右高度之差不超过1,Add/delete可能造成高度>1,此时要旋转,维持平衡状态,避免二叉树退化为链表,让Add/Delete时间复杂度但控制在O(log2N),旋转算法2个方法,1是求树的高度,2是求2个高度最大值,1个空树高度为-1,只有1个根节点的树的高度为0,以后每一层+1,平衡树任意节点最多有2个儿子,因此高度不平衡时,此节点的2棵子树高度差为2。例如单旋转,双旋转,插入等。
红黑树放弃完全平衡,追求大致平衡,保证每次插入最多要3次旋转就能平衡。
30,多线程同步锁
A,RentrantLock,可重入的互斥锁,可中断可限时,公平锁,必须在finally释放锁,而synchronize由JVM释放。可重入但是要重复退出,普通的lock()不能响应中断,lock.lockInterruptbly()可响应中断,可以限时tryLock(),超时返回false,不会永久等待构成死锁。
B,Confition条件变量,signal唤醒其中1个在等待的线程,signalall唤醒所有在等待的线程await()等待并释放锁,与lock结合使用。
C,semaphore信号量,多个线程比(额度=10)进入临界区,其他则阻塞在临界区外。
D,ReadWriteLock,读读不互斥,读写互斥,写写互斥。
E,CountDownLantch倒数计时器,countdown()和await()
F,CyCliBarrier
G,LockSupport,方法park和unpark
31,栈溢出的原因
是否递归的调用;大量循环;全局变量是否过多;数组,List,Map数据是否过大;用DDMS工具检查地方。
内存溢出的原因
过多使用了static;static最好只用int和string等基本类型;大量的递归或者死循环;大数据项的查询,如返回表的所有记录,应该采用分页查询。检查是否有数组、List、map中存放的是对象的引用而不是对象,这些引用会让对应对象不能被释放。
栈过大会导致内存占用过多,频繁页交换阻碍效率。
32,说一下http/2
Http/2采用二进制格式而不是文本
Http/2是完全多路复用的,而非有序并阻塞的。
Http/2使用报头压缩
Http/2让服务器可以将响应主动推送到客户端缓存中。
33,说一下内存泄露
A,HashMap,vector等容易(静态集合类), 和应用程序生命周期一样,所引用的所有对象Object也不能释放。
B,当集合类里面的对象属性被修改后,再调用remove()不起作用,hashcode值发生了改变
C,其对象add监听器,但是往往释放对象时忘记去删除这些监听器
D,各种连接记得关闭
E,内部类的引用
F,调用其他模块,对象作用参数
G,单例模式,持有外部对象引用无法收回。
内存泄露例子
Vector<String> A = new Vector<String>();
for(int i = 0; i < 100; i++){
Object o = new Object ();
A.add(o);
o = null;
}
........
内存溢出的例子
StringBuffer b = new StringBuffer ();
for(int i =0; i < 100; i++){
for(int j =0; i < 100; j++){
b.append(*);
}
}
34,SpirngMVC的生命周期 和 SpringBean的生命周期
SpirngMVC的生命周期 :
A,DispatcherSerlvet(前端控制器)
B,-》 HandlerMapping(处理器映射器),根据xml注解查找对应的Hander -》 返回Handler
C,-》处理器适配器去执行Handler
D,-》Handler执行完成后给处理器适配器返回ModelAndView
E,-》前端控制器请求视图解析器去执行视图解析,根据逻辑视图名解析成真正的视图JSP,向前端控制器返回view
F,-》前端控制器进行视图渲染,将模型数据放到request-》返回给用户
SpringBean的生命周期:
Instance实例化-》设置属性值-》调用BeanNameAware的setBeanName方法-》调用BeanPostProsessor的预初始化方法-》调用InitializationBean的afterPropertiesSet()的方法-》调用定制的初始化方法callCustom的init-method-》调用BeanPostProsessor的后初始化方法-》Bean可以使用了 -》 容器关闭-》 调用DisposableBean的destroy方法-》调用定制的销毁方法CallCustom的destroy-method。
35,AQS,抽象队列同步器
AQS定义2种资源共享方式:独占与share共享
独占:只能有1个线程运行
share共享:多个线程可以同p执行如samphore/countdownlanch
AQS负责获取共享state的入队和/唤醒出队等,AQS在顶层已经实现好了,AQS有几种方法:acquire()是独占模式下线程共享资源的顶层入口,如获取到资源,线程直接返回,否则进入等待队列,直到获取到资源为止。tryAcquire()将线程加入等待队列的尾部,并标志为独占。acquireQueued()使线程在等待队列中获取资源,一直到获取资源后不返回,如果过程被中断也返回true,否则false。
线程在等待过程中被中断是不响应的,获取资源才补上中断。将线程添加到队列尾部用了CAS自旋(死循环直到成功),类似于AutomicInteger的CAS自旋volatile变量。
start->tryAcquire -> 入队 -> 找安全点 -> park等待状态 -> 当前节点成对头 -> End
36,单例模式的7种写法
懒汉2种,枚举,饿汉2种,静态内部类,双重校验锁(推荐)。
37,lucence倒排索引
三个文件:字典文件,频率文件,位置文件。词典文件不仅保存有每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信息。
field的概念,用于表达信息所在位置(如标题中,文章中,url中),在建索引中,该field信息也记录在词典文件中,每个关键词都有一个field信息(因为每个关键字一定属于一个或多个field)。
关键字是按字符顺序排列的(lucene没有使用B树结构),因此lucene可以用二元搜索算法快速定位关键词。
假设要查询单词 “live”,lucene先对词典二元查找、找到该词,通过指向频率文件的指针读出所有文章号,然后返回结果。词典通常非常小,因而,整个过程的时间是毫秒级的。
对词典文件中的关键词进行了压缩,关键词压缩为<前缀长度,后缀>,例如:当前词为“阿拉伯语”,上一个词为“阿拉伯”,那么“阿拉伯语”压缩为<3,语>。对数字的压缩,数字只保存与上一个值的差值。
38,ZooKeeper分布式高可用
ZooKeeper 运行期间,集群中至少有过半的机器保存了最新数据。集群超过半数的机器能够正常工作,集群就能够对外提供服务。
zookeeper可以选出N台机器作主机,它可以实现M:N的备份;keepalive只能选出1台机器作主机,所以keepalive只能实现M:1的备份。
通常有以下两种部署方案:双机房部署(一个稳定性更好、设备更可靠的机房,这个机房就是主要机房,而另外一个机房则更加廉价一些,例如,对于一个由 7 台机器组成的 ZooKeeper 集群,通常在主要机房中部署 4 台机器,剩下的 3 台机器部署到另外一个机房中);三机房部署(无论哪个机房发生了故障,剩下两个机房的机器数量都超过半数。在三个机房中都部署若干个机器来组成一个 ZooKeeper 集群。假设机器总数为 N,各机房机器数:N1 = (N-1)/2 ,N2=1~(N-N1)/2 ,N3 = N - N1 - N2 )。
水平扩容就是向集群中添加更多机器,Zookeeper2种方式(不完美),一种是集群整体重启,另外一种是逐台进行服务器的重启。
39,如何将数据分布在redis第几个库?
答:redis 本身支持16个数据库,通过 数据库id 设置,默认为0。 例如jedis客户端设置。一:JedisPool(org.apache.commons.pool.impl.GenericObjectPool.Config poolConfig, String host, int port, int timeout, String password, int database); 第一种通过指定构造函数database字段选择库,不设置则默认0库。二:jedis.select(index);调用jedis的select方法指定。
40,类加载器的双亲委派加载机制?
答:当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
41,kafka高性能的原因?
答:
A,Broker NIO异步消息处理,实现了IO线程与业务线程分离;
B,磁盘顺序写;
C, 零拷贝(跳过用户缓冲区的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到用户态缓冲区);
D,分区/分段(每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力);
F,批量发送 (可以指定缓存的消息达到某个量的时候就发出去,或者缓存了固定的时间后就发送出去,大大减少服务端的I/O次数)
E,数据压缩
42,幂等的处理方式?
答:一、查询与删除操作是天然幂等
二、唯一索引,防止新增脏数据
三、token机制,防止页面重复提交
四、悲观锁 for update
五、乐观锁(通过版本号/时间戳实现, 通过条件限制where avai_amount-#subAmount# >= 0)
六、分布式锁
七、状态机幂等(如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。)
八、select + insert(并发不高的后台系统,或者一些任务JOB,为了支持幂等,支持重复执行)
43,HTTPS工作流程? a、客户端发送自己支持的加密规则给服务器,代表告诉服务器要进行连接了 b、服务器从中选出一套加密算法和hash算法以及自己的身份信息(地址等)以证书的形式发送给浏览器,证书中包含服务器信息,加密公钥,证书的办法机构 c、客户端收到网站的证书之后要做下面的事情: c1、验证证书的合法性 c2、如果验证通过证书,浏览器会生成一串随机数作为密钥K,并用证书中的公钥进行加密 c3、用约定好的hash算法计算握手消息,然后用生成的密钥K进行加密,然后一起发送给服务器 d、服务器接收到客户端传送来的信息,要求下面的事情: d1、用私钥解析出密码,用密码解析握手消息,验证hash值是否和浏览器发来的一致 d2、使用密钥加密消息,回送 如果计算法hash值一致,握手成功
44,RabbitMQ消息堆积怎么处理?
答:
增加消费者的处理能力(例如优化代码),或减少发布频率 单纯升级硬件不是办法,只能起到一时的作用 考虑使用队列最大长度限制,RabbitMQ 3.1支持 给消息设置年龄,超时就丢弃 默认情况下,rabbitmq消费者为单线程串行消费,设置并发消费两个关键属性concurrentConsumers和prefetchCount,concurrentConsumers设置的是对每个listener在初始化的时候设置的并发消费者的个数,prefetchCount是每次一次性从broker里面取的待消费的消息的个数 建立新的queue,消费者同时订阅新旧queue 生产者端缓存数据,在mq被消费完后再发送到mq 打破发送循环条件,设置合适的qos值,当qos值被用光,而新的ack没有被mq接收时,就可以跳出发送循环,去接收新的消息;消费者主动block接收进程,消费者感受到接收消息过快时主动block,利用block和unblock方法调节接收速率,当接收线程被block时,跳出发送循环。 新建一个topic,partition是原来的10倍;然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue;接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据;等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息; 45,RabbitMQ的消息丢失解决方案?
答:
消息持久化:Exchange 设置持久化:durable:true;Queue 设置持久化;Message持久化发送。 ACK确认机制:消息发送确认;消息接收确认。 46,负载均衡算法?
常见6种负载均衡算法:轮询,随机,源地址哈希,加权轮询,加权随机,最小连接数。
nginx5种负载均衡算法:轮询,weight,ip_hash,fair(响应时间),url_hash
dubbo负载均衡算法:随机,轮询,最少活跃调用数,一致性Hash
47,JVM内存区域划分?
答:
堆:Java中的堆是用来存储对象本身的以及数组(当然,数组引用是存放在Java栈中的),是Java垃圾收集器管理的主要区域。堆是被所有线程共享的,在JVM中只有一个堆。 虚拟机栈:虚拟机栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表、操作数栈、指向当前方法所属的类的运行时常量池的引用、方法返回地址和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。 本地方法栈:本地方法栈则是为执行本地方法(Native Method)服务的,在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一 方法区:方法区与堆一样,是被线程共享的区域。方法区存储了类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。当方法区无法满足内存分配需求时,则抛出OutOfMemoryError异常。在HotSpot虚拟机中,用永久代来实现方法区,将GC分代收集扩展至方法区,但是这样容易遇到内存溢出的问题。JDK1.7中,已经把放在永久代的字符串常量池移到堆中。JDK1.8撤销永久代,引入元空间。 程序计数器(线程私有):是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行java方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果还是Native方法,则为空。 直接内存:在JDK1.4中新加入的NOI类,引入了一种基于通道与缓冲区的I/O方式,它可以使用Native函数直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。 48,jvm YGC和FGC发生的具体场景?
答:
正在处理的实现事务功能,下次自动回滚。
队列实现持久化储存,下次启动自动载入。
添加标志位,未处理 0,处理中 1,已处理 2。每次启动的时候,把所有状态为 1 的,置为 0。
关键性的应用就给电脑配个 UPS。
YGC :对新生代堆进行gc。频率比较高,因为大部分对象的存活寿命较短,在新生代里被回收。性能耗费较小。 FGC :全堆范围的gc。默认堆空间使用到达80%(可调整)的时候会触发fgc。以我们生产环境为例,一般比较少会触发fgc,有时10天或一周左右会有一次。
YGC发生场景:edn空间不足 FGC发生场景:old空间不足,perm空间不足,调用方法System.gc() ,ygc时的悲观策略, dump live的内存信息时(jmap –dump:live)
49,一个线程池正在处理服务如果忽然断电该怎么办?
答:
队列实现持久化储存,下次启动自动载入。 但是实际需要看情况,大体思路是这样。 添加标志位,未处理 0,处理中 1,已处理 2。每次启动的时候,把所有状态为 1 的,置为 0。或者定时器处理 关键性的应用就给电脑配个 UPS。
50,SpringBoot的优点?
答:
快速构建项目,极大的提高了开发、部署效率。 对主流开发框架的无配置集成。 项目可独立运行,无须外部依赖Servlet容器。 提供运行时的应用监控。
51,DoS,DDoS,DRDoS攻击分别是什么?
答:DoS是Denial of Service的简写就是拒绝服务。
DDoS就是Distributed Denial of Service的简写就是分布式拒绝服务。
DRDoS就是Distributed Reflection Denial of Service的简写,分布反射式拒绝服务。
DoS、DDos以及DRDoS攻击手段和防范措施 52,服务限流的方式?
答:
漏桶:水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求。 令牌桶算法:系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token,如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token就拒绝服务。 基于redis实现的限流:假设每分钟访问次数不能超过10次,在Redis中创建一个键,过期60秒,对此服务接口的访问就把键值加1,在60秒内增加到10的时候,禁止访问服务接口。 计数器,滑动窗口 53,Quartz实现原理?
答:A、scheduler是一个计划调度器容器(总部),容器里面可以盛放众多的JobDetail和trigger,当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去执行。 B、JobDetail是一个可执行的工作,它本身可能是有状态的。 C、Trigger代表一个调度参数的配置,什么时候去调。 D、当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的作业(JobDetail和Trigger所组成的一对儿),就可以伴随容器启动而调度执行了。 E、scheduler是个容器,容器中有一个线程池,用来并行调度执行每个作业,这样可以提高容器效率。
54,数据库的锁?
答:行锁(共享锁和排他锁),表锁,页级锁,页级锁,意向锁,读锁,写锁,悲观锁,乐观锁等
55,简述ThreadPoolExecutor内部工作原理?
答:
先查看当前运行状态,如果不是RUNNING 状态会拒绝执行任务,如果是RUNNING状态,就会查看当前运行的线程数量,如果小于核心线程数,会创建新的线程来执行这个任务,如果不小于核心线程,会将这个任务放到阻塞队列去等代执行,直到上一个任务执行完再来执行这个任务。如果失败会创建一个非核心线程来执行这个任务如果当前线程数大于最大线程数,会直接拒绝该任务。
56,聚集索引和非聚集索引的区别?
答:
聚集索引: 索引中键值的逻辑顺序决定了表中相应行的物理顺序(索引中的数据物理存放地址和索引的顺序是一致的),可以这么理解:只要是索引是连续的,那么数据在存储介质上的存储位置也是连续的。 比方说:想要到字典上查找一个字,我们可以根据字典前面的拼音找到该字,注意拼音的排列时有顺序的。 聚集索引就像我们根据拼音的顺序查字典一样,可以大大的提高效率。在经常搜索一定范围的值时,通过索引找到第一条数据,根据物理地址连续存储的特点,然后检索相邻的数据,直到到达条件截至项。 非聚集索引 索引的逻辑顺序与磁盘上的物理存储顺序不同。非聚集索引的键值在逻辑上也是连续的,但是表中的数据在存储介质上的物理顺序是不一致的,即记录的逻辑顺序和实际存储的物理顺序没有任何联系。索引的记录节点有一个数据指针指向真正的数据存储位置。 总结如下: 如果一个主键被定义了,那么这个主键就是作为聚集索引 如果没有主键被定义,那么该表的第一个唯一非空索引被作为聚集索引 如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,改列的值会随着数据的插入自增。 InnoDB引擎会为每张表都加一个聚集索引,而聚集索引指向的的数据又是以物理磁盘顺序来存储的,自增的主键会把数据自动向后插入,避免了插入过程中的聚集索引排序问题。如果对聚集索引进行排序,这会带来磁盘IO性能损耗是非常大的。
57,java并发包下有哪些类?
答:ConcurrentHashMap,ConcurrentSkipListMap,ConcurrentNavigableMap
CopyOnWriteArrayList
BlockingQueue,BlockingDeque (ArrayBlockingQueue,LinkedBlockingDeque,LinkedBlockingQueue,DelayQueue,PriorityBlockingQueue,SynchronousQueue)
ConcurrentLinkedDeque,ConcurrentLinkedQueue,TransferQueue,LinkedTransferQueue
CopyOnWriteArraySet,ConcurrentSkipListSet
CyclicBarrier,CountDownLatch
Lock(ReetrantLock,ReetrantReadWriteLock)
Atomic包
58,threadlocal为什么会出现oom?
答:ThreadLocal里面使用了一个存在弱引用的map, map的类型是ThreadLocal.ThreadLocalMap. Map中的key为一个threadlocal实例。这个Map的确使用了弱引用,不过弱引用只是针对key。每个key都弱引用指向threadlocal。 当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收。 但是,我们的value却不能回收,而这块value永远不会被访问到了,所以存在着内存泄露。因为存在一条从current thread连接过来的强引用。只有当前thread结束以后,current thread就不会存在栈中,强引用断开,Current Thread、Map value将全部被GC回收。最好的做法是将调用threadlocal的remove方法。
在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value,但是这些被动的预防措施并不能保证不会内存泄漏:
(1)使用static的ThreadLocal,延长了ThreadLocal的生命周期,可能导致内存泄漏。 (2)分配使用了ThreadLocal又不再调用get(),set(),remove()方法,那么就会导致内存泄漏,因为这块内存一直存在。
59,mysql数据库锁表怎么解决?
答:查询锁表信息 当前运行的所有事务 select * from information_schema.innodb_trx 当前出现的锁 select * from information_schema.innodb_locks 锁等待的对应关系 select * from information_schema.innodb_lock_waits
通过 select * from information_schema.innodb_trx 查询 trx_mysql_thread_id然后执行 kill 线程ID KILL 8807;//后面的数字即时进程的ID
60,java 判断对象是否是某个类的类型方法?
instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。 getClass判断,如o.getClass().equals(ClassA.class)。(使用instanceof来判断一个对象是不是属于某个类,但是有时候这个类是继承于一个父类的,所以,不能严格判断出是不是自己的类,而不是自己的父类。) 61,Spring+MyBatis实现读写分离简述?
答:
方案一:通过MyBatis配置文件创建读写分离两个DataSource,每个SqlSessionFactoryBean对象的mapperLocations属性制定两个读写数据源的配置文件。将所有读的操作配置在读文件中,所有写的操作配置在写文件中。 方案二:通过Spring AOP在业务层实现读写分离,在DAO层调用前定义切面,利用Spring的AbstractRoutingDataSource解决多数据源的问题,实现动态选择数据源 方案三:通过Mybatis的Plugin在业务层实现数据库读写分离,在MyBatis创建Statement对象前通过拦截器选择真正的数据源,在拦截器中根据方法名称不同(select、update、insert、delete)选择数据源。 方案四:通过spring的AbstractRoutingDataSource和mybatis Plugin拦截器实现非常友好的读写分离,原有代码不需要任何改变。推荐第四种方案 62,红黑树的特点?
答:(1)每个节点或者是黑色,或者是红色。 (2)根节点是黑色。 (3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!] (4)如果一个节点是红色的,则它的子节点必须是黑色的。 (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。[这里指到叶子节点的路径]
63,kafka消息会不会丢失?
答:Kafka消息发送分同步(sync)、异步(async)两种方式。默认是使用同步方式,可通过producer.type属性进行配置;Kafka保证消息被安全生产,有三个选项分别是0,1,-1。 通过request.required.acks属性进行配置: 0代表:不进行消息接收是否成功的确认(默认值); 1代表:当Leader副本接收成功后,返回接收成功确认信息; -1代表:当Leader和Follower副本都接收成功后,返回接收成功确认信息;
网络异常 acks设置为0时,不和Kafka集群进行消息接受确认,当网络发生异常等情况时,存在消息丢失的可能; 客户端异常 异步发送时,消息并没有直接发送至Kafka集群,而是在Client端按一定规则缓存并批量发送。在这期间,如果客户端发生死机等情况,都会导致消息的丢失; 缓冲区满了 异步发送时,Client端缓存的消息超出了缓冲池的大小,也存在消息丢失的可能; Leader副本异常 acks设置为1时,Leader副本接收成功,Kafka集群就返回成功确认信息,而Follower副本可能还在同步。这时Leader副本突然出现异常,新Leader副本(原Follower副本)未能和其保持一致,就会出现消息丢失的情况; 以上就是消息丢失的几种情况,在日常应用中,我们需要结合自身的应用场景来选择不同的配置。 想要更高的吞吐量就设置:异步、ack=0;想要不丢失消息数据就选:同步、ack=-1策略
64,kafka的leader副本选举?
答:如果某个分区patition的Leader挂了,那么其它跟随者将会进行选举产生一个新的leader,之后所有的读写就会转移到这个新的Leader上,在kafka中,其不是采用常见的多数选举的方式进行副本的Leader选举,而是会在Zookeeper上针对每个Topic维护一个称为ISR(in-sync replica,已同步的副本)的集合,显然还有一些副本没有来得及同步。只有这个ISR列表里面的才有资格成为leader(先使用ISR里面的第一个,如果不行依次类推,因为ISR里面的是同步副本,消息是最完整且各个节点都是一样的)。 通过ISR,kafka需要的冗余度较低,可以容忍的失败数比较高。假设某个topic有f+1个副本,kafka可以容忍f个不可用,当然,如果全部ISR里面的副本都不可用,也可以选择其他可用的副本,只是存在数据的不一致。
65,kafka消息的检索?
答:其实很简单主要是用二分查找算法,比如我们要查找一条offest=10000的文件,kafka首先会在对应分区下的log文件里采用二分查看定位到某个记录该offest =10000这条消息的log,然后从相应的index文件定位其偏移量,然后拿着偏移量到log里面直接获取。这样就完成了一个消息的检索过程。
66,RabbitMQ 集群方式?
答:
1)普通集群:
以两个节点(rabbit01、rabbit02)为例来进行说明。 rabbit01和rabbit02两个节点仅有相同的元数据,即队列的结构,但消息实体只存在于其中一个节点rabbit01(或者rabbit02)中。 当消息进入rabbit01节点的Queue后,consumer从rabbit02节点消费时,RabbitMQ会临时在rabbit01、rabbit02间进行消息传输,把A中的消息实体取出并经过B发送给consumer。所以consumer应尽量连接每一个节点,从中取消息。即对于同一个逻辑队列,要在多个节点建立物理Queue。否则无论consumer连rabbit01或rabbit02,出口总在rabbit01,会产生瓶颈。当rabbit01节点故障后,rabbit02节点无法取到rabbit01节点中还未消费的消息实体。如果做了消息持久化,那么得等rabbit01节点恢复,然后才可被消费;如果没有持久化的话,就会产生消息丢失的现象。
2)镜像集群:
在普通集群的基础上,把需要的队列做成镜像队列,消息实体会主动在镜像节点间同步,而不是在客户端取数据时临时拉取,也就是说多少节点消息就会备份多少份。该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。所以在对可靠性要求较高的场合中适用 由于镜像队列之间消息自动同步,且内部有选举master机制,即使master节点宕机也不会影响整个集群的使用,达到去中心化的目的,从而有效的防止消息丢失及服务不可用等问题
67,ElasticSearch如何解决深度分页的问题?
答:使用scroll(有状态)和search after(无状态)的游标方式。
68,java代码优化(一)
答:尽量指定类、方法的final修饰符。 尽量重用对象。 尽可能使用局部变量。 及时关闭流。 尽量减少对变量的重复计算。 尽量采用懒加载的策略,即在需要的时候才创建。 慎用异常。 不要在循环中使用try...catch...,应该把其放在最外层。 如果能估计到待添加的内容长度,为底层以数组方式实现的集合、工具类指定初始长度。 当复制大量数据时,使用System.arraycopy()命令。 乘法和除法使用移位操作。 循环内不要不断创建对象引用。 基于效率和类型检查的考虑,应该尽可能使用array,无法确定数组大小时才使用ArrayList。 尽量使用HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销。 不要将数组声明为public static final。
69,java代码优化(二)
答:尽量在合适的场合使用单例。 尽量避免随意使用静态变量。 及时清除不再需要的会话。 实现RandomAccess接口的集合比如ArrayList,应当使用最普通的for循环而不是foreach循环来遍历 使用同步代码块替代同步方法。 将常量声明为static final,并以大写命名。 不要创建一些不使用的对象,不要导入一些不使用的类。 程序运行过程中避免使用反射。 使用数据库连接池和线程池。 使用带缓冲的输入输出流进行IO操作。 顺序插入和随机访问比较多的场景使用ArrayList,元素删除和中间插入比较多的场景使用LinkedList。 不要让public方法中有太多的形参。 字符串变量和字符串常量equals的时候将字符串常量写在前面。 请知道,在java中if (i == 1)和if (1 == i)是没有区别的,但从阅读习惯上讲,建议使用前者。 不要对数组使用toString()方法。 不要对超出范围的基本数据类型做向下强制转型。
70,java代码优化(三)
答:公用的集合类中不使用的数据一定要及时remove掉。 把一个基本数据类型转为字符串,基本数据类型.toString()是最快的方式、String.valueOf(数据)次之、数据+""最慢 使用最有效率的方式去遍历Map。 对资源的close()建议分开操作。 对于ThreadLocal使用前或者使用后一定要先remove。 切记以常量定义的方式替代魔鬼数字,魔鬼数字的存在将极大地降低代码可读性,字符串常量是否使用常量定义可以视情况而定。 long或者Long初始赋值时,使用大写的L而不是小写的l,因为字母l极易与数字1混淆,这个点非常细节,值得注意。 所有重写的方法必须保留@Override注解。 推荐使用JDK7中新引入的Objects工具类来进行对象的equals比较,直接a.equals(b),有空指针异常的风险。 循环体内不要使用"+"进行字符串拼接,而直接使用StringBuilder不断append。 不捕获Java类库中定义的继承自RuntimeException的运行时异常类。 避免Random实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed导致的性能下降,JDK7之后,可以使用ThreadLocalRandom来获取随机数。 静态类、单例类、工厂类将它们的构造函数置为private。
71,单点登录原理与简单实现?
答:相比于单系统登录,sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。这个过程,也就是单点登录的原理,用下图说明
单点登录自然也要单点注销,在一个子系统中注销,所有子系统的会话都将被销毁,用下面的图来说明
72,MQ做数据同步也会造成不一致,又需要引入监控,实时计算2个集群的数据同步,做一致性同步。大部分来说,同步es和solr不要在代码中去同步,同步失败无法保证事务,而且业务耦合。可以使用Databug和cancel等工具去做代码解耦,MQ支持重试,存储失败后抛出异常下次再处理。数据做异构,对外服务时任意拼装,MYSQL在半同步复制上做了一些优化,保证了一致性,引入了诸如paxos等主流算法保证强一致性问题。 当DB(监听从库),binlog有变化,cancel监听到时候解析过滤发送MQ(表名字,主键等)到变化的实时从库中查询数据同步到ES聚合表,MQ可以重试,系统解耦。事务log挖掘县城会对DB的事务log监听,并把这些事件发布到消息代理。
73,分布式服务调用可以实现跟踪系统,可以在业务日志中添加调用链ID,各个环节RPC均添加调用时延,QPS等。
非业务组件应该少加入业务代码,服务调用采用买点,也会采用配置采样率方式,买点即当前节点的上下文信息,包含TraceId,RPCId,开始结束时间,类型,协议,调用方IP,端口,服务名等,以及其他异常信息,报文等扩展,日志采用离线+实时的如flume结合kafka等,应按照TraceId汇总日志后按RPCId顺序整理。
74,Sentinel 工作原理?
答:(1)每个 Sentinel 以每秒钟一次的频率向它所知的 Master,Slave 以及其他 Sentinel 实例发送一个 PING 命令; (2)如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线; (3)如果一个 Master 被标记为主观下线,则正在监视这个 Master 的所有 Sentinel 要以每秒一次的频率确认 Master 的确进入了主观下线状态; (4)当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认 Master 的确进入了主观下线状态,则 Master 会被标记为客观下线; (5)在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有 Master,Slave 发送 INFO 命令; 当 Master 被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次; (6)若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除; (7)若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。
监控( Monitoring ): Redis Sentinel 实时监控主服务器和从服务器运行状态; 自动故障转移:如果一个 master 不正常运行了,哨兵可以启动一个故障转移进程,将一个 slave 升级成为 master,其他的 slave 被重新配置使用新的 master,并且应用程序使用 Redis 服务端通知的新地址;
75,高性能统计UV的方式?
(1)使用redis的set集合
(2)使用redis的bitmap(注意内存消耗)
76,Hbase二级索引,索引海量数据实现方案?
答:
(1) 方案1:使用开源的hbase-indexer,是借助于hbase的WAL实现,不会影响hbase性能
https://blog.csdn.net/xiahoujie_90/article/details/53400044
(2) 方案2:基于ES自己实现,利用habse的协处理器实现,会影响hbase性能
关键注意点:因为数据是存在Hbase中,ES充当的是索引角色,所以在创建ES的mapping时,
应指定_source为enabled:false。关闭存储原始文档。
https://wenku.baidu.com/view/422722fdd0f34693daef5ef7ba0d4a7303766c71.html
77,Elasticsearch分片使用优化?
答:(1)拆分集群 对于存在明显分界线的业务,可以按照业务、地域使用不同集群,这种拆分集群的思路是非常靠谱的。对于我们的场景,已经按照地域拆分了集群,且同一地域的子业务间分界线不明显,拆分过多的集群维护成本较高。 (2)调整滚动周期 根据保留时长调整index滚动周期是最简单有效的思路。例如保留3天的数据按天滚动,保留31天的数据按周滚动,保留一年的数据按月滚动。合理的滚动周期,可以在存储成本增加不大的情况下,大幅降低分片数量。 对于我们的场景,大部分数据保留31天,在按周滚动的情况下,集群的总分片数可以下降到6.5w~个。 (3)合理设置分片数和副本数 除个别子业务压力较高外,大部分业务压力较小,合理设置单Index的分片数效果也不错。我们的经验是单个分片的大小在10GB~30GB之间比较合适,对于压力非常小的业务可以直接分配1个分片。其他用户可结合具体场景考虑,同时注意单分片的记录条数不要超过上限2,147,483,519。 在平衡我们的业务场景对数据可靠性的要求 及 不同副本数对存储成本的开销 两个因素之后,我们选择使用一主一从的副本策略。 目前我们集群单Index的平均分配数为3,集群的总分片数下降到3w~个。 (4)分片分配流程优化 默认情况下,ES在分配分片时会考虑分片relocation对磁盘空间的影响。在分片数较少时,这个优化处理的副作用不明显。但随着单机分片数量的上升,这个优化处理涉及的多层循环嵌套过程耗时愈发明显。可通过cluster.routing.allocation.disk.include_relocations: false关闭此功能,这对磁盘均衡程度影响不明显。 (5)预创建Index 对于单集群3w分片的场景,集中在每周某天0点创建Index,对集群的压力还是较大,且存储空间存在波动。考虑到集群的持续扩展能力和可靠性,我们采用预创建方式提前创建分片,并把按Index的创建时间均匀打散到每周的每一天。 (6)持续调整分片数 对于集群分片的调整,通常不是一蹴而就的。随着业务的发展,不断新增的子业务 或 原有子业务规模发生突变,都需要持续调整分片数量。 默认情况下,新增的子业务会有默认的分片数量,如果不足,会在测试阶段及上线初期及时发现。随着业务发展,系统会考虑Index近期的数据量、写入速度、集群规模等因素,动态调整分片数量。
###78,如何编写高质量代码151建议?
答:
一、Java开发中通用的方法和准则
不要在常量和变量中出现易混淆的字母;
莫让常量蜕变成变量;
三元操作符的类型务必一致;
避免带有变长参数的方法重载;
别让null值和空值威胁到变长方法;
覆写变长方法也要循规蹈矩;
警惕字增的陷阱;
不要让旧语法困扰你;
少用静态导入;
不要在本类中覆盖静态导入的变量和方法;
养成良好习惯,显示声明UID;
避免用序列化类在构造函数中为不变量赋值;
避免为final变量复杂赋值;
使用序列化类的私有方法巧妙解决部分属性持久化问题;
break万万不可忘;
易变业务使用脚本语言编写;
慎用动态编译;
避免instantceof非预期结果;
断言对决不是鸡肋;
不要只替换一个类;
二、基本类型
使用偶判断,不用奇判断;
用整数类型处理货币;
不要让类型默默转换;
边界,边界,还是边界;
不要让四舍五入亏了一方;
提防包装类型的null值;
谨慎包装类型的大小比较;
优先使用整型池;
优先选择基本类型;
不要随便设置随机种子;
三、类、对象及方法
在接口中不要存在实现代码;
静态变量一定要先声明后赋值;
不要覆写静态方法;
构造函数尽量简化;
避免在构造函数中初始化其他类;
使用构造代码块精炼程序;
使用静态内部类提供封装性;
使用匿名类的构造函数;
匿名类的构造函数很特殊;
让多重继承成为现实;
让工具类不可实例化;
避免对象的浅拷贝;
推荐使用序列化实现对象的拷贝;
覆写equals方法时不要识别不出自己;
equals应该考虑null值情景;
在equals中使用getClass进行类型判断;
覆写equals方法必须覆写hashCode方法;
推荐覆写toString方法;
使用package-info类为包服务;
不要主动进行垃圾回收;
四、字符串
推荐使用String直接量赋值;
注意方法中传递的参数要求;
正确使用String、StringBuffer、StringBuilder;
注意字符串的位置;
自由选择字符串拼接方法;
推荐在复杂字符串操作中使用正则表达式;
强烈建议使用UTF编码;
对字符串排序持一种宽容的心态;
五、数组和集合
性能考虑,数组是首选;
若有必要,使用变长数组;
警惕数组的浅拷贝;
在明确的场景下,为集合指定初始容量;
多种最值方法,适时选择;
避开基本类型数组转换列表陷阱;
asList方法产生的List对象不可更改;
不同的列表选择不同的遍历方法;
频繁插入和删除时使用LinkedList;
列表相等只需关心元素数据;
推荐使用subList处理局部列表;
生成子表后不要再操作原列表;
使用Comparator进行排序;
不推荐使用binarySearch对列表进行检索;
集合中的元素必须做到compareTo和equals同步;
集合运算时使用更优雅的方式;
使用shuffle大乱列表;
减少HashMap中元素的数量;
集合中的哈希码不要重复;
多线程使用Vector或者HashTable;
非稳定排序推荐使用List;
六、枚举和注解
推荐使用枚举定义常量;
使用构造函数协助描述枚举项;
小心switch带来的空值异常;
在switch的default代码块中增加AssertionError错误;
使用valueOf前必须进行校验;
用枚举实现工厂方法模式更简洁;
枚举项的数量限制在64个以内;
小心注解继承;
枚举和注解结合使用威力更大;
注意@Override不同版本的区别;
七、枚举和注解
Java的泛型是类型擦除的;
不能初始化泛型参数和数组;
强制声明泛型的实际类型;
不同的场景使用不同的泛型通配符;
警惕泛型是不能协变和逆变的;
建议采用的顺序是List<T>、List<?>、List<Object>;
严格限定泛型类型采用多重界限;
数组的真实类型必须是泛型类型的子类型;
注意Class类的特殊性;
适时选择getDeclaredXXX和getXXX;
反射访问属性或方法时将Accessible设置为true;
使用forName动态加载类文件;
动态加载不合适数组;
动态代理可以使代理模式更加灵活;
反射让模板方法模式更强大;
不需要太多关注反射效率;
八、异常
提倡异常封装;
采用异常链传递异常;
受检异常尽可能转化为非受检异常;
不要在finally块中处理返回值;
使用Throwable获取栈信息;
异常只为异常服务;
多使用异常,把性能问题放一边;
九、多线程和并发
不推荐覆写start方法;
启动线程前stop方法是不可靠的;
不使用stop方法停止线程;
线程优先级只使用三个等级;
使用线程异常处理器提升系统可靠性;
volatile不能保证数据同步;
异步运算多考虑使用Callable接口;
优先选择线程池;
适时选择不同的线程池来实现;
Lock与synchronized是不一样的;
预防线程死锁;
适当设置阻塞队列长度;
使用CountDownLatch协调子线程;
CyclicBarrier让多线程齐步走;
来源:oschina
链接:https://my.oschina.net/u/4379462/blog/3403297