Java方面技术点小整理

Java中的集合吗?

java 中的集合分为value、key-value 两种
存储值有分为list 和 set
List 有序的,可以重复
Set 是序的,不可以重复的
根据equals 和 hashCode判断如果一个对象要存储在set中,必须重写equals和hashCode的方法;存储key-value的为map

HashMap这个数据结构了解么?

HashMap在1.7 和1.8 做了比较大的改变 ,1.7之前使用的就是数组 + 链表,它数据节点是一个Entry 节点,它的一个内部类;1.7之前它的数据插入过程是使用了头插入,头插入法虽然效率比较高,但在resize拓容过程时,反复调用一个transfer的方法,把里面的一些Entry进行一个rehash,可能会造成链表的循环,就可能在下一次Get的时候出现一个死循环的情况;1.7没有加锁,也可能在多线程并发的情况下,数据不能保证它是一个安全的,就是我push的进去的值,取出来还是我push进去的一个值
jdk1.8 以后对它进行了一个比较大的变化,主要是把它变成了一个数组 + 链表/ 红黑树的这么一个结构。把原来一个Entry节点也变成了一个Node节点。
它的整个put的过程也做了优化,1.8是尾插入;当链表长度超过8时,升级为红黑树

说说HashMap的扩容机制吧

初始化这个HashMap,如果我们没有设置它的capacity,它的默认初始化容量是16,负载因子是0.75。它会计算出来一个threshold是一个它的一个阈值,就是一个扩容阈值,如果当我在put 的时候我会先判断,当前的这个size是不是要大于这个阈值,如果大于的时候,它就会扩容成原来的两倍,将原来的一个Entry 一个resize的这么一个过程

1.7是头插入法,线程不安全,那1.8它是安全的么?

HashMap的线程不安全体现在会造成死循环、数据丢失、数据覆盖这些问题。其中死循环和数据丢失是在JDK1.7中出现的问题,在JDK1.8中已经得到解决,然而1.8中仍会有数据覆盖这样的问题。

HashMap它线程不安全,你在日常开发中,你是怎么去保证它线程安全的?

我一般会使用ConcurrentHashMap这种线程安全的一个集合容器

线程安全的,比如hashTable,或者说我给它加synchronized、Lock,或者用Collection.Synchronized都可以对它进行一个同步的操作,为什么你选择ConcurrentHashMap?

第一个就是ConcurrentHashMap,它的并发是更高的,就是在我看来普通的hashTable是直接对里面的方法进行了一个Synchronized的就是加了对象类,但是concurrentHashMap数据结构就是1.8之后同样变成了数组 + 链表 / 红黑树,它只会锁住,就是我目前获取到的那个Entry所在的那个节点的一个值,在上锁的时候它使用了就是 CAS Synchronized,然后再加上JDK1.6以后对Synchronized 进行一个锁升级优化,所以它的效率更高的,就是它支持的并发度是更高

你能说一下Java 源码的HashMap底层实现原理么?

JDK1.8版本是:HashMap它底层是数据 + 链表(红黑树),数据存的是Hash的key,如有有hash冲突的话,就会放在数组对应的链表上,存储的时候会先算一个hashcode之后把这个key放在bucket上,value存在链表上获取的时候从bucket找,当hashcode相同时,用equal来判断需要获取的值

说一下JVM里面的内存结构吧

jvm内存结构按线程来讲可以分成两部分,一个是线程独占的,一个是线程共享的。先说一下线程共享的有方法区、堆;线程独占的有程序计数器、本地方法栈、虚拟机栈;另外方法区的话,它是Java虚拟机的一个模型规范,然后具体实现的话是元空间和永久代,永久代是1.7的,然后1.8 之后永久代就被移除了,然后它就变成元空间了;元空间的话它是发布在计算机内存的,它是脱离了这个Java虚拟机内存的,它是独立存在的

你知道字符串常量是放在哪里吗?

字符串常量池是放在堆里的

类文件常量池呢?

放在元空间里的

那你知道类加载有哪些过程吗?

它首先是要经过IO嘛,先经过ClassLoader去查询那个文件,然后文件的话它就开始进行类的加载了,类加载具体过程它其实分成很多步的:验证、准备、解析
就是把它加载到堆中和方法区的

那你知道有哪些类加载器吗?

启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)、系统类加载器(Application ClassLoader)

那你知道软引用跟弱引用有什么区别吗?

软引用就是在内存紧张时会被GC释放内存的
弱引用就是只要GC就会被回收内存

垃圾收集算法,你了解过吗?

  • 标记-清除:分为标记阶段、清除阶段;效率较高,但存在内存碎片问题,运行时间久了,内存碎片就越严重,导致它不能存一些大的对象
  • 复制算法:先标记哪些对象存活的,将内存分为两个大小相等的部分,可用内存减半,解决了产生内存碎片问题;缺点就是它会把内存分为两个部分,然后可用内存就是原来的二分之一了
  • 标记-整理:先判断哪些是垃圾,回收时会将存活对象移向一边。不会产生内存碎片,移动的代价比较高
  • 分代垃圾回收算法:就是前面几种的结合优点。

如果A和B 对象循环引用是否可以被GC?

可以,现在虚拟机通过GC Root根可达性分析算法,来判断对象是否存活,而不是通过简单的引用计数法来判断对象是否存活
可达性分析算法使用一系列GC Roots 对象,就是虚拟机栈引用的对象、静态属性引用对象、方法区常量引用的对象、本地方法栈引用的对象作为起点,向下搜索路径称为引用链,当一个对象到GC Roots 是没有任何引用链接则证明对象是不可用的

老年代一般采用什么垃圾回收算法?

标记-清除/标记-整理算法,因为它不常用嘛,但又不能产生垃圾碎片

年轻代垃圾回收算法

年轻代分为Eden/Survivor1/Survivor2三个区域,主要采用的是复制算法

年轻代为什么要用复制算法?

年轻化经常回收,经常进行回收的话它就不适合用那个标记-清除算法 和 标记-整理算法;标记整理算法,它移动对象是需要消耗性能的,所以说不适全那种算法的;然后在那个年轻代区已经做了改进了,并不是把整个内存分成两部分,它是分成三部分的,分别是Eden/Survivor1/Survivor2,它们的比例大概是8:1:1 这个是默认的,具体的话,参数可以设置调大,这样可用空间就会比以前更大了,因为它大部分的对象都是经过一次GC就会被清除掉,所以说eden就很大,然后就survivor1/survivor2就剩余的比较小,然后就是survivor区的对象年龄达到了16,也就是大于15,这个参数也是可以设置的,然后它就会进入老年代了

如果创建一个新对象,那么它一定是先分配在那个年轻代吗?

不是的,它有些很大的对象它会直接分配到老年代

你知道哪些垃圾收集器

知道有Serial/ParNew/Parallel Scavenge/CMS/SerialOld/Perallel Old/G1/ZGC

CMS 你说下它的收集过程吧

CMS 是缩写,C是concurrent,M是mark,S是sweep
concurrent 指它是多线程并发的垃圾收集器,然后mark-sweep 是说明它是进行了标记-清除算法

  • 初始标记:仅仅单线程标记GC Roots的直接关联对象,并且STW,这个过程非常短暂,可以忽略不计;
  • 并发标记:使用GC Roots Tracing算法,进行跟踪标记,不会STW;
  • 重新标记:因为之前并发标记,其他用户线程不暂停,可能产生了新垃圾,所以需要重新标记;
  • 清除垃圾:与用户线程并行执行垃圾回收,使用清除算法
  • CMS缺点:因为与用户工作程一起并发执行,所以会边清理,一边会产生新的垃圾

G1 垃圾收集器

G1 垃圾收集器将堆内存划分为不同的region,相当于很多很多小方块,每个Region 由Eden Space、Survivor Space、Old Space组成。
G1垃圾回收器,使用标记-整理算法,可以避免CMS标记-清除算法产生的内存碎片问题;在两个Region区域之间,则是使用复制算法。JDK8没有默认G1垃圾回收器,需要手动开启G1

Java 栈内存溢出,通常是由什么原因造成的?

栈生命周期与线程相同,执行的时候都会创建一个栈帧用来存储各种信息。栈溢出就是方法执行时创建的栈帧超过了栈的深度。一般递归调用可能会导致

一般怎么解决栈内存溢出问题?

一般的话就是调整jvm 栈的内存大小,然后要递归的时候注意一下

ThreadLocal知道么?

Thead 类里面都包含了一个Map,然后每个Map就对应了一个ThreadLocal,然后每次调用ThreadLocal的一个类的话就会调用那个ThreadMap里面的具体的那个ThreadLocal,这样的话,它每个线程都对应自己的一个Map,对应自己一个Local的那个对象,也是Object的东西

ThreadLocal 它的key跟value是什么?

Java 里的锁有了解吗?

我知道有Lock 和 Synchronized

Lock和Synchronized它们的使用方式和实现原理有什么区别?

  • Synchronized 用于方法和代块块可以锁对象,和类以及方法。
  • Lock 一般锁代码块,并且lock可以搭配condition使用
    至于原理的话,synchronized使用底层的mutex锁需要系统调用,而Lock的使用AQS的实现

Synchronized锁升级过程?

synchronized它JDK6之前是没有锁升级的过程,JAVA6后JVM对同步关键词进行优化,引入偏向锁、自旋锁、轻量级锁等概念。对象头中锁状态有无锁状态、偏向锁、轻量级锁和重量级锁。JDK6以前要么没有锁,要么全部给。
一开始是无锁的状态,就一上来,它会先去判断一下
当前线程获取到锁资源的这个线程,会优先让它再去获取到这个锁;如果它没有获取到这个锁,就升级成一个轻量级的,一个CAS的锁就是一个乐观锁,乐观锁的时候它是一个比较并交换的过程,如果这个CAS 如果没有设置成功的话,它会进行一个自旋;自旋到一定的次数之后才会升级成一个重量级锁。因为在大部分时间不是线程在并发,这样就保证了它的一个性能的问题

CAS会有什么问题么?

  • 会有ABA的问题
    问题描述:CAS是compare and sweep,先比较如果期待的值与当前值一致,一致就说明值未被改变过,就可以交换了。如果不一致,获取新的期待值再次比较并交换。其他线程修改数次最后值和原值相同,就是中间不知道变化了多少次。
    问题解决:一般来讲的话,CAS都会用版本号或者时间戳来实现,避免ABA问题
    JDK使用:JDK的原子类都是采用CAS来实现的,已封装好,不需要用户自己实现
  • 自旋锁消耗
    问题描述:多个线程争夺同一个资源时,如果CAS自旋一直不成功,将会一直占用CPU
    问题解决:1.将锁粒度或范围变小(ConcurrentHashMap);2.超过一定时间或者一定次数时,return退出
  • 多变量共享一致性问题
    问题描述:CAS操作是针对一个变量的,如果对多个变量操作无能为力
    问题解决:1. 可以加锁来解决。2 .封装成对象类解决

JUC后面对原子类有一个优化?一个叫LongAdder,知道么?

设计思想就是热点分散,类似于分治。同时采用了用空间换时间。也是CAS优化的地方

你知道Java 的动态代理吗?

它是由JDK的动态代理和Cglib 的动态代理

JDK的动态代理和Cglib动态代理区别?

一个是JDK提供的,Cglib是别人写好的我就知道,就是在maven上弄的一个jar 包。JDK是jdk自己实现的
JDK动态代理:通过JDK的proxy,可以生成语言接口,就是语言实现相同接口的,这么一个类,但如果原来类没有实现接口的话,就是它就不太合适
Cglib动态代理:使用一个字节码的编辑器,就是ASM的一个编辑器,就是生成一个目标的子类,去实现类似一个代理的功能;至于性能上来说的话,Gglib它在创建对象的过程当中,可能就是做的更慢一些,但是在运行时,可能效率要更高一些。

你提到反射机制,在开发中你什么地方用到了反射,或者说是JAVA它本身哪些地方用到了反射,你能大概给讲述一个例子么?

  • JDK inputStream

反射性能不高,为什么?

因为反射是一种解释操作嘛,我们需要去告诉JVM,希望它怎么做,就是比我们直接去写代码,直接操作会慢一点

nginx 负载均衡算法有哪些?

一致性哈希:跟HashMap做那个key一样做一个一次性哈希,把负载比较均衡地放在各个机器上;还有一种就是加权哈希,哈希因为有的机器它可能性能比较好,可以给权它加大一点,让更多的流量打在某个机器上面;还有一些是轮询,可能效果并没有那么好吧

一致性哈希是什么意思?

所有的流量过来是一样的,就是如果我这一次打到了一台机器上,下一次还可以打到同一台机器上

你知道Java 怎么实现单例模式吗?

它有懒汉模式,还有饿汉模式。
懒汉模式,当需要使用时它才会去创建单例对象
饿汉模式,还没等我们调用,就先创建出来
通用的写法都是private 私有化构造函数,对外提供一个public getInstance() 方法
静态内部类,利用类加载的特性,被类利用到的时候它才会去加载只能加载一个,因为这个是根据ClassLoader来实现的,它这个就是底层的东西了
DCL双端检查 + volatile来实现单例
static 类型 对象名 或 静态代码块

10亿个数怎么去重?

用hash分片

还有更好的方法吗?

hash分片可能不均匀,那就用bit map

20亿个数或者数量更大怎么办?

两个bit map

还是10亿个数,你怎么找出最大的10个?

用小顶堆就可以,量大的话,可以做分片或堆排序,再进行归并即可

你知道快速失败吗?

知道,集合在被遍历的过程中,modCount和预期不符的时候抛出的异常

volatile 关键字是干什么的?

  • 多线程可见性:volatile关键字是在多线程中,对变量修改后,其它线程对这个变量的可见性
  • 禁止指令重排序:

volatile 是怎么实现的?

通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化

synchronized 是如何实现的?

用lock实现的

你项目有用到线程池么?为什么要使用线程池?

有用
线程池主要是通过重复利用已创建的线程减少线程创建和销毁造成的消耗,从而提高响应速度

知道Java 中线程池的产现原理吗?

通过ThreadPoolExecutor来创建,一个新任务提交之后,会先判断核心线程池是否已满;如果没有满则创建线程执行任务;如果核心线程池已满,则判断队列是否已经满了,如果没有满,则将任务存储在队列里;如果任务队列已满,则会判断最大线程池是否已满,如果未满则会创建线程执行任务,已满则会按照策略处理无法执行的任务

拒绝策略有哪些?

丢弃任务并抛出异常、丢弃任务,但是不抛出异常、丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)、由调用线程处理该任务

在项目中怎样合理配置线程池的线程数大小

在项目中我们会根据该线程池的任务特性来设置线程数
比如任务CPU密集型的,那么我们会配置nCPU + 1
如果是IO密集型的会配置 2 x nCPU

知道JDK8 的新特性吗?

lambda表过式、Stream流、接口有default默认方法、日期时间API的改进、新增的DateTimeFormatter方法

知道重载和重写的区别吗?

重载的方法能否根据返回类型进行区分
方法的重载和重写都是实现多态的一种方式,区别在于前者实现的是编译时的多态性,后者实现的是运行时的多态性;重载发生在一个类中,同名的方法如果有不同的参数列表类型不、参数个数不同、顺序也不同则视为重载;
重写发生在子类与父类之间,重写要求子类重写之后的方法与父类被重写方法有相同的返回类型比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常;
里氏原则,重载对返回类型没有特殊的要求

知道抽象类和接口的区别吗?

换象方法需要子类重写,而静态方法无法被重写,因此二者是矛盾的。本地方法是由本地代码,比如C++代码实现的方法,而抽象方法是没有实现的

ArrayList与LinkedList有什么区别么?

ArrayList是数组的数组的数据结构,LinkedList是链表的数据结构
随机访问的时候,ArrayList的效率比较高,因为LinkedList要移动指针;ArrayList是基于索引的数据结构,可以直接映射到插入删除数据时候LinkedList的效率比较高;因为ArrayList要移动数据;LinkedList 比ArrayList 的开销更大,因为LinkedList的节点,除了存储数据,还需要存储引用

ArrayList 和Vector的区别是什么?

Vector 是线程安全的,
ArrayList 不是线程安全的,ArrayList 在底层数组不够用时,在原来基础上扩展0.5倍;Vector 是扩展一倍,Vector 只是关键性的操作方法,在方法声明中都加了synchronized的关键字来保下线程安全

怎样确保一个集后不被修改?

通常情况,用final关键字来进行修饰,final修饰成员变量如果是基本数据类型,表示这个变量值不可改变的;如果是引用类型,则表示引用的地址值是不能改变的,但是引用所指向的对象里面内容还是可以改变的。这种情况的话,要用unModifiableMap,List 和Set也一样

分存锁是怎么实现的?

我们是通过zookeeper来实现的分存式锁。分布式锁就是多个进程去消费公共资源时,需要做的一个同步操作,这时需要使用公共的组件比如mysql/redis/zk来实现分存式锁。zk是通过创建临时节点来表示上锁,比如第一个进程过来创建了临时节点,等它处理完公共资源后,再将临时节点删除来表示释放锁
相当于多个人去访问zk,基于zk的选举机制就可以表示谁拿到锁

流量激增,线程池被打满,你会怎么去排查这个问题

有可能是OOM,然后可以使用JDK自带的工具分析,jps/jstack等分析
问题可能是某人写和循环、自旋方面的

TCP协议中有一个叫 Time_Wait,了解么?

Time_Wait 是在挥手的时候,它是主动断开方有的,经过2倍2MSL时间(一去一回)

TCP 为什么是三次握手?而不是两次或者四次?

我所理解的三次握手是这样的,我客户端要给那个服务器报告我要和你建立连接,顺便把我自己的一个发送能力,发送给服务器,让服务器知道。服务器判断我是否可以给你创建链接,把我的一个接收的一个能力返回给客户端。只有三次握手,才能够保证双方的发送能力和接受能务都达到一个协调好的这么一个过程。因为协议没有100%可靠的,所以三次已够了,如果四次也不能保证100%可靠的
其实就是TCP,它本身是一个稳定的可靠的链接

RPC框架有哪些?什么是RPC

RPC 是远程方法调用,其实质就是分布式进程间通信(ip/port),底层是TCP/IP (socket)通信

通信超时,你们是怎么处理的?

捕获异常,重试。如果重试超过一定次数以后,就让它就是报错,就放弃重试,怕造成其它业务阻塞

---------------Redis--------

Redis 有哪些数据结构

有string字符串类型、hash哈希、list链表 、set集合、zset有序集合

Redis 它的key 有哪些淘汰策略

定期删除+惰性删除

有了解过redis 的持久化吗?

redis 是具有两种持久化方式,分别是AOF、RDB
AOF是存储的指令文件,每个在redis中执行的命令都会被记录下来
RDB是快照,一个数据文件,有需要可以直接加载到内存中,快速恢复。恢复数据速度来说,RDB会大于AOF;恢复数据完整性来说AOF会大于RDB;但AOF也可能会丢失数据,因为linux中的操作并不是直接写入到redis aof文件中,而是先写入到os cache中,然后每隔1秒做一个强制刷新动作,如果数据丢失可能会丢失1秒的数据,建议每次定时备份到其他云磁盘,避免机器故障

一致性哈希算法你有了解过吗?

我记得它是分到一个16384的槽这样的,一个2的16次方,然后它会把现有的机器对这些槽进行分配,如果就是这样的情况打了之后,它会相对这个key可以进行一个哈希处理,然后16384这样一个取模它落在的槽,如果进行哈希处理的话就是去那台机器查询一次

redis缓存与java 中的map缓存有什么区别?

  • redis 是分布式的缓存,而java中的map是单节点缓存;这们A服务、B服务都能拿到缓存; 我们的缓存思想就是缓存到公共的地方;redis 中央缓存就是集中管理缓存,而不是各自的节眯进行管理的一个效果
  • redis 有自己的内存管理方式

Redis支持事务吗?与MySQL的事务有什么区别吗?

mysql 的事务,如果失败的话,它是可以回滚的,因为它有一些日志操作
redis不一样,redis失败的话就直接失败了,后面的也不用去处理了

你知道什么叫缓存穿透、缓存击穿?

缓存穿透就是比如说一个用户去访问一个不存在的键,不只是一个用户,很多很多个请求,都访问这样一个不存在的键,会导致这个缓存里没有,然后它所有请求都会发送到那个MySQL上,这样的话,它的数据库压力太大了,就可能造成奔溃了

怎样处理缓存穿透这种情况呢?

可以对请求进行过滤,常见的就是那个布隆过滤器

布隆过滤器是什么原量呀?

使用多个hash函数,hash函数对应一个数组的,数组里存储的是一个byte【0-1】。就是一个请求根据很多哈希之后,得到不同的索引,去索引位判断是否出现过。默认情况是0,如果全是1的话,代表它可能会出现过。如果其中有一个0话代表它不存在的

---------------Redis--------

Kafka用过么?

用过

用在什么场景?

实时的数据消费加工。比如一个场景是实时行情异动、金融风控。因为我是做实时行情异动平台这块,我们将实时行情写入到kafka集群中,通过Spark Streaming/Flink 是消费Kafka topic中的数据。分析的结果数据可以写入到MySQL/HBase或者再写回Kafka中供其它应用继续分析。

Kafka怎样处理乱序

这个我处理过,主要借助外存,比如说redis、hbase、es这种支持幂等写操作的存储引擎。

Kafka的消息是有序的吗?它是全局有序的么?

kafka是有序的,但它的有序是局限的。比如发的消息,消费消息的时候一个consumer只能消费一个partition的消息,一个partition只能供一个消费者来消费,保证它消费的数据一定是有序的。

不同Partition之间,它是全局有序的吗?

是无法保证全局有序的

如果说我有一个业务,就是说同一个userId它产生的消息,我都要保证有序,这种用kafka怎么处理?

我觉得这个比较好触发就是在客户端来指定来保证一个用户有序

这多个用户他们的消息在保证同个用户产生的那些日志和消息都是有序的前提下,可以混合在一起吗?

我认为在topic 下,partition只设成一份就可以

------------MySQL

innodb 熟悉吗?innodb如果没有设置主键的话,它内部会怎么处理?

innodb它默认是有一个整型的主键,因为在索引这方面还是整型的就比较有优势

innodb是怎么选主键的,它是随机选还是怎么样的?

如果没有设置这样一个主键的话,它就会选一个一表为整型的键来作为它的主键。但如果没有这样一列的话,它会自己生成一个随机长的列,然后来做一这样的一个主键

innodb 与 myisam的区别?

innodb支持事务,myisam它不支持事务;innodb的主键索引存在是主键和这条数据,即聚集索引;而myisam是非聚集索引

什么叫聚簇索引,你了解不?

找到了索引就找到了需要的数据,那么这个索引就是聚簇索引,所以主键就是聚簇索引,修改聚簇索引其实就是修改主键。

什么是非聚簇索引

索引的存储和数据的存储是分离的,也就是说找到了索引但没找到数据,需要根据索引上的值(主键)再次回表查询,非聚簇索引也叫做辅助索引。

讲讲如何提高MySQL数据库的安全性

如果MySQL客户端和服务端的连接需要跨越并通过不可信任的网络,那么需要使用ssh隧道来加密给连接通信,第二也可以使用比如说 set password 语句来修改用户的密码,然后MySQL提防的攻击有防偷听、防篡改、回放、拒绝服务等,不涉及可用性和容错方面,设置除了root用户外的其他任何用户不允许访问 MySQL主数据库中的user表

知道事务的 四大特征是什么吗?

原子性、一致性、隔离性、持久性

讲讲MySQL性能优化的理解好吗?

举些例子:

  • 比如只需要一行数据时,可以使用limit 1
  • 也可以选择正确的数据库引擎
  • 用not exists 代替 not in
  • 对操作服务的优化,尽量不采用不利于索引的操作服务

讲一个sql 聚合函数有哪些?

挺多的:

  • 比如说avg() 它就求平均值
  • count() 求个数
  • max() 求最大值
  • min() 求最小值
  • sun() 求和
  • group by 对数据进行分组

你们数据库 是不是支持emoji 表情,如果不支持,怎么办呢?

如果是UTF8字符集的话,一般需要升级至utf8_mb4 然后来支持

什么情况下,应该不建或少建索引?

举几个例子:
比如说第一点就表记录太少
第二点,经常插入删除修改的表
第三点,比如说重复数据一些分存平均的表字段,假如说一个表有10万行记录,有一个字段A只有T和F两种值,且每个值的分存概率大约为50%,那么对这种表中字段建索引一般不会提高数据库的查询速度
第四点,比如说像经常和主字段一块查询,但主字段索引值比较多的表字段

数据调优经验,对数据库用多个索引匹配的时候有什么经验吗?

如果说就是在索引这一块,其实是MySQL的一个比较大的过程
在我看来,我们首先建立合适的索引,创建索引考虑覆盖索引,因为覆盖索引,因为覆盖索引可以减少回表的次数,MySQL 5.6以后对覆盖索引做了进一步的优化,支持索引下推的功能。就是把覆盖索引所覆盖的一个字段,然后进一步进行筛选,尽量减少回表的次数;这个我们可以在explain看sql的执和计划,那个Extra那个字段里面有Using index condition 可以看到的,其实我们可以进一步对他进行优化,如果我们的存储介质使用是机械硬盘的话,因为机械硬盘很怕随机读写的,它有一个磁盘寻址的开销,我们可以把Mrr打开,就是 multi range read,他可以把在回表之前把我们的ID读取一个buffer里面,进行一个排序,把原来的一个随机操作变成一个顺序操作,就覆盖索引可以做的一些优化,因为覆盖索引,可以避免排序用到的一些临时文件;可以利用最左原则和覆盖,配合,减少一些为索引的维护;还有一块就是如果就是对一些普通索引,如果我们的是一个写多读少的服务,并且这个服务就是唯一性要求没有那么高,或者我们的业务代码可以保证唯一性的,我们可以用普通索引,因为普通索引,因为普通索引可以用到Change Buffer的,Change Buffer就可以把一些写操作给缓存下来,在我们读的时候进行merge的操作,这样的话就可以提高写入速度,还有我们内存的一个命中率。
还有就是那些,如果我这个索引走不上, 我们应该考虑哪些方面?
1、是不是我们SQL写得有问题,比如索引字段进行了一些函数操作;连接两个表的编码不一样;
2、字段的类型不一样,比如说String,赋给它的一个ID,如果String跟ID比较的话,会把String转成ID,在MySQL里面就是运用到一个隐式的一个cast的一个函数转换,也有可能是一

事务脏读,是怎么解决?

通过事务隔离级别:可重复读;实现方面通过:行级锁,读锁写锁

MySQL查询为什么要 jion小表jion大表?

有点类似于双重for循环一样,最佳的写法还是小循环在外面,大循环在里面;因为中间有很一些连接、释放,小表驱动大表可以减少连接、释放

谈谈数据优化你们是怎么做的?

保证

  • 1.首先数据库、表设计没错
  • 2.SQL写得没错
    数据量大于500万,考虑分库分表
    建立合适的索引
    explain + sql 查看sql 是否走索引

分表策略

  • 1.按区间,比如id从1~10000到第一张表
  • 2.按时间,按照年月
  • 3.Hash算法,保证插入、查询都是同一张表

查询跨表了,这个时候怎么查?

根据标识先计算先跨了哪些表,然后我分别把数据查询出来,SQL有个语句叫 union,然后把它们组合起来,相当于把它们合并起来,形成最终的结果返回

union和unionAll有什么区别?

unionAll它是可以有重复的,而union它是没有重复的

索引有去了解它的底层吗?

索引是帮助查询MySQ数据库的一种高效的数据结构,避免全盘扫描;如果索引足够优化,查询的效率就变高了。MySQL采用了B+树作为索引结构,基于树的查询是远远高于从表头到表尾的一个查询

你有了解B+tree,它是底层是怎样的么?

索引是帮助MySQL高效获取数据的排好序的数据结构

  • 非叶子节点不存储data,只存储key,可以增大度
  • 叶子节点不存储指针
  • 顺序该问指针,提高区间访问的性能
  • 每层节点横向扩增,存储更多的索引

MySQL有哪此引擎你知道吗?

innodb / myisam / memory ,还有其他的

innodb和myisam在索引方面有什么区别?

myisam 主要做多读一写的操作,不需要保证数据的安全,它不支持事务,它支持表锁
没有隔离级别概念,因为它不支持事务

innodb 最大的特点就是支持事务,mysql数据库建表默认是innodb,它支持行锁、表锁
我们在做公司官网,就是选用myisam来,因为比较少修改,多读的场景
支持可重复读

什么是回表问题?

InnoDB里只有主键是有聚簇索引的,它根据那个索引直接能查找那个数据的一些信息
非聚簇索引的话,它那个索引段它会指向的就是文件的地址,这样的话还要去查一遍
回表:去查一个非主键索引,然后这个非主键索引最后查询到的东西其实是一个主键,这样的话,还要根据那个主键去查询

什么叫覆盖索引吗?

如果索引的叶子节点包含了要查询的数据,那么就不用回表查询了,也就是说这种索引包含(亦称覆盖)所有需要查询的字段的值,我们称这种索引为覆盖索引。

听说过索引下推吗?

如果索引的列在 select 所需获得的列中(因为在 mysql 中索引是根据索引列的值进行排序的,所以索引节点中存在该列中的部分值)或者根据一次索引查询就能获得记录就不需要回表,如果 select 所需获得列中有大量的非索引列,索引就需要到表中找到相应的列的信息,这就叫回表。

MySQL索引有什么规则吗?或者注意的事项有没有了解过

1.主键自动建立唯一索引
2.频繁作为查询条件的字段应该创建索引
3.查询中与其它表关联的字段,外键关系建立索引
4.频繁更新的字段不适合创建索引
因为每次更新不单单是更新了记录还会更新索引
5.Where条件里用不到的字段不创建索引
6.单键/组合索引的选择问题,who? (在高并发下倾向创建组合索引)
7.查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
8.查询中统计或者分组字段

1.表记录太少
百万级别以下,官方宣称不建索引,能hold住百万级别
2.经常增删改的表
Why: 提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT/UPDATE和DELETE
因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件
3.数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引
注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。

你在平时开发有没有遇到过锁表的情况?

数据库优化,怎样做?

0 查询优化
1 慢查询的开启并捕获
2 explain + 慢SQL分析
3 show profile 查询SQL在MySQL服务器里面的执行细节和生命周期情况
4 SQL数据库服务器的参数调优

数据库大批量数据需要更新,你们是怎么做的?

可以去把它写在一个内存的临时表里面,因为我们知道 innodb,会维护一个 buffer pool,如果我们直接把大量的一些数据全部读进去的话,可能会造成flush 的一个操作,就是把脏页刷回MySQL,就是这么一个操作会造成我们线上的一个业务的阻塞。

热点数据你数据太猛的话,其实你使用那个Redis的客户端可能访问不到,带宽打满吗?那这个时候又该怎么办?

可以使用一种叫本地缓存

------------------

你担任组长,你是怎么去沟通和协调?就是平时你们组员之间的一些工作/任务呀?出现或者说组员之间出一些矛盾或者工作压力比较大的情况,你是怎么处理这种情况?

我觉得作为一个组长,首先互联网这个行业压力本身就是很大的,作为一个组长,我只能说就是按照他们目前的一个工作的状态,还有他们目前的就是一个能力的范围给他们。安排合适的一个需求一个迭代的过程。尽量的把我们的开发规范,还有我们的需求、开发、上线一个个评审的过程做规范,把文档给做规范。这样的话,才能够让我们项目组,更平稳的在往前发展

平时,你是怎么学习的?除了java这外,你还会去了解一些其它的技术吗?

我们平时喜欢看一些比较深入的,比如mysql/redis/spark/flink,然后就是看官网资料比较多。因为我觉得官方说明会更全面一些,同时也会看一下大家平时遇到的故障问题。还是希望把自己的技术更深入一些。看完以后,我也会就是做一些输出总结性的东西。放在我们项目组的学习文档,构建一个学习库,然后大家可以交流

进入一家企业,有哪些收获?

进入企业,了解该如何上手,快速上手项目。给我的心得就是首先可以去看一下业务的上下游关系,比如我们服务器是服务于哪些服务?然后我们的服务又提供了哪些能务给其他服务调用的,我觉得这个关系要理清楚。还有就是有效的沟通,进入企业之后写代码跟沟通可以是六四开,也有可能是五五开。因为你要跟业务方沟通,要跟产品要跟开发、测试各方面沟通。如何用最有效的一个沟通方式去达到你的目的,我觉得这也是非常重要的。

你是怎么理解业务和技术之间的关系的?你觉得哪些重要一点?

说实话,我以前觉得我是一个做技术的,可能技术更重要一点,但是其实我发现,业务也是非常重要的,现在因为我觉得技术是服务于业务的,因为我们去给别人、去给企业去做这个东西,企业是希望盈利的。而它的盈利的东西是这个业务去盈利,那么如何把用我们的技术做好,能够真正的去服务于那些业务,那让些业务运行得更好,我觉得是很重要的。
总结一点:就是用技术去驱动业务,让业务产生价值,一个良性循环

你有什么想问我的?

我能问下您对我今天的面试,感觉怎么样?或者觉得我在哪方面可以做得更好?

你这边整体来说都是挺不错的,你看我问胸的技术栈,问到一定深度之后,其实像有些数据库,没有问你,特别特别再深了,就比如分库分表我没有问你了。因为我知道你像索引、还有很多你都特别熟悉了,感觉这一块你可能问题就不是很大了。但是像刚刚问到一些实际场景的时候,你那个思路的话,转化可能没那么块,可能跟你实际的经验有关,所以说这个有没有关系的。后面可以锻炼的。就你整体回答下来的话,思路是很清晰的。你的整个技术的话,就你一套知识体系一样。我问到你HashMap时,你基本上把我想知道的东西一整条链路介绍给我了。因为我问到很多人时,很多人就告诉我数组 + 链表 + 红黑树,就是这样,就告诉我就完了。但是他没有告诉我为什么后面变成了红黑树,或者说它之前死循环的原因,后面它变成死循环之后他的一些替代品啊,或者你基本上就是不用我问,你自己全部说出来,我就感觉你的逻辑比较清晰。然后我问你一些实际场景,也可以看得出这个项目你是实际参与过的,

猜你喜欢

转载自blog.csdn.net/weixin_32265569/article/details/108429436