阿里云内推一面挂
MYSQL数据库索引类型包括普通索引,唯一索引,主键索引与组合索引
普通索引,这是最基本的MySQL数据库索引,它没有任何限制。
唯一索引,它与前面的普通索引类似,不同的就是:MySQL数据库索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
为了进一步榨取MySQL的效率,就要考虑建立组合索引,符合“最左前缀”原则
2、聚簇索引
定义:数据库表的索引从数据存储方式上可以分为聚簇索引和非聚簇索引(又叫二级索引)两种。Innodb的聚簇索引在同一个B-Tree中保存了索引列和具体的数据,在聚簇索引中,实际的数据保存在叶子页中,中间的节点页保存指向下一层页面的指针。“聚簇”的意思是数据行被按照一定顺序一个个紧密地排列在一起存储。一个表只能有一个聚簇索引,因为在一个表中数据的存放方式只有一种。
优点:聚簇索引将索引和数据行保存在同一个B-Tree中,查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次查询(非覆盖索引的情况下)效率要高。聚簇索引将索引和数据行保存在同一个B-聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的。
3、如何查询SQL执行时间
show profiles
查看show profiles功能是否开启:show variables like "%pro%";
开启show profiles功能:set profiling = 1;
查看每条执行过的SQL语句的执行时间:show profiles;
查看一条SQL语句的详细执行时间:show profile for query Query_ID
4、explain执行结果都有什么字段
id:查询序号
select_type:查询类型
table:表名
partitions:匹配的分区
type:join类型
prossible_keys:可能会选择的索引
key:实际选择的索引
key_len:索引的长度
ref:与索引作比较的列
rows:要检索的行数(估算值)
filtered:查询条件过滤的行数的百分比
Extra:额外信息
5、多线程实现方式
答案:继承Thread类创建线程、实现Runnable接口创建线程、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的线程
6、HashMap实现原理
答案:
HashMap是一种Key-Value数据结构,线程不安全
以JDK8而言put方法逻辑
1)如果HashMap未被初始化过,则初始化
2)对key求hash值((n - 1) & hash),然后计算下标
3)如果没有产生hash冲突,直接放入桶中
4)如果产生hash碰撞,以链表形式链接到后面
5)如果链表长度超过阈值8,则把链表变成红黑树
6)如果链表长度低于6,就把红黑树转换回链表
7)如果节点已经存在就替换旧值
8)如果桶满了( capacity * 0.75),就需要resize扩容2倍capacity,重新进行hash,存储旧元素和新元素
7、了解其它的编程语言
答案:了解C/C++、Python
8、如何创建线程池
答案:
通过Executors创建不同的线程满足不同的场景
1)newFixedThreadPool(int nThreads)
指定工作数量的线程池
2)newCachedThreadPool
处理短时间工作任务的线程池
试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程
如果线程闲置的时间已超过阈值,则会被终止并移出缓存
系统长时间闲置的时候不会消耗资源
3)newSingleThreadExecutor
创建唯一的工作线程来执行任务,如果线程异常结束,会有另外线程取代它
4)newSingleThreadScheduledExecutor()与newScheduledThreadPool newScheduledThreadPool(int corePoolSize)都是进行定时或者周期性任务调度,两者区别在于单一线程还是多个线程
5)newWorkStealingPool(int parallelism)
内部会构建ForkJoinPool,利用working-stealing算法,并并行地处理任务
Fork/Join框架把大任务分割成若干个小任务并行执行,最终汇总每个小任务结果得到大任务结果
9、线程池拒绝策略
答案:当线程池表示拒绝处理任务时有四种策略:AbortPplicy(直接抛出异常,线程池默认拒绝策略)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃队列中最旧(队头)任务,并执行当前任务)、CallerRunsPolicy(不用线程池中的线程执行,用调用所在线程执行)
10、阻塞队列应用场景
阻塞队列是一个支持附加操作的队列。这两个附加操作是:在队列为空时。获取元素的线程会等待队列为非空;当队列满时,存储元素的线程会等队列可用。阻塞队列常用于生产者和消费者的场景,生成者是往队列里添加元素的线程,消费者是从队列里拿走元素的线程。
11、dubbo负载均衡
答案:
dubbo的负载均衡策略,主体向外暴露出来是一个接口,名字叫做loadBlace,接口下是一个抽象类AbstractLoadBlance,dubbo拥有4种负载均衡策略,分别是一致性Hash均衡算法、随机调用法、轮询法、最少活动调用法,四种算法都继承抽象类AbstractLoadBalance,重写doSelect方法。
ConsistentHashLoadBalance:
一致性Hash算法,doSelect方法进行选择。一致性Hash负载均衡涉及到两个主要的配置参数为hash.arguments与hash.nodes:当进行调用时候根据调用方法的哪几个参数生成key,并根据key来通过一致性hash算法来选择调用节点。例如调用方法invoke(Strings1,Strings2);若hash.arguments为1(默认值),则仅取invoke的参数1(s1)来生成hashCode。
RandomLoadBalance:
随机调用负载均衡,方法的细节就是首先遍历每个提供服务的机器,获取每个服务的权重,然后累加权重值,判断每个服务的提供者权重是否相同,如果每个调用者的权重不相同,并且每个权重大于0,那么就会根据权重的总值生成一个随机数,再用这个随机数,根据调用者的数量每次减去调用者的权重,直到计算出当前的服务提供者随机数小于0,就选择那个提供者!另外,如果每个机器的权重的都相同,那么权重就不会参与计算,直接选择随机算法生成的某一个选择,完全随机。
RoundRobinLoadBlance:
轮询调用,轮询调用的过程主要是维护了局部变量的一个LinkdesHashMap(有顺序的Map)去存储调用者和权重值的对应关系,然后遍历每个调用者,把调用者和当前大于0的权重值放进去,再累加权重值。还有一个全局变量的map,找到第一个服务调用者,首先是找到每个服务的key值和method,这里可以理解为标识第一个调用者的唯一key,然后再给它对应的值保证原子性的+1(AtomicPositiveInteger是原子的),再对这个值取模总权重,再每次对其权重值-1,知道它取模与总权重值等于0就选择该调用者,可以称之为"降权取模"(只是一种的计算层面,而不是真正降权)。总结:轮询调用并不是简单的一个接着一个依次调用,它是根据权重的值进行循环的
LeastActiveLoadBlance:
最少活跃数调用法:这个方法的主要作用根据服务的提供者的运行状态去选择服务器,主要的思路就是遍历每个调用者,然后获取每个服务器的运行状态,如果当前运行的运行状态小于最小的状态-1,把它保存在leastIndexs中的第一个位置,并且认定所有的调用者权重都相同,然后直接返回那个调用者(这里的逻辑是:找到最少活跃数(在代码层反应就是:active的值))。如果计算出的权重值和最少的权重值相同,那么把它保存在leastIndexs数组里面,累加权重值,如果当前的权重值不等于初始值firstWeight,那么就认定不是所有的调用者的权重不同。然后再遍历lestIndexs,取权重累加值的随机数生成权重偏移量,在累减它,到它小于0的时候返回那个调用者。如果这些都不符合,就从leastIndexs随机选一个index,返回那个调用者!
dubbo默认的负载均衡策略是随机调用法(@SPI注解)、@Reference可改变
12、框架或者JDK底层源码
13、阿里云表格存储
14、开源项目
15、生产消费模型(编程题)
答案:
自己使用ArrayBlockingQueue方式
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
public class Main {
public static void main(String[] args) {
ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<>(50);
Random random = new Random();
new Thread(()->{
while(true){
try {
Integer msg = abq.take();
System.out.println("Consume : " + msg);
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}).start();
while (true){
Integer msg = random.nextInt();
System.out.println("Produce : " + msg);
if (abq.offer(msg) == false) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
使用LinkedBlockingQueue方式:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ProducerConsumerPattern {
public static void main(String[] args) {
BlockingQueue bq = new LinkedBlockingQueue();
Thread producer = new Thread(new Producer(bq));
Thread consumer = new Thread(new Consumer(bq));
producer.start();
consumer.start();
}
}
class Producer implements Runnable {
private final BlockingQueue bq;
public Producer(BlockingQueue bq) {
this.bq = bq;
}
public void run() {
for (int i = 0; i < 10; i++) {
try {
System.out.println("Produced : " + i);
bq.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
private final BlockingQueue bq;
public Consumer(BlockingQueue bq) {
this.bq = bq;
}
public void run() {
while(true){
try {
Integer msg = (Integer)bq.take();
System.out.println("Consumed : " + msg);
System.out.println();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
使用Object的wait()和notify()方式:
import java.util.PriorityQueue;
import java.util.Random;
public class ProducerConsumerPattern {
static PriorityQueue<Integer> queue = new PriorityQueue<>(10);
static Random random = new Random();
static class Consumer extends Thread{
public void run() {
while (true){
synchronized (queue){
while (queue.size() == 0){
try {
queue.wait();
}catch (InterruptedException e){
e.printStackTrace();
queue.notify();
}
}
System.out.println(queue.poll());
queue.notify();
}
}
}
}
static class Producer extends Thread{
public void run() {
while (true){
synchronized (queue){
while (queue.size() == 10){
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.offer(random.nextInt(11));
queue.notify();
}
}
}
}
public static void main(String[] args) {
Thread producer = new Thread(new Producer());
Thread consumer = new Thread(new Consumer());
producer.start();
consumer.start();
}
}
使用Condition方式:
import java.util.PriorityQueue;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumerPattern {
private static PriorityQueue<Integer> queue = new PriorityQueue<>(10);
private static Random random = new Random();
private static Lock lock = new ReentrantLock();
private static Condition notFull = lock.newCondition();
private static Condition notEmpty = lock.newCondition();
static class Consumer extends Thread{
public void run() {
while (true){
lock.lock();
try{
while (queue.size() == 0){
try {
notEmpty.await();
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println(queue.poll());
notFull.signal();
}finally {
lock.unlock();
}
}
}
}
static class Producer extends Thread{
public void run() {
while (true){
lock.lock();
try{
while (queue.size() == 10){
try {
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(random.nextInt(11));
notEmpty.signal();
}finally {
lock.unlock();
}
}
}
}
public static void main(String[] args) {
Producer producer = new Producer();
Consumer consumer = new Consumer();
new Thread(producer).start();
new Thread(consumer).start();
}
}
#阿里巴巴##面经##校招##Java工程师#