JAVA基础

一:

1. java线程同步都有哪几种方式,Synchronized和ReentrantLock的区别

1、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候

线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,

如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断

如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

ReentrantLock获取锁定与三种方式:

a):lock(),如果获得锁,则直接返回,如果有其他线程获得锁,则一直等待进入休眠模式,直到获得锁,

b):trylock(),如果获得锁则返回true,否则返回false

c):trylock(long time,TimeUnit unit),如果获得锁,则返回true,如果别的线程正持有锁,则等待给定的时间,在等待的时间获得锁,返回true,否则返回false

d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

2,synchronized是在JVM层面实现的,不但可以通过一些工具监控synchronized的锁定,而且在代码运行时出现异常,JVM会自动释放锁定,但是使用lock则不行,lock是通过代码实现的,要保证锁定一定要被释放,必须在finally{}中使用unlock()释放锁

3,在资源竞争不是很激烈的情况下,synchronized的性能优于ReentrantLock,但是在竞争比较激烈的情况下,synchronized性能会下降几十倍,ReentrantLock会持续稳定,ReentrantLock性能优于synchronized

2. ReentrantLock底层是怎么实现的,怎么实现的超时获取锁。

答:CAS+CLH队列实现,

CAS:Compare and Swap,交换并比较,CAS有三个操作数,内存数V,预期数A,要修改的数值B,只有当预期值A与内存数V相等时候,才将内存数V修改为B,否则不进行修改,该操作是一个原子操作,被广泛应用于Java底层实现,在Java中主要sun.misc.Unsafe通过JNI调用底层CPU进行实现

ReentrantLock的基本实现可以概括为:通过CAS尝试获取锁,如果此时别的线程占有锁,则加入金CLH队列并被挂起,当锁释放时,排在CLH队列队首的线程被唤醒,然后CAS进行再次尝试获得锁,

公平锁:当还有别的线程去尝试获得锁的时候,当他发现自己不在队首的时候,会自动排到队尾,由队首获得锁

不公平锁:当还有别的线程去尝试获得锁时候,也可能被别的线程获取到锁

首先记录下调用方法的时间,然后尝试获取锁,如过获取成功则直接返回,如果获取失败,判断超时时间是否为0,如果是,则时间超时,返回false,如果不是,则获取当前时间-进入方法的时间,然后在用超时时间-这个结果,得到超时剩余时间,如果剩余超时时间大于1000毫秒,则利用LockSupport挂起当前线程剩余时长,否则进入自旋,避免超时时间精度出现偏差

 

3. cas的原理,变量要用哪个关键字修饰,volatile实现的原理,进而引申到了java虚拟机的内存模型。

CAS:Compare and Swap,交换并比较,CAS有三个操作数,内存数V,预期数A,要修改的数值B,只有当预期值A与内存数V相等时候,才将内存数V修改为B,否则不进行修改,该操作是一个原子操作,被广泛应用于Java底层实现,在Java中主要sun.misc.Unsafe通过JNI调用底层CPU进行实现

原子变量类型中的所有域都使用了volatile关键字修饰以保证内存可见性

4. 介绍一下java虚拟机内存模型,然后继续讨论volatile。

组成:java堆,java栈(即虚拟机栈),本地方法栈,方法区和程序计数器

1.程序计数器

JVM中的程序计数器是一块很小的内存区域,但是这块内存区域挺有意思的。主要特性有3个:

1、存储内容:对于java普通方法(即没用native关键字修饰的方法),存储的是执行过程中当前指令的地址,而对于native方法,这里是空的(undefined),为啥呢?因为调用本地方法的时候可能已经超出了JVM虚拟机的内存地址了。

2、线程私有的:为什么程序计数器是线程私有的?根据存储内容也好理解,假如是线程共享的,那多个线程执行的时候,都不知道自己当前线程执行的地址是哪个了,有的线程快,有的线程慢,快的执行完就进入下一步,等慢的线程执行完回来发现自己的地址都变了,岂不乱套?

3、是JVM中唯一不会报内存溢出(OutOfMemoryError)的区域。

2、虚拟机栈

 

虚拟机栈主要存储的是一个个栈帧,每个栈帧中存储的是局部变量表,操作数栈,动态链接和方法出口信息等。其中局部变量表中存储的是方法中定义的一些局部变量,对象的引用,参数,和方法的返回地址等。局部变量表所占用的空间大小在编译期就能确定,在方法运行的时候,并不会改变局部变量表的空间大小,这结合局部变量表存储的内容就很好理解。操作数栈可以理解为对当前操作的数据入出栈,对于64位长度的long和double类型,每个操作数占用2个字宽(slot),其他类型的操作数占用一个字宽(slot)。每个方法调用时都会创建一个栈帧,执行的过程对应的就是一个栈帧在虚拟机栈中从入栈到出栈的过程。有关栈帧的内容可以参考一个网友写的一篇博客:https://blog.csdn.net/xtayfjpk/article/details/41924283,讲的很好很详细。这里放个栈帧的图,看了一目了然。

关于虚拟机栈内存溢出有2种情况:

1、线程请求的栈深度 超过了虚拟机允许的深度,会抛出StackOverflowError,所以当我们在代码中看到这个异常时,就应该想到可能是虚拟机栈出了问题。

2、如果虚拟机栈可以动态扩展(当前大部分JVM都可以动态扩展,不过JVM也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时,会抛出OutOfMemoryError异常。

3.本地方法栈

这块知识点比较简单,本地方法栈和虚拟机栈的功能类似,只不过是为JVM调用native方法时服务的,而且JVM对本地方法使用的语言(比如Java调用C语言实现的功能,就需要定义native方法来实现)、使用方式和数据结构都没有强制规定,因此不同的虚拟机可以自由实现。而且HotSpot虚拟机直接把本地方法栈和虚拟机栈合二为一。与虚拟机栈类似,本地方法栈也会抛出StackOverflowError和OutOfMemoryError。

4、方法区

方法区是一个比较重要的区域,java虚拟机规范中把方法区描述为堆的一个逻辑部分,但是为了和Heap(堆区)对应,也称Non-Heap(非堆区)。主要存储的是静态变量,常量(包括运行时常量),类的加载信息和java编译后的代码。这部分空间不需要连续,可以选择固定大小和可扩展,通常在这部分是没有GC的,因为GC回收的都是些静态变量,常量和类的加载信息,这些对象回收效果通常不尽人意,因此可以选择不实现垃圾回收。这块区域也称为持久代,当这块内存不足时,也会报OutOfMemoryError异常。

5、堆区

Java堆区是JVM内存中最胖的一块区域,因为这里存储的都是对象的实例和数组对象。这块区域是线程共享的,在JVM启动时就会创建,想想如果这么大的空间是线程私有的,那内存不得爆掉吗?按照java虚拟机规范,堆区的内容可以物理上不连续,只要逻辑上连续即可,在实现时可以是固定大小的,也可以是可扩展的,而且通常都是可扩展的,我们常用的内存参数-Xms和-Xmx就是用来调节堆大小的。java堆区按生命周期不同,分为新生代和老年代。新生代又可以细分为Eden和Survivor区,而Survivor又可以细分为Survivor1和Survivor2,这两者通常只使用其中一块,另一块用来GC时保留存活的对象。大部分的new出来的对象都是存放在Eden区,如果是大对象,比如一个很大的数组或者List对象,可以通过JVM参数-XX:PretenureSizeThreshold将超过指定大小的对象直接存入到老年代,需要注意的是,写程序时应该尽量避免朝生夕死的大对象进入老年代,因为相比年轻代的GC,老年代GC的成本更大。Eden和Survivor的默认大小比值的8:1:1,新生代默认的GC算法是复制算法。老年代的默认GC算法是标记整理法。关于这2种GC算法,会在下篇博客讲解。

5. 线程池种类,哪四种 workqueue分别是什么

Executors的四种线程池

1、newFixedThreadPool 定长线程池

一个有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快。正规的并发线程,多用于服务器。固定的线程数由系统资源设置。核心线程是没有超时机制的,队列大小没有限制,除非线程池关闭了核心线程才会被回收。

2、newCachedThreadPool 可缓冲线程池

只有非核心线程,最大线程数很大,每新来一个任务,当没有空余线程的时候就会重新创建一个线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收,它可以一定程序减少频繁创建/销毁线程,减少系统开销,适用于执行时间短并且数量多的任务场景。

3、ScheduledThreadPool 周期线程池

创建一个定长线程池,支持定时及周期性任务执行,通过过schedule方法可以设置任务的周期执行

4、newSingleThreadExecutor 单任务线程池

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行,每次任务到来后都会进入阻塞队列,然后按指定顺序执行。

Executors工厂创建线程池

newCachedThreadPool:

 

创建一个可缓存线程池

 

优点:很灵活,弹性的线程池线程管理,用多少线程给多大的线程池,不用后及时回收,用则新建

 

缺点:一旦线程无限增长,会导致内存溢出。

 

newFixedThreadPool :

 

优点:创建一个固定大小线程池,超出的线程会在队列中等待。

 

缺点:不支持自定义拒绝策略,大小固定,难以扩展

 

newScheduledThreadPool :

 

优点:创建一个固定大小线程池,可以定时或周期性的执行任务。

 

缺点:任务是单线程方式执行,一旦一个任务失败其他任务也受影响

 

newSingleThreadExecutor :

 

优点:创建一个单线程的线程池,保证线程的顺序执行

 

缺点:不适合并发。。不懂为什么这种操作要用线程池。。为什么不直接用队列

 

统一缺点:不支持自定义拒绝策略。

 

ThreadPoolExecutor线程池参数

corepollsize : 核心池的大小,默认情况下,在创建线程池后,每当有新的任务来的时候,如果此时线程池中的线程数小于核心线程数,就会去创建一个线程执行(就算有空线程也不复用),当创建的线程数达到核心线程数之后,再有任务进来就会放入任务缓存队列中

 

Maximumpoolsize : 线程池中最多可以创建的线程数

 

keeplivetime : 线程空闲状态时,最多保持多久的时间会终止。默认情况下,当线程池中的线程数大于corepollsize 时,才会起作用 ,直到线程数不大于 corepollsize 。

 

workQuque: 阻塞队列,用来存放等待的任务

 

rejectedExecutionHandler :任务拒绝处理器(这个注意一下),有四种

ThreadPoolExecutor优势

第一:降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗

第二:提高响应速度,当任务到达时任务不需要等到线程创建就能立即执行

第三:提高线程的可管理性,使用线程池进行统一的分配,调优和监控

 

6. 反射讲一讲,主要是概念,都在哪需要反射机制,反射的性能,如何优化

答:反射机制的定义:是在运行状态中,对于任意的一个类,都能够知道这个类的所有属性和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息及动态调用类对象方法的功能称为java的反射机制。

 

反射的作用:

 

1、动态地创建类的实例,将类绑定到现有的对象中,或从现有的对象中获取类型。

 

2、应用程序需要在运行时从某个特定的程序集中载入一个特定的类

7. 什么时候触发minor GC 什么时候触发full GC

 

8. 聊点数据库,一般选什么样的字段做主键,有什么选取原则吗,用种子自增来做主键,为什么每次种子要加1,加2加3可以吗。

自增主键

这种方式是使用数据库提供的自增数值型字段作为自增主键,它的优点是:

(1)数据库自动编号,速度快,而且是增量增长,按顺序存放,对于检索非常有利;

(2)数字型,占用空间小,易排序,在程序中传递也方便;

(3)如果通过非系统增加记录时,可以不用指定该字段,不用担心主键重复问题。

其实它的缺点也就是来自其优点,缺点如下:

(1)因为自动增长,在手动要插入指定ID的记录时会显得麻烦,尤其是当系统与其它系统集成时,需要数据导入时,很难保证原系统的ID不发生主键冲突(前提是老系统也是数字型的)。特别是在新系统上线时,新旧系统并行存在,并且是异库异构的数据库的情况下,需要双向同步时,自增主键将是你的噩梦;

(2)在系统集成或割接时,如果新旧系统主键不同是数字型就会导致修改主键数据类型,这也会导致其它有外键关联的表的修改,后果同样很严重;

(3)若系统也是数字型的,在导入时,为了区分新老数据,可能想在老数据主键前统一加一个字符标识(例如“o”,old)来表示这是老数据,那么自动增长的数字型又面临一个挑战。

 

9. sql优化有哪些思路

1.查询语句不用*

2.尽量少使用子查询,使用关联查询

3.少使用in,not in 用exists代替

4.or查询尽量使用union或者union all代替

5.减少使用表连接查询

6.建表时能使用数字就是用数字,数字类型的字段作为条件比字符串的快

7.那些可以过滤掉最大数量记录的条件必须写在WHERE子句的最末尾

8.索引优化:如果sql没有什么需要优化的话,就需要考虑加索引了

9.

10. 索引使用注意事项

一、为什么要创建索引呢(优点)?

这是因为,创建索引可以大大提高系统的性能。

第一, 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。

第二, 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。

第三, 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。

第四, 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。

第五, 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

 

二、建立方向索引的不利因素(缺点)

也许会有人要问:增加索引有如此多的优点,为什么不对表中的每一个列创建一个索引呢?这种想法固然有其合理性,然而也有其片面性。虽然,索引有许多优点,但是,为表中的每一个列都增加索引,是非常不明智的。这是因为,增加索引也有许多不利的一个方面。

 

第一, 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。

第二, 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。

第三, 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。

 

三、创建方向索引的准则

索引是建立在数据库表中的某些列的上面。因此,在创建索引的时候,应该仔细考虑在哪些列上可以创建索引,在哪些列上不能创建索引。

一般来说,应该在这些列上创建索引。

第一, 在经常需要搜索的列上,可以加快搜索的速度;

第二, 在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;

第三, 在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;

第四, 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;

第五, 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;

第六, 在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。

 

同样,对于有些列不应该创建索引。一般来说,不应该创建索引的的这些列具有下列特点:

第一, 对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。

第二, 对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。

第三, 对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。

第 四, 当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少 索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。

四、创建索引的方法

创建索引有多种方法,这些方法包括直接创建索引的方法和间接创建索引的方法。

第一, 直接创建索引,例如使用CREATE INDEX语句或者使用创建索引向导。

第二, 间接创建索引,例如在表中定义主键约束或者唯一性键约束时,同时也创建了索引。

虽然,这两种方法都可以创建索引,但是,它们创建索引的具体内容是有区别的。

使 用CREATE INDEX语句或者使用创建索引向导来创建索引,这是最基本的索引创建方式,并且这种方法最具有柔性,可以定制创建出符合自己需要的索引。在使用这种方式 创建索引时,可以使用许多选项,例如指定数据页的充满度、进行排序、整理统计信息等,这样可以优化索引。使用这种方法,可以指定索引的类型、唯一性和复合 性,也就是说,既可以创建聚簇索引,也可以创建非聚簇索引,既可以在一个列上创建索引,也可以在两个或者两个以上的列上创建索引。

通过定义主 键约束或者唯一性键约束,也可以间接创建索引。主键约束是一种保持数据完整性的逻辑,它限制表中的记录有相同的主键记录。在创建主键约束时,系统自动创建 了一个唯一性的聚簇索引。虽然,在逻辑上,主键约束是一种重要的结构,但是,在物理结构上,与主键约束相对应的结构是唯一性的聚簇索引。换句话说,在物理 实现上,不存在主键约束,而只存在唯一性的聚簇索引。同样,在创建唯一性键约束时,也同时创建了索引,这种索引则是唯一性的非聚簇索引。因此,当使用约束 创建索引时,索引的类型和特征基本上都已经确定了,由用户定制的余地比较小。

当在表上定义主键或者唯一性键约束时,如果表中已经有了使用 CREATE INDEX语句创建的标准索引时,那么主键约束或者唯一性键约束创建的索引覆盖以前创建的标准索引。也就是说,主键约束或者唯一性键约束创建的索引的优先 级高于使用CREATE INDEX语句创建的索引。

五、索引的特征

索引有两个特征,即唯一性索引和复合索引。

唯 一性索引保证在索引列中的全部数据是唯一的,不会包含冗余数据。如果表中已经有一个主键约束或者唯一性键约束,那么当创建表或者修改表时,SQL Server自动创建一个唯一性索引。然而,如果必须保证唯一性,那么应该创建主键约束或者唯一性键约束,而不是创建一个唯一性索引。当创建唯一性索引 时,应该认真考虑这些规则:当在表中创建主键约束或者唯一性键约束时,SQL Server自动创建一个唯一性索引;如果表中已经包含有数据,那么当创建索引时,SQL Server检查表中已有数据的冗余性;每当使用插入语句插入数据或者使用修改语句修改数据时,SQL Server检查数据的冗余性:如果有冗余值,那么SQL Server取消该语句的执行,并且返回一个错误消息;确保表中的每一行数据都有一个唯一值,这样可以确保每一个实体都可以唯一确认;只能在可以保证实体 完整性的列上创建唯一性索引,例如,不能在人事表中的姓名列上创建唯一性索引,因为人们可以有相同的姓名。

复合索引就是一个索引创建在两个列 或者多个列上。在搜索时,当两个或者多个列作为一个关键值时,最好在这些列上创建复合索引。当创建复合索引时,应该考虑这些规则:最多可以把16个列合并 成一个单独的复合索引,构成复合索引的列的总长度不能超过900字节,也就是说复合列的长度不能太长;在复合索引中,所有的列必须来自同一个表中,不能跨 表建立复合列;在复合索引中,列的排列顺序是非常重要的,因此要认真排列列的顺序,原则上,应该首先定义最唯一的列,例如在(COL1,COL2)上的索 引与在(COL2,COL1)上的索引是不相同的,因为两个索引的列的顺序不同;为了使查询优化器使用复合索引,查询语句中的WHERE子句必须参考复合 索引中第一个列;当表中有多个关键列时,复合索引是非常有用的;使用复合索引可以提高查询性能,减少在一个表中所创建的索引数量。

六、索引的类型

根据索引的顺序与数据表的物理顺序是否相同,可以把索引分成两种类型。一种是数据表的物理顺序与索引顺序相同的聚簇索引,另一种是数据表的物理顺序与索引顺序不相同的非聚簇索引。

七、聚簇索引的体系结构

索 引的结构类似于树状结构,树的顶部称为叶级,树的其它部分称为非叶级,树的根部在非叶级中。同样,在聚簇索引中,聚簇索引的叶级和非叶级构成了一个树状结 构,索引的最低级是叶级。在聚簇索引中,表中的数据所在的数据页是叶级,在叶级之上的索引页是非叶级,索引数据所在的索引页是非叶级。在聚簇索引中,数据 值的顺序总是按照升序排列。

应该在表中经常搜索的列或者按照顺序访问的列上创建聚簇索引。当创建聚簇索引时,应该考虑这些因素:每一个表只能 有一个聚簇索引,因为表中数据的物理顺序只能有一个;表中行的物理顺序和索引中行的物理顺序是相同的,在创建任何非聚簇索引之前创建聚簇索引,这是因为聚 簇索引改变了表中行的物理顺序,数据行按照一定的顺序排列,并且自动维护这个顺序;关键值的唯一性要么使用UNIQUE关键字明确维护,要么由一个内部的 唯一标识符明确维护,这些唯一性标识符是系统自己使用的,用户不能访问;聚簇索引的平均大小大约是数据表的百分之五,但是,实际的聚簇索引的大小常常根据 索引列的大小变化而变化;在索引的创建过程中,SQL Server临时使用当前数据库的磁盘空间,当创建聚簇索引时,需要1.2倍的表空间的大小,因此,一定要保证有足够的空间来创建聚簇索引。

当 系统访问表中的数据时,首先确定在相应的列上是否存在有索引和该索引是否对要检索的数据有意义。如果索引存在并且该索引非常有意义,那么系统使用该索引访 问表中的记录。系统从索引开始浏览到数据,索引浏览则从树状索引的根部开始。从根部开始,搜索值与每一个关键值相比较,确定搜索值是否大于或者等于关键 值。这一步重复进行,直到碰上一个比搜索值大的关键值,或者该搜索值大于或者等于索引页上所有的关键值为止。

八、非聚簇索引的体系结构

非聚簇索引的结构也是树状结构,与聚簇索引的结构非常类似,但是也有明显的不同。

在非聚簇索引中,叶级仅包含关键值,而没有包含数据行。非聚簇索引表示行的逻辑顺序。 非聚簇索引有两种体系结构:一种体系结构是在没有聚簇索引的表上创建非聚簇索引,另一种体系结构是在有聚簇索引的表上创建非聚簇索引。

如 果一个数据表中没有聚簇索引,那么这个数据表也称为数据堆。当非聚簇索引在数据堆的顶部创建时,系统使用索引页中的行标识符指向数据页中的记录。行标识符 存储了数据所在位置的信息。数据堆是通过使用索引分配图(IAM)页来维护的。IAM页包含了数据堆所在簇的存储信息。在系统表sysindexes中, 有一个指针指向了与数据堆相关的第一个IAM页。系统使用IAM页在数据堆中浏览和寻找可以插入新的记录行的空间。这些数据页和在这些数据页中的记录没有 任何的顺序并且也没有链接在一起。在这些数据页之间的唯一的连接是IAM中记录的顺序。当在数据堆上创建了非聚簇索引时,叶级中包含了指向数据页的行标识 符。行标识符指定记录行的逻辑顺序,由文件ID、页号和行ID组成。这些行的标识符维持唯一性。非聚簇索引的叶级页的顺序不同于表中数据的物理顺序。这些 关键值在叶级中以升序维持。

当非聚簇索引创建在有聚簇索引的表上的时候,系统使用索引页中的指向聚簇索引的聚簇键。聚簇键存储了数据的位置信 息。如果某一个表有聚簇索引,那么非聚簇索引的叶级包含了映射到聚簇键的聚簇键值,而不是映射到物理的行标识符。当系统访问有非聚簇索引的表中数据时,并 且这种非聚簇索引创建在聚簇索引上,那么它首先从非聚簇索引来找到指向聚簇索引的指针,然后通过使用聚簇索引来找到数据。

当需要以多种方式检索数据时,非聚簇索引是非常有用的。当创建非聚簇索引时,要考虑这些情况:在缺省情况下,所创建的索引是非聚簇索引;在每一个表上面,可以创建不多于249个非聚簇索引,而聚簇索引最多只能有一个。

系统如何访问表中的数据

一 般地,系统访问数据库中的数据,可以使用两种方法:表扫描和索引查找。第一种方法是表扫描,就是指系统将指针放置在该表的表头数据所在的数据页上,然后按 照数据页的排列顺序,一页一页地从前向后扫描该表数据所占有的全部数据页,直至扫描完表中的全部记录。在扫描时,如果找到符合查询条件的记录,那么就将这 条记录挑选出来。最后,将全部挑选出来符合查询语句条件的记录显示出来。第二种方法是使用索引查找。索引是一种树状结构,其中存储了关键字和指向包含关键 字所在记录的数据页的指针。当使用索引查找时,系统沿着索引的树状结构,根据索引中关键字和指针,找到符合查询条件的的记录。最后,将全部查找到的符合查 询语句条件的记录显示出来。

在SQL Server中,当访问数据库中的数据时,由SQL Server确定该表中是否有索引存在。如果没有索引,那么SQL Server使用表扫描的方法访问数据库中的数据。查询处理器根据分布的统计信息生成该查询语句的优化执行规划,以提高访问数据的效率为目标,确定是使用 表扫描还是使用索引。

九、索引的选项

在创建索引时,可以指定一些选项,通过使用这些选项,可以优化索引的性能。这些选项包括FILLFACTOR选项、PAD_INDEX选项和SORTED_DATA_REORG选项。

使 用FILLFACTOR选项,可以优化插入语句和修改语句的性能。当某个索引页变满时,SQL Server必须花费时间分解该页,以便为新的记录行腾出空间。使用FILLFACTOR选项,就是在叶级索引页上分配一定百分比的自由空间,以便减少页 的分解时间。当在有数据的表中创建索引时,可以使用FILLFACTOR选项指定每一个叶级索引节点的填充的百分比。缺省值是0,该数值等价于100。在 创建索引的时候,内部索引节点总是留有了一定的空间,这个空间足够容纳一个或者两个表中的记录。在没有数据的表中,当创建索引的时候,不要使用该选项,因 为这时该选项是没有实际意义的。另外,该选项的数值在创建时指定以后,不能动态地得到维护,因此,只应该在有数据的表中创建索引时才使用。

PAD_INDEX 选项将FILLFACTOR选项的数值同样也用于内部的索引节点,使内部的索引节点的填充度与叶级索引的节点中的填充度相同。如果没有指定 FILLFACTOR选项,那么单独指定PAD_INDEX选项是没有实际意义的,这是因为PAD_INDEX选项的取值是由FILLFACTOR选项的 取值确定的。

当创建聚簇索引时,SORTED_DATA_REORG选项清除排序,因此可以减少建立聚簇索引所需要的时间。当在一个已经变成 碎块的表上创建或者重建聚簇索引时,使用SORTED_DATA_REORG选项可以压缩数据页。当重新需要在索引上应用填充度时,也使用该选项。当使用 SORTED_DATA_REORG选项时,应该考虑这些因素:SQL Server确认每一个关键值是否比前一个关键值高,如果都不高,那么不能创建索引;SQL Server要求1.2倍的表空间来物理地重新组织数据;使用SORTED_DATA_REORG选项,通过清除排序进程而加快索引创建进程;从表中物理 地拷贝数据;当某一个行被删除时,其所占的空间可以重新利用;创建全部非聚簇索引;如果希望把叶级页填充到一定的百分比,可以同时使用 FILLFACTOR选项和SORTED_DATA_REORG选项。

十、索引的维护

为了维护系统性能,索引在创建之后,由于频繁地对数据进行增加、删除、修改等操作使得索引页发生碎块,因此,必须对索引进行维护。

使 用DBCC SHOWCONTIG语句,可以显示表的数据和索引的碎块信息。当执行DBCC SHOWCONTIG语句时,SQL Server浏览叶级上的整个索引页,来确定表或者指定的索引是否严重碎块。DBCC SHOWCONTIG语句还能确定数据页和索引页是否已经满了。当对表进行大量的修改或者增加大量的数据之后,或者表的查询非常慢时,应该在这些表上执行 DBCC SHOWCONTIG语句。当执行DBCC SHOWCONTIG语句时,应该考虑这些因素:当执行DBCC SHOWCONTIG语句时,SQL Server要求指定表的ID号或者索引的ID号,表的ID号或者索引的ID号可以从系统表sysindexes中得到;应该确定多长时间使用一次 DBCC SHOWCONTIG语句,这个时间长度要根据表的活动情况来定,每天、每周或者每月都可以。

使用DBCC DBREINDEX语句重建表的一个或者多个索引。当希望重建索引和当表上有主键约束或者唯一性键约束时,执行DBCC DBREINDEX语句。除此之外,执行DBCC DBREINDEX语句还可以重新组织叶级索引页的存储空间、删除碎块和重新计算索引统计。当使用执行DBCC DBREINDEX语句时,应该考虑这些因素:根据指定的填充度,系统重新填充每一个叶级页;使用DBCC DBREINDEX语句重建主键约束或者唯一性键约束的索引;使用SORTED_DATA_REORG选项可以更快地创建聚簇索引,如果没有排列关键值, 那么不能使用DBCC DBREINDEX语句;DBCC DBREINDEX语句不支持系统表。另外,还可以使用数据库维护规划向导自动地进行重建索引的进程。

统计信息是存储在SQL Server中的列数据的样本。这些数据一般地用于索引列,但是还可以为非索引列创建统计。SQL Server维护某一个索引关键值的分布统计信息,并且使用这些统计信息来确定在查询进程中哪一个索引是有用的。查询的优化依赖于这些统计信息的分布准确 度。查询优化器使用这些数据样本来决定是使用表扫描还是使用索引。当表中数据发生变化时,SQL Server周期性地自动修改统计信息。索引统计被自动地修改,索引中的关键值显著变化。统计信息修改的频率由索引中的数据量和数据改变量确定。例如,如 果表中有10000行数据,1000行数据修改了,那么统计信息可能需要修改。然而,如果只有50行记录修改了,那么仍然保持当前的统计信息。除了系统自 动修改之外,用户还可以通过执行UPDATE STATISTICS语句或者sp_updatestats系统存储过程来手工修改统计信息。使用UPDATE STATISTICS语句既可以修改表中的全部索引,也可以修改指定的索引。

使用SHOWPLAN和STATISTICS IO语句可以分析索引和查询性能。使用这些语句可以更好地调整查询和索引。SHOWPLAN语句显示在连接表中使用的查询优化器的每一步以及表明使用哪一 个索引访问数据。使用SHOWPLAN语句可以查看指定查询的查询规划。当使用SHOWPLAN语句时,应该考虑这些因素。SET SHOWPLAN_ALL语句返回的输出结果比SET SHOWPLAN_TEXT语句返回的输出结果详细。然而,应用程序必须能够处理SET SHOWPLAN_ALL语句返回的输出结果。SHOWPLAN语句生成的信息只能针对一个会话。如果重新连接SQL Server,那么必须重新执行SHOWPLAN语句。STATISTICS IO语句表明输入输出的数量,这些输入输出用来返回结果集和显示指定查询的逻辑的和物理的I/O的信息。可以使用这些信息来确定是否应该重写查询语句或者 重新设计索引。使用STATISTICS IO语句可以查看用来处理指定查询的I/O信息。

就象SHOWPLAN语句一样,优化器隐藏也用来 调整查询性能。优化器隐藏可以对查询性能提供较小的改进,并且如果索引策略发生了改变,那么这种优化器隐藏就毫无用处了。因此,限制使用优化器隐藏,这是 因为优化器隐藏更有效率和更有柔性。当使用优化器隐藏时,考虑这些规则:指定索引名称、当index_id为0时为使用表扫描、当index_id为1时 为使用聚簇索引;优化器隐藏覆盖查询优化器,如果数据或者环境发生了变化,那么必须修改优化器隐藏。

十一、索引调整向导

索引调整向导是一种工具,可以分析一系列数据库的查询语句,提供使用一系列数据库索引的建议,优化整个查询语句的性能。对于查询语句,需要指定下列内容:

查询语句,这是将要优化的工作量

包含了这些表的数据库,在这些表中,可以创建索引,提高查询性能。

在分析中使用的表

在分析中,考虑的约束条件,例如索引可以使用的最大磁盘空间

这 里指的工作量,可以来自两个方面:使用SQL Server捕捉的轨迹和包含了SQL语句的文件。索引调整向导总是基于一个已经定义好的工作量。如果一个工作量不能反映正常的操作,那么它建议使用的索 引不是实际的工作量上性能最好的索引。索引调整向导调用查询分析器,使用所有可能的组合评定在这个工作量中每一个查询语句的性能。然后,建议在整个工作量 上可以提高整个查询语句的性能的索引。如果没有供索引调整向导来分析的工作量,那么可以使用图解器立即创建它。一旦决定跟踪一条正常数据库活动的描述样 本,向导能够分析这种工作量和推荐能够提高数据库工作性能的索引配置。

索引调整向导对工作量进行分析之后,可以查看到一系列的报告,还可以使该向导立即创建所建议的最佳索引,或者使这项工作成为一种可以调度的作业,或者生成一个包含创建这些索引的SQL语句的文件。

 

 

 

二、对Spring的理解,项目中都用什么?怎么用的?对IOC、和AOP的理解及实现原理

 

答:Spring是一个开源框架,处于MVC模式中的控制层,它能应对需求快速的变化,其主要原因它有一种面向切面编程(AOP)的优势,其次它提升了系统性能,因为通过依赖倒置机制(IOC),系统中用到的对象不是在系统加载时就全部实例化,而是在调用到这个类时才会实例化该类的对象,从而提升了系统性能。这两个优秀的性能使得Spring受到许多J2EE公司的青睐,如阿里里中使用最多的也是Spring相关技术。

 

Spring的优点:

 

1、降低了组件之间的耦合性,实现了软件各层之间的解耦。

 

2、可以使用容易提供的众多服务,如事务管理,消息服务,日志记录等。

 

3、容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。

 

Spring中AOP技术是设计模式中的动态代理模式。只需实现jdk提供的动态代理接口InvocationHandler,所有被代理对象的方法都由InvocationHandler接管实际的处理任务。面向切面编程中还要理解切入点、切面、通知、织入等概念。

 

Spring中IOC则利用了Java强大的反射机制来实现。所谓依赖注入即组件之间的依赖关系由容器在运行期决定。其中依赖注入的方法有两种,通过构造函数注入,通过set方法进行注入。

 

三、线程同步,并发操作怎么控制

 

答:

 

Java中可在方法名前加关键字syschronized来处理当有多个线程同时访问共享资源时候的问题。syschronized相当于一把锁,当有申请者申请该

 

资源时,如果该资源没有被占用,那么将资源交付给这个申请者使用,在此期间,其他申请者只能申请而不能使用该资源,当该资源被使用完成后将释放该资源上的锁,其他申请者可申请使用。

 

并发控制主要是为了多线程操作时带来的资源读写问题。如果不加以空间可能会出现死锁,读脏数据、不可重复读、丢失更新等异常。

 

并发操作可以通过加锁的方式进行控制,锁又可分为乐观锁和悲观锁。

 

悲观锁:

 

悲观锁并发模式假定系统中存在足够多的数据修改操作,以致于任何确定的读操作都可能会受到由个别的用户所制造的数据修改的影响。也就是说悲观锁假定冲突总会发生,通过独占正在被读取的数据来避免冲突。但是独占数据会导致其他进程无法修改该数据,进而产生阻塞,读数据和写数据会相互阻塞。

 

乐观锁:

 

乐观锁假定系统的数据修改只会产生非常少的冲突,也就是说任何进程都不大可能修改别的进程正在访问的数据。乐观并发模式下,读数据和写数据之间不会发生冲突,只有写数据与写数据之间会发生冲突。即读数据不会产生阻塞,只有写数据才会产生阻塞。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值