1.1)集合,string等基础问题
1、arraylist ,linkedlist的区别,为啥集合有的快有的慢
①[ArrayList]它的底层是数组,有下标的概念,可以通过index下标直接定位元素,所以查询快;在增删时,会进行扩容判断和拷贝,所以增删慢。
②LinkedList的底层是[双向链表]。每次查询都要循环遍历,所以查询慢;在增删时只需要链接新的元素,而不用修改列表中剩余的元素,所以增删快。
集合有的快有的慢,主要是因为它们的底层实现不同。
2、字符串倒叙输出
有很多种方法,最简单的就是使用StringBuilder类,它里面有个reverse()方法,可以将字符串进行倒序输出。
第二种是String类的toCharArray()方法把字符串转换为char数组。使用循环将数组中的字符进行倒序交换,输出结果。
2.1、字符串常用方法
toCharArray() 将字符串转换为字符数组
split(String regex) 切割
substring(int beginIndex, int endIndex)截取字串
int length() 获取字符串的长度
int indexOf(String str) 获取特定字符的位置
toUpperCase() 转大写转小写忽略大小写
2.2、字符串+号拼接的底层原理
看一段代码:
public class Test {
public static void main(String[] args) {
String a = \"abc\";
String b = \"def\";
String c = \"abc\" + \"def\";
String d = a + \"def\";
String e = new String(\"abc\") + \"abc\";
String g = \"abcdef\";
}
}
运行项目并下载源码java
运行
12345678910
反编译后
public class Test
{
public Test()
{
}
public static void main(String args[])
{
String a = \"abc\";
String b = \"def\";
String c = \"abcdef\";
String d = (new StringBuilder()).append(a).append(\"def\").toString();
String e = (new StringBuilder()).append(new String(\"abc\")).append(\"abc\").toString();
String g = \"abcdef\";
}
}
运行项目并下载源码java
运行
1234567891011121314151617
结论:字符串+拼接如果无变量和new关键词参与则在字符串常量池。。
如果有变量或者是new关键词参与拼接, 那么就会都new出一个StringBuilder对象, 然后使用append方法, 随后又使用toString方法来new一个对应的String类, 这样繁琐的创建对象, 不仅消耗时间, 还会消耗内存资源。
所以:循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。而不要使用+
需要全套面试笔记及答案【扫一扫】 即可免费获取**
3、讲一下Java的集合框架
说到集合框架我们可以把集合框架分为两个部分,单列集合collection和双列集合顶层接口 map。其中map的存储是key-value形式,collection下面有set接口特点是存储无序不可重复,list接口存储有序可重复。
| 列表list(集合有序可重复,可有null元素) | 集set(元素是无序不可重复的) | map(顶层接口,存储的是键值对,键是唯一的。) |
|---|---|---|
| Vector:底层数据结构是数组数据结构.特点是查询和增删速度都很慢。 集合长度。线程安全。 | HashSet:底层数据结构是哈希表、存取速度快、元素唯一、线程不安全。 | HashMap:底层是哈希表数据结构;允许使用null键和null值;线程不安全,效率高; |
| ArrayList:底层的数据结构是数组数据结构,特点是查询速度快(因为带下标),但是增删速度稍慢,因为当元素多时,增删一个元素则所有元素的下标都得改变,线程不安全。默认长度是10,当超过长度时,按1.5倍延长集合长度。 | TreeSet:底层数据结构式二叉树。可以对Set集合中的元素进行排序。元素有序、线程不安全。 | HashTable: 底层是哈希表数据结构;不可以使用null键和null值线程安全效率低,因为它里面的方法都是用了 synchronized关键字修饰。 |
| LinkedList:底层数据结构式双向链表数据结构(即后面一个元素记录前一个),特点:查询速度慢,因为每个元素只知道前面一个元素,但增删速度快,因为元素再多,增删一个只要让其前后 的元素 重新相连即可,线程不安全。 | ①特点:有序的,保证元素不可重复的前提下,维护了一层添加顺序,判断是否重复的依据:hashCode、equals | TreeMap:底层是二叉树结构;允许使用null键和null值;线程不安全; |
4、定义线程安全的map,有哪些方法,ConcurrentHashMap原理
①定义线程安全的Map可以通过在每个方法上添加synchronized关键字实现或者使用Collections.synchronizedMap方法来保证线程安全。但是这样效率比较低。
②还可使用ConcurrentHashMap。原理是采用分段锁的机制,将整个Map分成多个Segment,在每个Segment上都加锁,不同的线程可以同时访问不同的Segment,这样兼顾了线程安全和运行效率。
5、equals与==
①==:如果比较的对象是基本数据类型,则比较的是数值;如果比较的是引用数据类型,则比较的是对象的地址值。
②equals():用来比较两个对象的内容是否相等。equals方法不能用于基本数据类型的变量,如果没有对equals方法进行重写,则比较的是对象的地址值。
6、hashtable和hashmap的区别
hashmap底层是哈希表存储是无序的它和HashTable最大的区别是hashmap线程不安全并且key值允许为null,而HashTable线程安全并且key值不允许为null,由于Hashtable直接在put和get方法上面加synchronized关键字来实现线程安全所有操作都需要竞争同一把锁所以效率很低。
| 区别 | 存储 | 底层 | 如何选择 | key是否允许null | 是否线程同步 |
|---|---|---|---|---|---|
| HashMap | 存储无序 | 哈希表 | 不需要排序 | 允许 | 非线程安全 |
| HashTable | 存储无序 | 哈希表 | 需要线程安全 | 不允许 | 线程安全 |
| TreeMap | 存储有序 | 红黑树 | 需要排序 | 不允许 | 非线程安全 |
| LinkedHashMap | 存储有序 | 链表和哈希表 | 需要存储有序 | 允许 | 非线程安全 |
public class Test1{
//1、私有化本类所有的构造方法
private Test1(){}
//2、直接在本类中创建唯一对象
private static Test1 t1 = new Test1();
//3、提供外界获取唯一对象的方法(公共的、静态的)
public static Test1 getInstance(){
return t1;
}
}
运行项目并下载源码java
运行
1234567891011
懒汉式单例设计模式的特点
①懒汉式模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance (获取实例)方法 时才去创建这个单例。(比较懒别人喊一次才动一次?)。
②好处:不存在浪费内存的问题,弊端:在多线程环境下,可能不能保证对象是唯一的
public class Test2{
//1、私有化本类所有的构造方法
private Test2(){ }
//private和static修饰的成员变量
private static Test2 t2;
//3、提供外界获取唯一对象的方法(公共的、静态的)
public static Test2 getInstance(){
if(t2 == null){
//2、在本类中创建唯一对象
t2 = new Test2();
}
return t2;
}
}
运行项目并下载源码java
运行
123456789101112131415
8、什么是哈希表
哈希表又叫散列表是一种数据结构,特点是查找增删都很快。哈希表里面每个数据都有唯一的键(key),把这个关键码值(key)通过映射函数映射到哈希表中一个位置来访问。
这个映射函数叫做哈希函数(散列函数),可以把任意长度的key值变换输出成固定长度的哈希值(Hash value)。一个好的哈希函数能够将键均匀地映射到哈希表中,以减少冲突和查找时间。
Hash code是一种编码方式,在Java中,每个对象都会有一个hashcode。Java可以通过这个hashcode来识别一个对象。
9、什么是哈希冲突,怎么解决
哈希函数把key变换输出为哈希码(哈希值),当不同的key值产生的哈希值H(key)是一样的,就产生了哈希冲突。
解决哈希冲突的方法
(1) 再哈希法
当发生冲突时,用不同的哈希函数计算地址,直到无冲突。虽然不易发生聚集,但是增加了计算时间。
(2) 链地址法
通过将具有相同哈希码的数据元素存储在一个链表中,来避免冲突的发生。
在查询、插入或删除数据元素时,首先根据哈希码找到对应的链表,然后在链表中搜索或修改相应的数据元素。
(3)建立公共溢出区
将哈希表分为基本表和溢出表两部分,将冲突的元素存储在另一个溢出表中
10、final关键字可以修饰哪些对象
final类:不可被继承,如java.lang.Math就是一个 final类,不可被继承。
final方法:不可被重写
final变量:final修饰的变量是一个常量一般和static联用。只能被赋值一次在初始化后不可改变变量值。如果final变量是引用变量,则不可以改变它的引用对象,但可以改变对象的属性。
public static final double pi=3.14;
11、lise集合便利查找其中的一项怎么处理比较快。
12、固定的不可变的一些对象,放到哪里让全局都可以使用?
放在常量的类里,配置到数据库里,放在配置文件里,放到缓存里面,第三方配置中心
1.2)java8新特性,xxx原理。反射等高级问题
1、Java8有哪些新特性
Java8出现了很多新特性我说几个比较大的改变,第一个是接口可以写默认方法和静态方法,默认方法用default修饰符标记,不强制实现类实现默认方法。第二个是stream流,提供了便捷快速的操作数据的方式。第三个是Lambda表达式和函数式接口,函数式接口仅有一个抽象方法的接口,Lambda 表达式本质上是一个匿名方法,让我们的代码更简洁直观。第四个Java 8引入了重复注解的概念,允许在同一个地方多次使用同一个注解。
2、单例模式
1)饿汉式单例设计模式的特点
①饿汉式模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance (获取实例)方法之前单例就已经存在了。(比较饿,没喊就迫不及待的创建了)。
②好处:就算在多线程环境下,也一定可以保证对象是唯一的。弊端:创建比较早,有浪费内存的现象。
2)懒汉式单例设计模式的特点
在第一次使用时才创建实例,但需要考虑线程安全问题。
3、
4、
5、
第二章、深入技术栈
2.1)JVM 运行机制等问题
1、JVM是什么 包含哪些模块
①JVM是Java虚拟机,是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java语言非常重要的特点跨平台性就是通过jvm实现,Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。
整个JVM 框架由加载器加载文件,然后执行器在内存中处理数据,通过本地接口与异构系统交互
②JVM包括
1、类加载器(Class Loader )类加载器的作用是加载类文件到内存
2、执行器(Execution Engine) 执行引擎执行引擎也叫做解释器,负责解释命令,提交操作系统执行。
3、本地接口(Native Interface )作用是融合不同的编程语言为Java 所用
4、运行时数据区(Runtime data area) 我们所有写的程序都被加载到这里之后才开始运行。包括:
(1)线程共享的堆(含有字符串常量池),元空间(元空间在本地内存中,包含了加载的类信息和运行时常量池)
(2)线程私有的虚拟机栈、本地方法栈以及程序计数器
2、JVM 内存区域(运行时数据区)
①栈是运行时的单位 , 是线程私有的,它的生命周期和线程相同,随着线程的创建而创建,随着线程的死亡而死亡。所有的 Java 方法调用都是通过栈来实现的,方法调用的数据需要通过栈进行传递,每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。
②Java 堆是所有线程共享的最大的一块内存区域,唯一目的就是存放对象实例。里面包含字符串常量池(针对字符串专门开辟的一块区域,主要目的是为了避免字符串的重复创建。)。因为Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆。通过垃圾回收算法 Java 堆还可以细分为:新生代和老年代。
③方法区(元空间)
方法区是被所有线程共享,方法区会存储已被虚拟机加载的 类信息、字段信息等。JDK1.8 最大的变化就是方法区的位置转移到了本地内存中被称为元空间。元空间中还包含运行时常量池:各种字面量和符号引用的常量池表 。
④程序计数器
线程私有的,是一块很小的内存空间,每个线程都有一个程序计数器,代码的流程控制,如分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。
⑤本地方法栈
本地方法栈为JVM调用native方法时服务,一个Native方法就是一个java调用非java代码的接口。
PS:直接内存在本地内存中,不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现
3、垃圾收集器之G1 收集器
①垃圾回收器通常是作为一个单独的低级别的线程运行,通过垃圾回收算法对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。
②JDK9 开始,G1 垃圾收集器成为了默认的垃圾收集器。使用标记整理算法主要有初始标记,并发标记,最终标记,筛选回收四个过程。G1将整个堆分为大小相等的多个Region(区域),G1跟踪每个区域的垃圾大小,在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的区域,已达到在有限时间内获取尽可能高的回收效率。
需要全套面试笔记及答案【扫一扫】 即可免费获取**
4.1、内存中死亡对象判断方法
①引用计数法
每当有一个地方引用它,计数器就加 1;
当引用失效,计数器就减 1;
任何时候计数器为 0 的对象就是不可能再被使用的。
②可达性分析算法
以 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
引用的概念:
引用分为强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱)
1、强引用:最普遍的引用如String s = new String(“aaa”)。如果一个对象具有强引用,垃圾回收器绝不会回收它
2、软引用:如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。
3、弱引用:的对象拥有更短暂的生命周期。一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
4、虚引用:虚引用并不会决定对象的生命周期。是一种形同虚设的引用,随时会被回收。
4.2、新老年代的含义和晋升逻辑
①长期存活的对象将进入老年代
对象首先在 Eden 区域分配。如果对象在 Eden 出生并经过第一次 Minor GC (新生代GC)后仍然能够存活年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。
②大对象直接进入老年代
大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。
③新生代 GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC 非常频繁,也比较快。
老年代 GC(Major GC/Full GC):指发生在老年代的 GC,Major GC 的速度一般会比 Minor GC 的慢 10 倍以上。
4.3、垃圾回收算法
①标记清除算法
标记阶段,标记所有的可访问对象。
收集阶段,垃圾收集算法扫描堆并回收所有的未标记对象。
② 标记整理算法
标记-整理(Mark-and-Compact)算法是根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
③复制算法
将内存按照容量大小分为大小相等的两块,每次只使用一块,当一块使用完了,就将还存活的对象移到另一块上,然后在把使用过的内存空间移除。特点:不会产生空间碎片;内存使用率极低
④分代收集算法
不同的对象的生命周期是不一样的。分代回收把不同生命周期的对象 放在不同代上,在新生代中,有大量对象死去和少量对象存活,所以采用复制算法,老年代中因为对象的存活率极高,所以采用标记清理或者标记整理算法进行回收。
5、常用 JVM 调优手段
1、内存分配:根据应用程序的需求调整JVM的内存分配。如果应用程序需要处理大量数据,则可以增加JVM的堆内存和非堆内存。
2、垃圾回收:JVM的垃圾回收是一项重要的任务,它可以释放无用的对象并回收内存。通过选择不同的垃圾回收器、调整垃圾回收器的参数和设置合适的内存阈值等,可以提高垃圾回收的效率。
3、线程管理:JVM中的线程是Java应用程序的核心组成部分。通过调整线程的数量、设置线程的优先级和使用线程池等方式,可以提高Java应用程序的并发性能。
4、类加载:Java应用程序需要在运行时动态加载类。通过优化类的加载过程,可以提高应用程序的启动速度和响应性能。
5、编译优化:JIT编译器可以将Java代码编译为本机代码,从而提高应用程序的执行速度。通过设置JIT编译器的参数和选择适当的编译器,可以提高编译器的性能和效率。
6、I/O优化:I/O操作是Java应用程序中常见的性能瓶颈之一。通过使用缓冲区、选择合适的I/O库、减少I/O操作次数等方式,可以提高I/O操作的效率。
6、类的加载
JVM中类的装载是由类加载器和它的子类来实现的,类加载器负责在运行时查找和装入类文件中的类。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。
1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;
2)如果类中存在初始化语句,就依次执行这些初始化语句。
7、主内存和本地内存
①主内存 :所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量)
②本地内存 :每个线程都有一个私有的本地内存来存储共享变量的副本,并且,每个线程只能访问自己的本地内存,无法访问其他线程的本地内存。本地内存是 JMM 抽象出来的一个概念,存储了主内存中的共享变量副本。
8、类什么时候被初始化?
1)创建类的实例,也就是 new 一个对象
2)访问某个类或接口的静态变量,或者对该静态变量赋值
3)调用类的静态方法
4) 反 射 (Class.forName(“com.lyj.load”))
5)初始化一个类的子类(会首先初始化子类的父类)
6)JVM 启动时标明的启动类,即文件名和类名相同的那个类只有这 6 中情况才会导致类的类的初始化。
类的初始化步骤:
1)如果这个类还没有被加载和链接,那先进行加载和链接
2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)
3)加入类中存在初始化语句(如 static 变量和 static 块),那就依次执行这些初始化语句。
9、内存溢出内存泄露的区别
10、说一下双亲委派机制
2.2)数据库事务、索引, 优化 SQL 等
1.1、隔离级别
①读未提交 : 可以看到其他事务没有提交的结果。等于没有任何隔离性。
②读已提交 :只能看到其他事务已经提交的改变。这种隔离级别会引起不可重复读。
③可重复读: 它保证了可重复读取同样的数据,即同一个事务多次读取操作数据会看到同样的数据。解决了不可重复读但可能造成幻读。
④可串行化 : 通过强制事务排序,解决了幻读的问题。它在每个读的数据行上面加上共享锁。(实际中基本不使用)
不同隔离级别可能导致的问题
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交 | 可能 | 可能 | 可能 |
| 读已提交 | 不可能 | 可能 | 可能 |
| 可重复读 | 不可能 | 不可能 | 可能 |
| 可串行化 | 不可能 | 不可能 | 不可能 |
1.2、脏读幻读,不可重复读和幻读的区别
①脏读:当一个事务读取到另外一个事务修改但未提交的数据时,就可能发生脏读。
②不可重复读:就是说,比如在A事务中进行多次相同的查询,B事务在A事务多次查询之间修改对应表中的数据,导致A事务多次读取的结果不一致。
③幻读是’‘不可重复读’\’的一种特殊场景:例子:事务一将表中性别列的值都更改为1,事务二又添加了一条\”性别”的值为0记录,事务一再查询所有的记录时会发现有多了一条记录的“性别为0,这种情况就是所谓的幻读
不可重复读和幻读区别
不可重复读:一个事务中多次读取同一数据,但是由于其他事务的修改,导致两次读取的结果不一致。
幻读:一个事务中多次读取同一范围内的数据,但是由于其他事务的插入操作,导致两次读取的结果不一致。
2、事务的特性
①原子性
事务要么全部提交成功,要么全部失败回滚,不能分割执行其中的操作。
②一致性
事务的执行不会破坏数据关系的完整性和业务逻辑的完整性。
③隔离性
一个事务不会被另一个事务影响,第一个事务执行完成后再执行另一个事务,但处于性能上的考虑,一般都需要事务并发执行,所以隔离程度有区别。
④持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
3、数据库批处理有了解吗
批处理(batch) 操作数据库,批处理指的是一次操作中执行多条SQL语句。
第一步:打开mysql的批处理:
如: url=jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&rewriteBatchedStatements=true
运行项目并下载源码java
运行
1
第二步:使用Statement接口或者Preparedstatement接口
保存将执行的SQL语句,执行保存的SQL语句
增加批处理:addBatch(String sql) 将要执行的SQL先保存在当前命令列表中,先不执行
执行批处理:int[] executeBatch() 执行SQL语句,将批处理中所有SQL语句执行,
返回一个数组,这个数组是说明每条命令所影响的行数
清空批处理:clearBatch() : 清空当前批处理的语句
运行项目并下载源码java
运行
123456
4.1、数据库优化
①数据库参数配置优化:
很多配置默认参数(如最大连接数、数据库占用的内存等),不是最佳的配置,例如最大连接数默认为100,当请求超过100时都要等待。
②分库分表:
后面第五题有讲
③优化表结构的设计:
包括字段数据类型,比如人的年龄用无符号的unsigned tinyint即可,没必要用integer。数据类型的长度比如用户的手机号11位长度,没必要用255个长度。有外键约束会影响插入和删除性能,如果程序能够保证数据的完整性,那在设计数据库时就去掉外键。
④SQL语句的优化:
其中最重要的方式就是使用索引,还有#{}防止sql注入
⑤事务优化:
避免大事务,操作的数据比较多的事务会带来风险:锁定太多的数据,造成大量的阻塞和锁超时,回滚时所需时间比较长,执行时间长容易造成主从延迟
服务器层面
①主从复制,读写分离: 一台MySQL服务器同一时间点支持的并发数是有限的,所以增加MySQL服务器的数量也是一种增强数据库性能的方式。使用MySQL主从复制,增删改操作走Master主服务器,查询走Slaver从服务器,这样就减少了只有一台MySQL服务器的压力。
②增加缓存层
增加缓存层,减少数据库连接也是一种优化手段,有些查询可以不用访问数据库,可以通过使用缓存服务器如redis、memcache、elasticsearch等增加缓存,减少数据库的连接
③升级服务器硬件
更快的磁盘IO设备,更强的CPU,更大的内存,更大的网卡流量(带宽)等。
4.2、查询语句优化
①使用索引, 在SQL语句的WHERE和JOIN部分中用到的所有字段上,都应该加上索引。
②尽量防止索引失效,下面第七题有说索引失效原因
③分页查询优化 ,如果数据量较大,一次性获取所有数据会导致性能问题。应该通过使用LIMIT关键字,进行分页查询。
④where后面条件查询只在自己需要的范围查询
⑤select后面尽量不要使用 * 只选择你需要的字段
⑥OR改写成IN ,OR的效率是n级别,IN的效率是log(n)级别,IN的个数建议控制在200以内;
⑦能使用BETWEEN不用IN ,SELECT id FROM t WHERE num BETWEEN 1 AND 5;
⑧使用INNER JOIN代替子查询、使用EXISTS或NOT EXISTS代替IN和NOT IN等。
4.3、explain 执行计划
使用explain 执行计划进行优化,使用explain关键字可以知道MySQL是如何处理你的SQL语句以及表结构的性能瓶颈。使用方法,在select语句前加上EXPLAIN就可以了。如:
EXPLAIN select * from TableName;
运行项目并下载源码java
运行
1
这篇文章写的很好,看这个就行[SQL优化之EXPLAIN执行计划]
5、数据库分库分表
分库:就是一个数据库分成多个数据库,部署到不同机器。
①业务量剧增,MySQL单机磁盘容量会撑爆,在高并发的场景下大量请求访问数据库,MySQL单机是扛不住的!分库可以增加磁盘容量,应对高并发,。
垂直分库:比如原来的数据库有用户表,订单表、积分表、商品表把他们拆分成用户库、订单库、积分库、商品库。
水平分库:将表的数据量切分到不同的数据库服务器上,每个服务器具有相同的库和表,只是表中的数据集合不一样。
分表:就是一个表分成多个表。
①数据量太大的话,一个查询SQL没命中索引,千百万数据量的表可能会拖垮这个数据库。
②高度为3的B+树可以存差不多千万的数据,单表数据量超过千万,就需要分表了否则磁盘IO次数会增多。
垂直分表:用户表包含id、name、age、email、desc,如果email、desc等字段不常用,我们把它拆分到另外一张表,命名为用户详细信息表。
水平分表:一个表的数据量太大,可以按照规则(如hash取模、时间范围等),把数据切分到多张表,将常用数据从表中独立出来。比如 在登录业务中只需要查询用户名和密码。
6、什么是索引,索引有几种
①索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。
②根据存储方式分为:
聚集索引(InnoDB引擎)
主键索引属于聚簇索引的叶子节点会存储指针的值和数据行,也就是说数据和索引是在一起,这就是聚簇索引,InnoDB中也只有主键索引才能是聚簇索引。
非聚集索引(MyISAM引擎)
二级索引(辅助索引)属于非聚簇索引,叶子节点只会存储数据行的指针,简单来说数据和索引不在一起,就是非聚聚簇索引;
③根据索引特点可以分为:主键索引,唯一索引,普通索引,多列索引,前缀索引,全文索引
6、索引原理
④索引就像目录,索引利用特定数据结构存储避免进行全表扫描。mysql默认索引结构B+树,特点是一个节点可以有多个分支节点,同时每个分支节点只存储key而不存储数据,只在叶子节点存储数据和key。这样大大降低了查找深度,减少了磁盘IO次数。
7、索引失效的原因/场景
①索引列作为计算的一部分或者使用函数,那么索引会失效。例如,下面这个查询无法使用 actor_ id 列的索引:
SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
运行项目并下载源码java
运行
1
②不符合最左匹配原则,例如定义了 (a,b,c) 联合索引,相当于构造了 (a)、(a,b)、(a,b,c) 索引。如果要使 c 索引实际工作,那么必须在 WHERE 中同时加入 a、b 字段的条件,顺序无所谓。
③WHERE 子句的查询条件里使用了比较操作符 LIKE和正则,第一个字符不是通配符的情况下才能使用索引。 比如查询条件是 LIKE ‘abc%’,MySQL 将使用索引,如果条件是 LIKE ‘%abc’,MYSQL 将不使用索引。
<b④查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找。常见的范围查找有:LIKE ‘%abc’,和通过 ,=, between,!=,或者 操作符做比较。它们都是导致索引失效。
注意事项:IN() 和exists不是范围匹配,而是多个等值匹配,因此并不会导致索引失效。
⑤ 如果 WHERE 子句带有 or 且有字段不属于索引,那么即使其中带索引的字段也不会使用。
⑥使用了select,大概率会查询非索引列的数据就无法使用覆盖索引了*
⑦对查询结果排序时使用order by,不符合索引结构的顺序。
⑧索引列值为null,致COUNT(*)不能走索引
查询诸如SELECT COUNT(*) FROM Table 的时候,因为HASHSET中不能存储空值的,所以优化器不会走索引。
⑨not in 和 not exists使索引失效 而IN() 和exists不是范围匹配,而是多个等值匹配,因此并不会导致索引失效。
⑩字段类型不同 ,比如你的索引字段是varchar型,但是你搜索条件却是 userid=333,那这样索引不生效。
8、聚集索引和非聚集索引的区别
聚集索引(InnoDB引擎)
主键索引属于聚簇索引的叶子节点会存储指针的值和数据行,也就是说数据和索引是在一起,这就是聚簇索引,InnoDB中也只有主键索引才能是聚簇索引。
非聚集索引(MyISAM引擎)
二级索引(辅助索引)属于非聚簇索引,叶子节点只会存储数据行的指针(innoDB是主键),简单来说数据和索引不在一起,就是非聚聚簇索引;
