当前位置:职场发展 > 阿里巴巴JAVA常考面试题及汇总答案

阿里巴巴JAVA常考面试题及汇总答案

  • 发布:2023-09-30 03:50

-->

1。 String、StringBuffer 和 StringBuilder 之间有什么区别?为什么 String 是不可变的?

答案:1、String是字符串常量,StringBuffer和StringBuilder都是字符串变量。后两者的字符内容是可变的,而前者的内容在创建后是不可变的。

2。 String 是不可变的,因为 String 类在 JDK 中被声明为 Final 类。

3。 StringBuffer是线程安全的,而StringBuilder不是线程安全的。

ps:线程安全会带来额外的系统开销,所以StringBuilder比StringBuffer效率更高。如果你非常熟悉系统中的线程是否安全,可以使用StringBuffer,并在线程不安全的地方添加关键字Synchronize。

2。 Vector、ArrayList 和 LinkedList 有什么区别?

答案:1、Vector和ArrayList以数组的形式存储在内存中,而LinkedList以链表的形式存储。

2。 List中的元素是有序的,并且允许重复元素。 Set中的元素是无序的,不允许有重复的元素。

3。 Vector 线程是同步的,而 ArrayList 和 LinkedList 线程不是同步的。

4。 LinkedList适合指定位置的插入和删除操作,但不适合搜索; ArrayList和Vector适合搜索,但不适合指定位置的插入和删除操作。

5。当元素填满容器时,ArrayList 会自动扩展容器大小的 50%,而 Vector 会扩展 100%,因此 ArrayList 节省更多空间。

详情请见:http://www.sychzs.cn/mgod/archive/2007/08/05/844011.html

3。 HashTable、HashMap 和 TreeMap 有什么区别?

答案:1.HashTable是线程同步的,HashMap是非线程同步的。

2。 HashTable不允许<键,值>有空值,HashMap允许<键,值>有空值。

3。 HashTable使用Enumeration,HashMap使用Iterator。

4。 HashTable中的哈希数组默认大小为11,递增方式为old*2+1。 HashMap中哈希数组默认大小为16,递增方式必须是2的指数倍。

5。 TreeMap可以根据key对其保存的记录进行排序,默认是升序排列。

详情请见:http://www.sychzs.cn/?p=679

5。 Tomcat、Apache 和 JBoss 之间有什么区别?

答案:1. Apache是​​HTTP服务器,Tomcat是Web服务器,JBoss是应用服务器。

2。 Apache解析静态html文件; Tomcat可以解析jsp动态页面,也可以充当servlet容器。

详情请参阅:http://www.sychzs.cn/blog/1195780

补充:

Nginx是一个高性能的HTTP和反向代理服务器,以及IMAP/POP3/SMTP服务器。它和apache可以简单理解为只提供html静态文件服务。对php等动态语言的支持依赖于扩展。

总结:Tomcat对静态资源的请求效率太低。一般使用ngxin/lighttpd(apache)作为前端,它只是将jsp请求转发给tomcat。

6。 GET 和 POST 有什么区别?

答:基础知识:HTTP请求格式如下。

                                              用于描述服务器要使用的附加信息

[] 请求的内容数据

差异:

1。 Get从服务器获取数据,Post向服务器发送数据。

2。在客户端,Get方法通过URL提交数据。您可以在 URL 地址栏中看到请求消息。消息被编码; Post 数据在 Html body 中提交。

3。对于Get方法,服务器使用Request.QueryString来获取变量的值;对于Post方法,服务器使用Request.Form来获取提交的数据值。

4。 Get方法提交的数据最大可以为1024字节,而Post方法则没有限制。

5。 Get方法提交的参数和参数值会显示在地址栏中,这是不安全的,但Post不会,更安全。

详情请见:http://www.sychzs.cn/wxf0701/archive/2008/08/17/1269798.html


7。 Session 和 Cookie 的区别

点击查看

8。 Servlet 生命周期

答:大致分为4部分:Servlet类加载-->实例化-->服务-->销毁

下图是Tomcat中的Servlet时序图。

1。 Web 客户端向 Servlet 容器(Tomcat)发送 Http 请求。

2。 Servlet容器接收来自Client的请求。

3。 Servlet容器创建一个HttpRequest对象,并将Client的请求信息封装到该对象中。

4。 Servlet 容器创建一个 HttpResponse 对象。

5。 Servlet容器将HttpRequest对象和HttpResponse对象作为参数传递给HttpServlet对象,并调用HttpServlet对象的service方法。

6。 HttpServlet调用HttpRequest对象的方法,获取Http请求,并进行相应的处理。

7。处理完成后,HttpServlet调用HttpResponse对象的方法并返回响应数据。

8。 Servlet容器将HttpServlet的响应结果传回客户端。

其中三个方法说明了 Servlet 的生命周期:

1。 init():负责初始化Servlet对象。

2。 service():负责响应客户端请求。

3。 destroy():负责在Servlet对象退出时释放占用的资源。

详情请参阅:http://www.sychzs.cn/blog/342996

9。 HTTP消息包含内容

答:主要包含四个部分:

1。请求专线

2、标题行

3、空行

4。请求正文

详情请见:http://www.sychzs.cn/heruiup/article/details/6072961

10。 Statement和PreparedStatement的区别,什么是SQL注入,如何防止SQL注入

答:1.PreparedStatement支持动态设置参数,Statement不支持。

2。 PreparedStatement可以避免单引号等编码麻烦,但Statement不能。

3。 PreparedStatement支持预编译,但Statement不支持。

4。当SQL语句发生错误时PreparedStatement不容易检查,而Statement则更容易检查。

5。 PreparedStatement可以防止Sql被使用,并且更安全,但Statement不能。

详情请见:http://www.sychzs.cn/zsm653983/article/details/7296609

什么是SQL注入:

一种通过sql语句的拼接来达到无参数查询数据库数据目的的方法。

例如要执行的sql语句是select * from table
where name = "+appName+",利用appName参数值的输入生成恶意sql语句。比如传入['or'1'='1'],就可以在数据库中执行。

因此,可以使用PrepareStatement来避免Sql注入。服务器端接收到参数数据后进行验证。这个时候PrepareStatement会自动检测,但是Statement就不行了,需要手动检测。

11。 sendRedirect和forward的区别

解答:1.forward是服务器端控制页面重定向,重定向后的地址不会显示在客户端的浏览器地址中; sendRedirect 是完全跳转,浏览器中会显示重定向的地址
新的发送请求链接。

原理:转发就是服务器请求资源的时候。服务器直接访问目标地址的URL,读取该URL的响应内容,然后将内容返回给浏览器。浏览器根本不知道
服务器发送的内容从哪里来,所以地址栏还是原来的地址。

重定向是指服务器根据逻辑发送一个状态码,告诉浏览器重新请求该地址。浏览器将使用刚才的所有参数重新发送新请求。

详情请见:http://www.sychzs.cn/blog/1056564

12。关于JAVA内存模型,一个对象(两个属性,四个方法)被实例化100次。当前内存中的存储状态是几个对象,几个属性,几个方法。

答案:

由于JAVA中新的对象都是放在堆中的,如果要实例化100次,就会在堆中生成100个对象。一般来说,对象及其属性和方法属于一个整体,但是如果
属性和方法是静态的,即用static关键字声明它们,因此属于该类的属性和方法在内存中永远只会存在一份副本。

详情请见:http://www.sychzs.cn/z/q252323203.htm?sp=4000

13。下面讲一下对Hibernate的理解,一级和二级缓存的作用,以及Hibernate在项目中如何使用缓存。

答案:Hibernate 是一个开发的对象关系映射框架(ORM)。它具有对 JDBC 非常对象的封装,Hibernate 允许程序员以面向对象的方式操作关系数据库。

休眠的优点:

1。程序更加面向对象

2。提高生产力

3。易于移植

4。非侵入性。

缺点:

1。效率比JDBC稍差

2。不适合批量操作

3。只能配置一种关联关系

Hibernate 有四种查询方式:

1。 Get和load方法根据ID号查询对象。

2。 Hibernate 查询语言

3。标准查询语言

4。通过sql查询

休眠如何工作:

1。配置hibernate对象关系映射文件并启动服务器

2。服务器通过实例化Configuration对象来读取hibernate.cfg.xml文件的配置内容,并根据相关需求建表以及表之间的映射关系。

3。通过实例化的Configuration对象创建SeesionFactory实例,通过SessionFactory实例创建Session对象。

4。通过Seesion对象完成数据库的增删改查操作。

休眠状态传输

临时状态(瞬态)

1。不在会话缓存中

2。数据库中没有对象记录

java如何进入临时状态: 1.通过new语句创建对象时。 2. 刚调用session的delete方法时,会从session缓存中删除一个对象。

持续状态(持续)

1。在会话缓存中

2。持久对象数据库中没有对象记录

3。 Seesion 会保存两者在特定时刻的同步

Java中如何进入持久化状态: 1. Seesion的save()方法。 2. seesion的load().get()方法返回的对象。 3. seesion 的 find() 方法返回的列表集合中存储的对象。 4.Session的update().save()方法。

分离

1。它不再位于会话缓存中

2。自由对象是由持久态转变而来的,数据库中没有对应的记录。

Java中如何进入徘徊状态: 1.Session的close()。 Session 的 evict() 方法从缓存中删除一个对象。

详情如下图:

Hibernate中的缓存主要包括Session缓存(一级缓存)和SessionFactory缓存(二级缓存,一般由第三方提供)。

详情请见:http://www.sychzs.cn/xiaoluo501395377/p/3377604.html
http://www.sychzs.cn/xinyu0100/article/details/5619715

14。我们来说说反射,主要是概念,哪里需要反射机制,反射的性能,以及如何优化

答案:反射机制定义:

在运行状态下,对于任何一个类,都可以知道该类的所有属性和方法,对于任何对象,都可以通过反射机制调用某个类的任意方法。这种动态获取类信息并动态调用类对象方法的功能就称为Java的反射机制。

反射的作用:

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

2。应用程序需要在运行时从特定程序集加载特定类

详情请见:http://www.sychzs.cn/nieweilin/article/details/5908165

15。来说说Hibernate和Ibatis的区别,哪个性能更高

答案:1.Hibernate更喜欢对象操作来达到数据库相关操作的目的;而ibatis更喜欢对SQL语句的优化。

2。 Hibernate使用的查询语句是它自己的hql,而ibatis是标准的sql语句。

3。 Hibernate相对复杂,难学; ibatis类似于sql语句,简单易学。

表现:

1。如果系统数据处理量巨大,对性能要求极高,往往需要手动编写高性能的SQL语句或者错误存储过程。此时ibatis的可控性更好,因此其性能比Hibernate要好。

2。相同需求下,由于hibernate可以自动生成hql语句,而ibatis需要手动编写sql语句,因此使用hibernate的效率要高于ibatis。
详情参见:http://www.sychzs.cn/cdh1213/article/details/5967405
http://www.sychzs.cn/view/06c4486aaf1ffc4ffe47acfb.html

16。您对Spring的理解如何,在项目中使用了什么?如何使用它? IOC、AOP的理解和实现原理

答案:

Spring是一个开源框架,位于MVC模型的Model层。它可以响应需求的快速变化。主要原因是它具有面向方面编程(AOP)的优势。其次,提高了系统性
是的,因为通过
依赖反转机制(IOC),系统中使用的对象并不是在系统加载时全部实例化,而是只有在调用该类时才实例化该类的对象,从而提高系统性能。这两个人都很优秀
性能
这使得Spring受到很多J2EE公司的青睐。比如阿里巴巴最常用的技术就是Spring相关的技术。

Spring 的优点:

1。减少组件之间的耦合,实现软件层之间的解耦。

2。您可以使用许多轻松提供的服务,例如事务管理、消息服务、日志记录等。

3。容器提供了AOP技术,可以方便地用来实现权限拦截、运行时监控等功能。

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

Spring中的

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

详情请参阅:http://www.sychzs.cn/content/08/0113/20/7635_970484.shtml
http://www.sychzs.cn/?p=694

17。线程同步,如何控制并发操作

答:在Java中,可以在方法名前添加关键字syschronized来处理多个线程同时访问共享资源时的问题。 syschronized 相当于一个锁。当申请人申请

资源,如果资源没有被占用,则将资源交付给申请者。在此期间,其他申请者只能申请但不能使用该资源。当资源被使用时,该资源就会被释放。其他申请者可以申请上锁锁。

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

可以通过锁定来控制并发操作。锁可以分为乐观锁和悲观锁。

悲观锁:

伤心
锁观察并发模型假设系统中有足够多的数据修改操作,任何特定的读操作都可能受到单个用户所做的数据修改的影响。换句话说,悲观锁定假设冲突永远存在
发生时,通过独占正在读取的数据来避免冲突。但是独占数据会阻止其他进程修改数据,造成阻塞。读数据和写数据会互相阻塞。

乐观锁:

乐观锁假设系统数据修改只会引起很少的冲突,这意味着任何进程都不可能修改其他进程正在访问的数据。乐观并发模式下,读数据和写数据不会发生冲突,只有写数据和写数据之间会发生冲突。即读数据不会造成阻塞,只有写数据才会造成阻塞。

详情请见:http://www.sychzs.cn/ego/articles/1514350.html
http://www.sychzs.cn/niu870781892/article/details/6140755

18。描述一下struts的工作流程

答案: 1. 当Web应用程序启动时,ActionServlet被加载并初始化。 ActionServlet从struts-config.xml文件中读取配置信息并将其存储在每个配置对象中。

2。当ActionServlet收到客户请求时,它首先检索与用户请求匹配的ActionMapping实例。如果不存在,则返回无效的用户请求路径信息。

3。如果ActionForm实例不存在,则创建一个ActionForm对象,并将客户提交的表单数据保存到ActionForm对象中。

4、
根据配置信息判断表单是否需要验证。如果需要,请调用 ActionForm 的 validate() 方法。如果ActionForm的validate()方法
如果该方法返回null或者返回一个不包含ActionMessage的ActionErrors对象,则表示表单验证成功。

5。 ActionServlet 根据 ActionMapping 实例中包含的映射信息确定将请求转发到哪个 Action。如果对应的Action实例不存在,则先创建一个实例,然后调用Action的execute()方法。

6。 Action的execute()方法返回一个ActionForward对象,ActionServlet将客户请求转发到ActionForward对象指向的JSP组件。

7。 ActionForward 对象指向的 JSP 组件生成一个动态网页并将其返回给客户。

19。 Tomcat的会话处理。如果让你实现一个tomcatserver,session机制如何实现

答案:session技术

A.什么是session
实现过程:当浏览器访问服务器时,服务器会(创建一个对象(称为Session对象),该对象有一个唯一的ID号) (称为Sessionid),然后服务器通过cookie技术默认使用SESSIONID。                                                                                                         正在发送到浏览器,并且下次浏览器  访问服务器,它将                                                                                                             发送到浏览器,并且             下次浏览器访问服务器时发送到服务器。服务器会根据sessionId
        找到对应的session对象(用户的状态可以写入
                 session对象)。
B. 如何创建会话对象? ?                                                                                                                                                                                                                                                                                       flag为true:
浏览器向服务器发送请求,服务器会检查请求中是否有sessionid。如果没有,则创建一个
Session 对象。如果是的话,还得看你要找的session对象是否还存在(因为有可能服务器因为某种原因删除了session,比如超时限制
对象),如果存在则返回 , if它不存在,创建一个新的会话对象。 FLAG向服务器发送请求,以便浏览器向服务器发送请求。服务器会检查请求中是否有sessionid。仍然存在。如果存在,则返回。
                                                                                                                                                                                                                                                                                       由于 HttpSession session =
          request.getSession(true) 是等价的;
          C.session 中常用方法
         .setAttribute(String name,Object obj);
Object session.getAttribute(String name)
如果未找到,则返回 null。 E String session.getid();
D、设置session.setmaxinactiveinerval(int秒)的最大会话数;方法一:设置整个服务器的默认过期时间
                此方法会影响部署到该服务器的所有应用。 Om Tomcat_home/Conf/Web.xml

30
30
30
30



:具体应用
只需要修复更改 web.xml


30

20. 关于缓存(Ehcache、Memcached)

解答:详情请见:http://www.sychzs.cn/blog/static/2246014120106144143737/

21、SQL优化相关问题

答:这篇文章写得确实很好,值得仔细阅读,所以转载在这里。

最近由于工作需要,希望能对SQL做一个比较全面的总结
我在网上搜索了SERVER数据库性能优化相关的注意事项,找到了很多文章,有的列出了上百条。但仔细一看,我发现很多都是似是而非或者过时的(也许
SQL SERVER 6.5或ORACLE之前的版本都适用),所以我只能根据以前的经验和测试结果自己总结一下。

我始终相信,提高一个系统的性能不仅仅是试运或维护阶段的性能调优工作,也不仅仅是开发阶段的问题,而是需要贯穿整个软件的始终生命周期。这只能通过有效的工作来实现。所以我希望根据软件生命周期的不同阶段来总结一下数据库性能优化相关的注意事项。

1。分析阶段


一般来说,在系统分析阶段往往要注意的事情太多。系统的各种功能、可用性、可靠性和安全性需求往往吸引我们的大部分注意力。不过,一定要注重表现
它是非常重要的非功能性需求,其实时性要求、响应时间要求、硬件配置等必须根据系统的特点来确定。最好有针对不同需求的量化指标。

另一方面,在分析阶段应该根据各种需求区分出系统的类型,大的方面,区分是OLTP(联机事务处理系统)和OLAP(联机分析处理系统)。

二、             设计阶段

设计阶段可以说是以后系统性能的关键阶段,在这个阶段,有一个关系到以后几乎所有性能调优的过程—数据库设计。

在数据库设计完成后,可以进行初步的索引设计,好的索引设计可以指导编码阶段写出高效率的代码,为整个系统的性能打下良好的基础。

以下是性能要求设计阶段需要注意的:

1、 数据库逻辑设计的规范化

数据库逻辑设计的规范化就是我们一般所说的范式,我们可以这样来简单理解范式:

第1规范:没有重复的组或多值的列,这是数据库设计的最低要求。

第2规范: 每个非关键字段必须依赖于主关键字,不能依赖于一个组合式主关键字的某些组成部分。消除部分依赖,大部分情况下,数据库设计都应该达到第二范式。

第3规范: 一个非关键字段不能依赖于另一个非关键字段。消除传递依赖,达到第三范式应该是系统中大部分表的要求,除非一些特殊作用的表。

更高的范式要求这里就不再作介绍了,个人认为,如果全部达到第二范式,大部分达到第三范式,系统会产生较少的列和较多的表,因而减少了数据冗余,也利于性能的提高。

2、 合理的冗余

完全按照规范化设计的系统几乎是不可能的,除非系统特别的小,在规范化设计后,有计划地加入冗余是必要的。

冗余可以是冗余数据库、冗余表或者冗余字段,不同粒度的冗余可以起到不同的作用。

冗余可以是为了编程方便而增加,也可以是为了性能的提高而增加。从性能角度来说,冗余数据库可以分散数据库压力,冗余表可以分散数据量大的表的并发压力,也可以加快特殊查询的速度,冗余字段可以有效减少数据库表的连接,提高效率。

3、 主键的设计

主键是必要的,SQL SERVER的主键同时是一个唯一索引,而且在实际应用中,我们往往选择最小的键组合作为主键,所以主键往往适合作为表的聚集索引。聚集索引对查询的影响是比较大的,这个在下面索引的叙述。

在有多个键的表,主键的选择也比较重要,一般选择总的长度小的键,小的键的比较速度快,同时小的键可以使主键的B树结构的层次更少。

主键的选择还要注意组合主键的字段次序,对于组合主键来说,不同的字段次序的主键的性能差别可能会很大,一般应该选择重复率低、单独或者组合查询可能性大的字段放在前面。

4、 外键的设计

外键作为数据库对象,很多人认为麻烦而不用,实际上,外键在大部分情况下是很有用的,理由是:

外键是最高效的一致性维护方法,数据库的一致性要求,依次可以用外键、CHECK约束、规则约束、触发器、客户端程序,一般认为,离数据越近的方法效率越高。

谨慎使用级联删除和级联更新,级联删除和级联更新作为SQL SERVER 2000当年的新功能,在2005作
了保留,应该有其可用之处。我这里说的谨慎,是因为级联删除和级联更新有些突破了传统的关于外键的定义,功能有点太过强大,使用前必须确定自己已经把握好
其功能范围,否则,级联删除和级联更新可能让你的数据莫名其妙的被修改或者丢失。从性能看级联删除和级联更新是比其他方法更高效的方法。

5、 字段的设计

字段是数据库最基本的单位,其设计对性能的影响是很大的。需要注意如下:

A、数据类型尽量用数字型,数字型的比较比字符型的快很多。

B、 数据类型尽量小,这里的尽量小是指在满足可以预见的未来需求的前提下的。

C、 尽量不要允许NULL,除非必要,可以用NOT NULL+DEFAULT代替。

D、少用TEXT和IMAGE,二进制字段的读写是比较慢的,而且,读取的方法也不多,大部分情况下最好不用。

E、 自增字段要慎用,不利于数据迁移。

6、 数据库物理存储和环境的设计

在设计阶段,可以对数据库的物理存储、操作系统环境、网络环境进行必要的设计,使得我们的系统在将来能适应比较多的用户并发和比较大的数据量。

这里需要注意文件组的作用,适用文件组可以有效把I/O操作分散到不同的物理硬盘,提高并发能力。

7、 系统设计

整个系统的设计特别是系统结构设计对性能是有很大影响的,对于一般的OLTP系统,可以选择C/S结构、三层的C/S结构等,不同的系统结构其性能的关键也有所不同。

系统设计阶段应该归纳一些业务逻辑放在数据库编程实现,数据库编程包括数据库存储过程、触发器和函数。用数据库编程实现业务逻辑的好处是减少网络流量并可更充分利用数据库的预编译和缓存功能。

8、 索引的设计

在设计阶段,可以根据功能和性能的需求进行初步的索引设计,这里需要根据预计的数据量和查询来设计索引,可能与将来实际使用的时候会有所区别。

关于索引的选择,应改主意:

A、              根据数据量决定哪些表需要增加索引,数据量小的可以只有主键。

B、              根据使用频率决定哪些字段需要建立索引,选择经常作为连接条件、筛选条件、聚合查询、排序的字段作为索引的候选字段。

C、              把经常一起出现的字段组合在一起,组成组合索引,组合索引的字段顺序与主键一样,也需要把最常用的字段放在前面,把重复率低的字段放在前面。

D、              一个表不要加太多索引,因为索引影响插入和更新的速度。

三、             编码阶段

编码阶段是本文的重点,因为在设计确定的情况下,编码的质量几乎决定了整个系统的质量。

编码阶段首先是需要所有程序员有性能意识,也就是在实现功能同时有考虑性能的思想,数据库是能进行集合运算的工具,我们应该尽量的利用这个工具,所谓集合
运算实际是批量运算,就是尽量减少在客户端进行大数据量的循环操作,而用SQL语句或者存储过程代替。关于思想和意识,很难说得很清楚,需要在编程过程中
来体会。

下面罗列一些编程阶段需要注意的事项:

1、 只返回需要的数据

返回数据到客户端至少需要数据库提取数据、网络传输数据、客户端接收数据以及客户端处理数据等环节,如果返回不需要的数据,就会增加服务器、网络和客户端的无效劳动,其害处是显而易见的,避免这类事件需要注意:

A、横向来看,不要写SELECT *的语句,而是选择你需要的字段。

B、 纵向来看,合理写WHERE子句,不要写没有WHERE的SQL语句。

C、 注意SELECT INTO后的WHERE子句,因为SELECT INTO把数据插入到临时表,这个过程会锁定一些系统表,如果这个WHERE子句返回的数据过多或者速度太慢,会造成系统表长期锁定,诸塞其他进程。

D、对于聚合查询,可以用HAVING子句进一步限定返回的行。

2、 尽量少做重复的工作

这一点和上一点的目的是一样的,就是尽量减少无效工作,但是这一点的侧重点在客户端程序,需要注意的如下:

A、              控制同一语句的多次执行,特别是一些基础数据的多次执行是很多程序员很少注意的。

B、              减少多次的数据转换,也许需要数据转换是设计的问题,但是减少次数是程序员可以做到的。

C、              杜绝不必要的子查询和连接表,子查询在执行计划一般解释成外连接,多余的连接表带来额外的开销。

D、              合并对同一表同一条件的多次UPDATE,比如

  1. UPDATE EMPLOYEE SET FNAME=’HAIWER’ WHERE EMP_ID=’ VPA30890F’
  2. UPDATE EMPLOYEE SET LNAME=’YANG’ WHERE EMP_ID=’ VPA30890F’

这两个语句应该合并成以下一个语句

  1. UPDATE EMPLOYEE SET FNAME=’HAIWER’,LNAME=’YANG’
  2. WHERE EMP_ID=’ VPA30890F’

E、               UPDATE操作不要拆成DELETE操作+INSERT操作的形式,虽然功能相同,但是性能差别是很大的。

F、               不要写一些没有意义的查询,比如

SELECT * FROM EMPLOYEE WHERE 1=2

3、 注意事务和锁

事务是数据库应用中和重要的工具,它有原子性、一致性、隔离性、持久性这四个属性,很多操作我们都需要利用事务来保证数据的正确性。在使用事务中我们需要做到尽量避免死锁、尽量减少阻塞。具体以下方面需要特别注意:

A、事务操作过程要尽量小,能拆分的事务要拆分开来。

B、 事务操作过程不应该有交互,因为交互等待的时候,事务并未结束,可能锁定了很多资源。

C、 事务操作过程要按同一顺序访问对象。

D、提高事务中每个语句的效率,利用索引和其他方法提高每个语句的效率可以有效地减少整个事务的执行时间。

E、 尽量不要指定锁类型和索引,SQL SERVER允许我们自己指定语句使用的锁类型和索引,但是一般情况下,SQL
SERVER优化器选择的锁类型和索引是在当前数据量和查询条件下是最优的,我们指定的可能只是在目前情况下更有,但是数据量和数据分布在将来是会变化
的。

F、 查询时可以用较低的隔离级别,特别是报表查询的时候,可以选择最低的隔离级别(未提交读)。

4、 注意临时表和表变量的用法

在复杂系统中,临时表和表变量很难避免,关于临时表和表变量的用法,需要注意:

A、如果语句很复杂,连接太多,可以考虑用临时表和表变量分步完成。

B、 如果需要多次用到一个大表的同一部分数据,考虑用临时表和表变量暂存这部分数据。

C、 如果需要综合多个表的数据,形成一个结果,可以考虑用临时表和表变量分步汇总这多个表的数据。

D、其他情况下,应该控制临时表和表变量的使用。

E、 关于临时表和表变量的选择,很多说法是表变量在内存,速度快,应该首选表变量,但是在实际使用中发现,这个选择主要考虑需要放在临时表的数据量,在数据量较多的情况下,临时表的速度反而更快。

F、 关于临时表产生使用SELECT INTO和CREATE TABLE + INSERT
INTO的选择,我们做过测试,一般情况下,SELECT INTO会比CREATE TABLE + INSERT
INTO的方法快很多,但是SELECT
INTO会锁定TEMPDB的系统表SYSOBJECTS、SYSINDEXES、SYSCOLUMNS,在多用户并发环境下,容易阻塞其他进程,所以我
的建议是,在并发系统中,尽量使用CREATE TABLE + INSERT INTO,而大数据量的单个语句使用中,使用SELECT
INTO。

G、  注意排序规则,用CREATE
TABLE建立的临时表,如果不指定字段的排序规则,会选择TEMPDB的默认排序规则,而不是当前数据库的排序规则。如果当前数据库的排序规则和
TEMPDB的排序规则不同,连接的时候就会出现排序规则的冲突错误。一般可以在CREATE
TABLE建立临时表时指定字段的排序规则为DATABASE_DEFAULT来避免上述问题。

5、 子查询的用法

子查询是一个 SELECT 查询,它嵌套在 SELECT、INSERT、UPDATE、DELETE 语句或其它子查询中。任何允许使用表达式的地方都可以使用子查询。

子查询可以使我们的编程灵活多样,可以用来实现一些特殊的功能。但是在性能上,往往一个不合适的子查询用法会形成一个性能瓶颈。

如果子查询的条件中使用了其外层的表的字段,这种子查询就叫作相关子查询。相关子查询可以用IN、NOT IN、EXISTS、NOT EXISTS引入。

关于相关子查询,应该注意:

A、NOT IN、NOT EXISTS的相关子查询可以改用LEFT JOIN代替写法。比如:

  1. SELECT PUB_NAME
  2. FROM PUBLISHERS
  3. WHERE PUB_ID NOT IN
  4. (SELECT PUB_ID
  5. FROM TITLES
  6. WHERE TYPE = ’BUSINESS’)

可以改写成:

  1. SELECT www.sychzs.cn_NAME
  2. FROM PUBLISHERS A LEFT JOIN TITLES B
  3. ON        B.TYPE = ’BUSINESS’ AND
  4. www.sychzs.cn_ID=B. PUB_ID
  5. WHERE www.sychzs.cn_ID IS NULL
  1. SELECT TITLE
  2. FROM TITLES
  3. WHERE NOT EXISTS
  4. (SELECT TITLE_ID
  5. FROM SALES
  6. WHERE TITLE_ID = TITLES.TITLE_ID)

可以改写成:

  1. SELECT TITLE
  2. FROM TITLES LEFT JOIN SALES
  3. ON SALES.TITLE_ID = TITLES.TITLE_ID
  4. WHERE SALES.TITLE_ID IS NULL

B、 如果保证子查询没有重复 ,IN、EXISTS的相关子查询可以用INNER JOIN 代替。比如:

  1. SELECT PUB_NAME
  2. FROM PUBLISHERS
  3. WHERE PUB_ID IN
  4. (SELECT PUB_ID
  5. FROM TITLES
  6. WHERE TYPE = ’BUSINESS’)

可以改写成:

  1. SELECT DISTINCT www.sychzs.cn_NAME
  2. FROM PUBLISHERS A INNER JOIN TITLES B
  3. ON        B.TYPE = ’BUSINESS’ AND
  4. www.sychzs.cn_ID=B. PUB_ID

C、 IN的相关子查询用EXISTS代替,比如

  1. SELECT PUB_NAME
  2. FROM PUBLISHERS
  3. WHERE PUB_ID IN
  4. (SELECT PUB_ID
  5. FROM TITLES
  6. WHERE TYPE = ’BUSINESS’)

可以用下面语句代替:

  1. SELECT PUB_NAME
  2. FROM PUBLISHERS
  3. WHERE EXISTS
  4. (SELECT 1
  5. FROM TITLES
  6. WHERE TYPE = ’BUSINESS’ AND
  7. PUB_ID= www.sychzs.cn_ID)

D、不要用COUNT(*)的子查询判断是否存在记录,最好用LEFT JOIN或者EXISTS,比如有人写这样的语句:

  1. SELECT JOB_DESC FROM JOBS
  2. WHERE (SELECT COUNT(*) FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)=0

应该改成:

  1. SELECT JOBS.JOB_DESC FROM JOBS LEFT JOIN EMPLOYEE
  2. ON EMPLOYEE.JOB_ID=JOBS.JOB_ID
  3. WHERE EMPLOYEE.EMP_ID IS NULL
  1. SELECT JOB_DESC FROM JOBS
  2. WHERE (SELECT COUNT(*) FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)<>0

应该改成:

  1. SELECT JOB_DESC FROM JOBS
  2. WHERE EXISTS (SELECT 1 FROM EMPLOYEE WHERE JOB_ID=JOBS.JOB_ID)

6、 慎用游标

数据库一般的操作是集合操作,也就是对由WHERE子句和选择列确定的结果集作集合操作,游标是提供的一个非集合操作的途径。一般情况下,游标实现的功能往往相当于客户端的一个循环实现的功能,所以,大部分情况下,我们把游标功能搬到客户端。

游标是把结果集放在服务器内存,并通过循环一条一条处理记录,对数据库资源(特别是内存和锁资源)的消耗是非常大的,所以,我们应该只有在没有其他方法的情况下才使用游标。

另外,我们可以用SQL SERVER的一些特性来代替游标,达到提高速度的目的。

A、字符串连接的例子

这是论坛经常有的例子,就是把一个表符合条件的记录的某个字符串字段连接成一个变量。比如需要把JOB_ID=10的EMPLOYEE的FNAME连接在一起,用逗号连接,可能最容易想到的是用游标:

  1. DECLARE @NAME VARCHAR(20)
  2. DECLARE @NAME VARCHAR(1000)
  3. DECLARE NAME_CURSOR CURSOR FOR
  4. SELECT FNAME FROM EMPLOYEE WHERE JOB_ID=10 ORDER BY EMP_ID
  5. OPEN NAME_CURSOR
  6. FETCH NEXT FROM RNAME_CURSOR INTO @NAME
  7. WHILE @@FETCH_STATUS = 0
  8. BEGIN
  9. SET @NAMES = ISNULL(@NAMES+’,’,’’)+@NAME
  10. FETCH NEXT FROM NAME_CURSOR  INTO @NAME
  11. END
  12. CLOSE NAME_CURSOR
  13. DEALLOCATE NAME_CURSOR

可以如下修改,功能相同:

  1. DECLARE @NAME VARCHAR(1000)
  2. SELECT @NAMES = ISNULL(@NAMES+’,’,’’)+FNAME
  3. FROM EMPLOYEE WHERE JOB_ID=10 ORDER BY EMP_ID

B、 用CASE WHEN 实现转换的例子

很多使用游标的原因是因为有些处理需要根据记录的各种情况需要作不同的处理,实际上这种情况,我们可以用CASE WHEN语句进行必要的判断处理,而且CASE WHEN是可以嵌套的。比如:

表结构:

  1. CREATE TABLE 料件表(
  2. 料号           VARCHAR(30),
  3. 名称           VARCHAR(100),
  4. 主单位         VARCHAR(20),
  5. 单位1         VARCHAR(20),
  6. 单位1参数      NUMERIC(18,4),
  7. 单位2         VARCHAR(20),
  8. 单位2参数      NUMERIC(18,4)
  9. )
  10. GO
  11. CREATE TABLE 入库表(
  12. 时间               DATETIME,
  13. 料号               VARCHAR(30),
  14. 单位               INT,
  15. 入库数量           NUMERIC(18,4),
  16. 损坏数量           NUMERIC(18,4)
  17. )
  18. GO

其中,单位字段可以是0,1,2,分别代表主单位、单位1、单位2,很多计算需要统一单位,统一单位可以用游标实现:

  1. DECLARE @料号     VARCHAR(30),
  2. @单位   INT,
  3. @参数      NUMERIC(18,4),
  4. DECLARE CUR CURSOR FOR
  5. SELECT 料号,单位 FROM 入库表 WHERE 单位 <>0
  6. OPEN CUR
  7. FETCH NEXT FROM CUR INTO @料号,@单位
  8. WHILE @@FETCH_STATUS<>-1
  9. BEGIN
  10. IF @单位=1
  11. BEGIN
  12. SET @参数=(SELECT 单位1参数 FROM 料件表 WHERE 料号 =@料号)
  13. UPDATE 入库表 SET 数量=数量*@参数,损坏数量=损坏数量*@参数,单位=1 WHERE CURRENT OF CUR
  14. END
  15. IF @单位=2
  16. BEGIN
  17. SET @参数=(SELECT 单位1参数 FROM 料件表 WHERE 料号 =@料号)
  18. UPDATE 入库表 SET 数量=数量*@参数,损坏数量=损坏数量*@参数,单位=1 WHERE CURRENT OF CUR
  19. END
  20. FETCH NEXT FROM CUR INTO @料号,@单位
  21. END
  22. CLOSE CUR
  23. DEALLOCATE CUR

可以改写成:

  1. UPDATE A SET
  2. 数量=CASE A.单位 WHEN 1 THEN      A.数量*B. 单位1参数
  3. WHEN 2 THEN         A.数量*B. 单位2参数
  4. ELSE A.数量
  5. END,
  6. 损坏数量= CASE A.单位 WHEN 1 THEN    A. 损坏数量*B. 单位1参数
  7. WHEN 2 THEN         A. 损坏数量*B. 单位2参数
  8. ELSE A. 损坏数量
  9. END,
  10. 单位=1
  11. FROM入库表 A, 料件表 B
  12. WHERE    A.单位<>1      AND
  13. A.料号=B.料号

C、 变量参与的UPDATE语句的例子

SQL ERVER的语句比较灵活,变量参与的UPDATE语句可以实现一些游标一样的功能,比如:

  1. SELECT A,B,C,CAST(NULL AS INT) AS 序号
  2. INTO #T
  3. FROM 表
  4. ORDER BY A ,NEWID()

产生临时表后,已经按照A字段排序,但是在A相同的情况下是乱序的,这时如果需要更改序号字段为按照A字段分组的记录序号,就只有游标和变量参与的UPDATE语句可以实现了,这个变量参与的UPDATE语句如下:

  1. DECLARE @A INT
  2. DECLARE @序号 INT
  3. UPDATE #T SET
  4. @序号=CASE WHEN A=@A THEN @序号+1 ELSE 1 END,
  5. @A=A,
  6. 序号=@序号

D、如果必须使用游标,注意选择游标的类型,如果只是循环取数据,那就应该用只进游标(选项FAST_FORWARD),一般只需要静态游标(选项STATIC)。

E、 注意动态游标的不确定性,动态游标查询的记录集数据如果被修改,会自动刷新游标,这样使得动态游标有了不确定性,因为在多用户环境下,如果其他进程或者本身更改了纪录,就可能刷新游标的记录集。

7、 尽量使用索引

建立索引后,并不是每个查询都会使用索引,在使用索引的情况下,索引的使用效率也会有很大的差别。只要我们在查询语句中没有强制指定索引,索引的选择和使
用方法是SQLSERVER的优化器自动作的选择,而它选择的根据是查询语句的条件以及相关表的统计信息,这就要求我们在写SQL语句的时候尽量使得优化
器可以使用索引。

为了使得优化器能高效使用索引,写语句的时候应该注意:

A、不要对索引字段进行运算,而要想办法做变换,比如

SELECT ID FROM T WHERE NUM/2=100

应改为:

SELECT ID FROM T WHERE NUM=100*2

SELECT ID FROM T WHERE NUM/2=NUM1

如果NUM有索引应改为:

SELECT ID FROM T WHERE NUM=NUM1*2

如果NUM1有索引则不应该改。

发现过这样的语句:

  1. SELECT 年,月,金额 FROM 结余表
  2. WHERE 100*年+月=2007*100+10

应该改为:

  1. SELECT 年,月,金额 FROM 结余表
  2. WHERE 年=2007 AND
  3. 月=10

B、 不要对索引字段进行格式转换

日期字段的例子:

WHERE CONVERT(VARCHAR(10), 日期字段,120)=’2008-08-15’

应该改为

WHERE日期字段〉=’2008-08-15’         AND   日期字段<’2008-08-16’

ISNULL转换的例子:

WHERE ISNULL(字段,’’)<>’’应改为:WHERE字段<>’’

WHERE ISNULL(字段,’’)=’’不应修改

WHERE ISNULL(字段,’F’) =’T’应改为: WHERE字段=’T’

WHERE ISNULL(字段,’F’)<>’T’不应修改

C、 不要对索引字段使用函数

WHERE LEFT(NAME, 3)=’ABC’ 或者WHERE SUBSTRING(NAME,1, 3)=’ABC’

应改为:

WHERE NAME LIKE ‘ABC%’

日期查询的例子:

WHERE DATEDIFF(DAY, 日期,’2005-11-30′)=0应改为:WHERE 日期 >=’2005-11-30′ AND 日期 <’2005-12-1‘

WHERE DATEDIFF(DAY, 日期,’2005-11-30′)>0应改为:WHERE 日期 <’2005-11-30‘

WHERE DATEDIFF(DAY, 日期,’2005-11-30′)>=0应改为:WHERE 日期 <’2005-12-01‘

WHERE DATEDIFF(DAY, 日期,’2005-11-30′)<0应改为:WHERE 日期>=’2005-12-01‘

WHERE DATEDIFF(DAY, 日期,’2005-11-30′)<=0应改为:WHERE 日期>=’2005-11-30‘

D、不要对索引字段进行多字段连接

比如:

WHERE FAME+ ’.’+LNAME=‘HAIWEI.YANG’

应改为:

WHERE FNAME=‘HAIWEI’ AND LNAME=‘YANG’

8、 注意连接条件的写法

多表连接的连接条件对索引的选择有着重要的意义,所以我们在写连接条件条件的时候需要特别的注意。

A、多表连接的时候,连接条件必须写全,宁可重复,不要缺漏。

B、 连接条件尽量使用聚集索引

C、 注意ON部分条件和WHERE部分条件的区别

9、 其他需要注意的地方

经验表明,问题发现的越早解决的成本越低,很多性能问题可以在编码阶段就发现,为了提早发现性能问题,需要注意:

A、程序员注意、关心各表的数据量。

B、 编码过程和单元测试过程尽量用数据量较大的数据库测试,最好能用实际数据测试。

C、 每个SQL语句尽量简单

D、不要频繁更新有触发器的表的数据

E、 注意数据库函数的限制以及其性能

10、              学会分辩SQL语句的优劣

自己分辨SQL语句的优劣非常重要,只有自己能分辨优劣才能写出高效的语句。

A、             
查看SQL语句的执行计划,可以在查询分析其使用CTRL+L图形化的显示执行计划,一般应该注意百分比最大的几个图形的属性,把鼠标移动到其上面会显示
这个图形的属性,需要注意预计成本的数据,也要注意其标题,一般都是CLUSTERED INDEX SEEK 、INDEX SEEK
、CLUSTERED INDEX SCAN 、INDEX SCAN 、TABLE SCAN等,其中出现SCAN说明语句有优化的余地。也可以用语句

SET SHOWPLAN_ALL ON

要执行的语句

SET SHOWPLAN_ALL OFF

查看执行计划的文本详细信息。

B、               用事件探查器跟踪系统的运行,可疑跟踪到执行的语句,以及所用的时间,CPU用量以及I/O数据,从而分析语句的效率。

C、               可以用WINDOWS的系统性能检测器,关注CPU、I/O参数

四、             测试、试运行、维护阶段

测试的主要任务是发现并修改系统的问题,其中性能问题也是一个重要的方面。重点应该放在发现有性能问题的地方,并进行必要的优化。主要进行语句优化、索引优化等。

试运行和维护阶段是在实际的环境下运行系统,发现的问题范围更广,可能涉及操作系统、网络以及多用户并发环境出现的问题,其优化也扩展到操作系统、网络以及数据库物理存储的优化。

这个阶段的优花方法在这里不再展开,只说明下索引维护的方法:

A、              可以用DBCC DBREINDEX语句或者SQL SERVER维护计划设定定时进行索引重建,索引重建的目的是提高索引的效能。

B、               可以用语句UPDATE STATISTICS或者SQL SERVER维护计划设定定时进行索引统计信息的更新,其目的是使得统计信息更能反映实际情况,从而使得优化器选择更合适的索引。

C、               可以用DBCC CHECKDB或者DBCC CHECKTABLE语句检查数据库表和索引是否有问题,这两个语句也能修复一般的问题。

D、

五、             网上资料中一些说法的个人不同意见

1、 “应尽量避免在 WHERE 子句中对字段进行 NULL 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

SELECT ID FROM T WHERE NUM IS NULL

可以在NUM上设置默认值0,确保表中NUM列没有NULL值,然后这样查询:

SELECT ID FROM T WHERE NUM=0”

个人意见:经过测试,IS NULL也是可以用INDEX SEEK查找的,0和NULL是不同概念的,以上说法的两个查询的意义和记录数是不同的。

2、 “应尽量避免在 WHERE 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。”

个人意见:经过测试,<>也是可以用INDEX SEEK查找的。

3、 “应尽量避免在 WHERE 子句中使用 OR 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:

SELECT ID FROM T WHERE NUM=10 OR NUM=20

可以这样查询:

SELECT ID FROM T WHERE NUM=10

UNION ALL

SELECT ID FROM T WHERE NUM=20”

个人意见:主要对全表扫描的说法不赞同。

4、 “IN 和 NOT IN 也要慎用,否则会导致全表扫描,如:

SELECT ID FROM T WHERE NUM IN(1,2,3)

对于连续的数值,能用 BETWEEN 就不要用 IN 了:

SELECT ID FROM T WHERE NUM BETWEEN 1 AND 3”

个人意见:主要对全表扫描的说法不赞同。

5、 “如果在 WHERE
子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然
而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

SELECT ID FROM T WHERE NUM=@NUM

可以改为强制查询使用索引:

SELECT ID FROM T WITH(INDEX(索引名)) WHERE NUM=@NUM”

个人意见:关于局部变量的解释比较奇怪,使用参数如果会影响性能,那存储过程就该校除了,我坚持我上面对于强制索引的看法。

6、 “尽可能的使用 VARCHAR/NVARCHAR 代替 CHAR/NCHAR ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。”

个人意见:“在一个相对较小的字段内搜索效率显然要高些”显然是对的,但是字段的长短似乎不是由变不变长决定,而是业务本身决定。在
SQLSERVER6.5或者之前版本,不定长字符串字段的比较速度比定长的字符串字段的比较速度慢很多,所以对于那些版本,我们都是推荐使用定长字段存
储一些关键字段。而在2000版本,修改了不定长字符串字段的比较方法,与定长字段的比较速度差别不大了,这样为了方便,我们大量使用不定长字段。

7、 关于连接表的顺序或者条件的顺序的说法,经过测试,在SQL SERVER,这些顺序都是不影响性能的,这些说法可能是对ORACLE有效。

二二、oracle中 rownum与rowid的理解,一千条记录我查200到300的记录怎么查? 

答:   rowid和rownum是oracle中的概念,详见:http://www.sychzs.cn/yzxz/article/details/4547495

如何查200到300行的记录,可以通过top关键字辅助:select top 100 * from table where id is not in (select top 200 id from table);

查询n到m行记录的通用公式:select top m * from table where id is not in (select top n * from table)

二三、如何分析ORACLE的执行计划? 

答:   详见:http://www.sychzs.cn/space.php?uid=22539324&do=blog&id=2644068

二四、 DB中索引原理,种类,使用索引的好处和问题是什么? 

答:    经常需要修改的表不易对改变建立索引,因为数据库对索引进行维护需要额外的开销。对经常需要查询的大数据表需要建立索引,这样会增加

查询的效率。

索引的原理:B+树或者bitmap等。

索引的种类:

1、B*Tree:http://www.sychzs.cn/NeuqUstcIim/archive/2008/08/30/1280134.html

2、位图索引:http://www.sychzs.cn/17203031/viewspace-695055/

3、其他索引:详见:http://www.sychzs.cn/view/d4d6ee1ba76e58fafab00336.html

二五、JVM垃圾回收实现原理。垃圾回收的线程优先级。

答:   详见:http://www.sychzs.cn/blog/455883

二六、jvm 最大内存设置。设置的原理。结合垃圾回收讲讲。

答:   详见:http://www.sychzs.cn/lib/view/open1324736648468.html

-->

相关文章

热门推荐