hashMap、hashtable、ConcurrentHashMap、hashset的区别
hashMap
1)允许空值和空健
2)线程不安全,效率高于hashtable
3)HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey
3)HashMap中hash数组的默认大小是16,而且一定是2的指数。
4)JDK1.2出现的
hashtable
1)不允许空健和空值
2)线程安全,效率低于hashMap
3)HashTable中hash数组默认大小是11,增加的方式是 old*2+1
3)JDK1.0出现的
ConcurrentHashMap
1)一个ConcurrentHashMap由多个segment组成,每一个segment都包含了一个HashEntry数组的hashtable
2)每一个segment包含了对自己的hashtable的操作,比如get,put,replace等操作,这些操作发生的时候,对自己的hashtable进行锁定。由于每一个segment写操作只锁定自己的hashtable,所以可能存在多个线程同时写的情况,性能无疑好于只有一个hashtable锁定的情况 , 高并发
hashset
1)只能放入单独的对象
2)放入的元素无序
3)放入的元素不能够重复
4)HashSet 底层采用 HashMap 来保存所有元素 所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
将一个字符串反转:
System.out.println(new StringBuilder("i love you ").reverse().toString());
String str="i Love you";
List list = new ArrayList();
String splitStr []=str.split(" ");
list=Arrays.asList(splitStr);
Collections.reverse(list);
for(String word:list){
System.out.print(word+" ");
volatile、synchronized、final、wait、notify的含义
Volatile:
Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
volatile是变量修饰符,用在多线程,同步变量。 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动作时才进行A和B的同步。因此存在A和B不一致的情况。volatile就是用来避免这种情况的。volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)
在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间,在一个线程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。
一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。
public class StoppableTask extends Thread {
private volatile boolean pleaseStop;
public void run() {
while (!pleaseStop) {
// do some stuff...
}
}
public void tellMeToStop() {
pleaseStop = true;
}
}
假如pleaseStop没有被声明为volatile,线程执行run的时候检查的是自己的副本,就不能及时得知其他线程已经调用tellMeToStop()修改了pleaseStop的值。
Synchronized:
synchronized获得并释放监视器 ,如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行
因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。
显然synchronized要比volatile消耗更多资源。
Final:
用于修饰变量属性、方法和类
修饰变量属性: 如果变量为基本数据类型表示为常量不能修改其值;如果为引用类型表示变量的引用不能指向新的对象
修饰方法表示改方法不可重写
修饰类表示改类不可继承
Wait:
wait是Object 类中的方法且不能够被重写
wait方法只能放在同步的方法货同步块中,表示资源同步时线程需要等待
wait会自动释放对象锁
wait方法需要等待线程唤醒后,线程才会继续执行,靠别人来唤醒
Notify:
线程的通信,用于唤醒线程;这里wait和notify方法必须要与sysnchronized一起使用,也就是wait与notify只针对已经获得对象锁进行操作,从语法角度wait与notify必须在sysnchronized(Obj){}语句块中,从功能上讲wait就是说线程在获取对象后,主动释放对象锁,同时本地线程休眠。直到有其他线程调用对象的notify方法对对象锁进行唤醒操作,notify调用后并不是马上释放对象锁,而是在相应的sysnchronized语句块执行结束,自动释放后,jvm会在wait对象锁的线程中随机选取一段线程赋予对象锁,唤醒线程继续执行,这样提供了在线程间同步、唤醒的操作.
java内存模型并做简要说明。
JVM具有一个堆,堆是运行时数据区域,所有的实例和数组的内存均从这里进行分配,内存中又分为栈、堆、数据段等的概念.
内存模型中有主内存和工作内存,类似于电脑中的闪存和高速缓存样,线程使用的数据库在主内存中,然后对数据库的操作,为了获得更快的速度,会放到寄存器和高速缓存中,java类型模型规定所有的变量都存储在主内存中,而工作闪存会放到变量的副本拷贝,线程对所有变量的操作都在工作内存中进行,而且不同的线程不能访问对方工作内存中的变量 ,只能够通过主内存进行访问.
Java线程之间的通信由Java内存模型控制.JMM决定一个线程对共享变量的写入何时对另一个线程可见,Java的并发采用的是共享内存模型.
主内存和工作内存之间的具体交互协议,即变量如何从主内存到工作内存,然后又从工作内存回到主内存的实现细节,JAVA 内存模型定义了以下8中操作完成,虚拟机实现时,必须保证下面的每一种操作都是原子的、不可分割的
1.lock(锁定) 作用于主内存的变量,它把变量表示成为一条线程独占的状态。
2.unlock(解锁)作用于主内存的变量,它把一个处于锁定状态的变量释放出来,解锁后才能被其他线程锁定
3.read(读取):作用于主内存的变量,它把主内存的变量值传输到线程对应的工作内存中,等待load
4.load(载入) 作用于工作内存的变量, 它把从read操作从主内存中得到的变量值,放入到工作内存的变量副本中。
5.use(使用) 作用于工作内存的变量,它把工作内存的变量的值传递给执行引擎,每当虚拟机遇到一个需要使用的变量的值的字
节码指令时,将会执行这个操作。
6.assigin(赋值) 作用于工作内存的变量,它把一个从执行引擎接收到的值给工作内存的变量,每当虚拟机遇到一个给变量赋值的
字节码指令时执行这个操作。
7.store(存储) 作用于工作内存的变量,它把工作内存中的一个变量的值传送到主内存中,以便write操作
8.write(写入) 作用于主内存的变量,它把store 操作从工作内存中得到的变量值放到主内存变量中。
什么是“异步模型”,与同步模型比有什么优点。
1、 如果做一件事情是有顺序的,先做完Task1,再做Task2,最后做Task3,这类事情也是我们日常见的最多的一种情况
2、如果做一件事情并没有顺序之分,可以同时进行,每一件事情也是相对独立的,其实这就是一种同步模型。当然,其实这也是一种理想
情况,在大多数情况下,进程之间或线程之间往往要进行通信,一个任务会等待另外一个任务的返回结果,这就有些较为复杂了
3、在异步模型中,任务是交替运行的,但仍然在一个进程中,其中每个任务的运行状态都是可以被我们控制的
异步模型与同步模型有什么区别吗?
a) 同步模型中的任务交替运行,是需要多个线程协同完成的,受到程序的控制,而在异步模型中则不需要。
b) 多线程本身就受到操作系统的调度与管理,所以同步模型本身就受到操作系统控制的,而在异步模型中一个任务会一直运行下去,直到任务被运行完或者程序暂停这个任务而去执行令一个任务
c) 异步模型比同步模型简单,因为异步模型只有一个进程而且任务的停止和运行状态是可控的。而在同步模型中,我们需要把每一个任务分成很多步骤,然后再有序的把他们组合起来
d) 如果程序中会有阻塞、被强迫等待等情况,异步模型会比同步模型运行速度快, 异步程序设计的原理就是当其中一个任务被阻塞的时候,可以先去执行一些其他可以执行的任务所以一个异步程序也会被叫做“无阻塞程序”。
那什么时候,我们需要考虑使用异步模型呢?
a) 有很多任务,经常总有一个任务可以继续执行
b) 这些任务中要执行很多I/O操作
c) 这些任务大多都是独立的
列举你常用的模式,重点说明观察者模式。
单例模式、工厂模式、观察者模式
又称作”发布—订阅”模式。定义了一种一对多的依赖关系. 让多个观察者对象同时监听某一个主题对象, 这个主题对象
在状态发生变化的时候,会通知所有的观察者对象,使他们能够自动更新自己。
什么时候使用:
当一个对象的改变需要同时通知其他对象的时候, 而且它不知道具体有多少对象需要通知的时候,需要通知的对象能够动态地增加
为什么要使用观察者模式?
交互对象之间的松耦合设计使得观察者和主题之间达到一个松耦合。
观察者模式的组成:
观察者模式的静态结构中包含这样一些角色:
1)抽象主题角色subject:
2)抽象观察者角色Observer
3)具体主题角色(concreteSubject)
4)具体观察者(ConcreteObserver)角色
什么时候会产生outofmeoryErrorException,并说明产生原因。
原因:
常见的有以下几种:
1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.使用的第三方软件中的BUG;
5.启动参数内存值设定的过小;
把启动参数内存值设置足够大。
2.Java代码导致错误的解决:
重点排查以下几点:
1)检查代码中是否有死循环或递归调用。
2)检查是否有大循环重复产生新对象实体。
3)检 查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数 据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
4 )检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
案例:
1.hibernate查询数据时,一次查询过多的数据,后来调整了该部分的代码,每次只取出指定量的数据,成功的解决该问题。
2.在做压力测试时,出现OutOfMemoryError,发现session的资源一直没有被释放产生的,最好通过session的invalidate()方法将session的资源释放。
3.程序中出现死循环。
4.tomcat部署、运行出现OutOfMemoryError,加大内存参数值,解决此问题。
如果让你设计一个高并发、高可用、实时性很高的网站,谈谈你对架构的设计。
第一 负载均衡系统
软件负载均衡系统价格较低或者免费,效率较硬件负载均衡系统低,不过对于流量一般或稍大些网站来讲也足够使用,比如lvs。
目前普遍使用Squid或者nginx,或者Lighttpd,Varish。
Lighttpd:反向代理能力一般,处理FastCGI比较好,消耗内存很小
由Apache负责解析PHP内容,也可以用Nginx,或者Lighttpd,相对来说Apache比较稳定。
存储量很大,经常会达到单台服务器无法提供的规模,比如相册、视频等应用。因此需要专业的大规模存储系统。
根据各自需要由C/C++开发设计供上层CGI调用。
1)使用MySQL数据库,考虑到Web应用的数据库读多写少的特点,我们主要对读数据库做了优化,提供专用的读数据库和写数据库,在应用程序中实现读操作和写操作分别访问不同的数据库。
2)使用同步机制实现快速将主库(写库)的数据库复制到从库(读库)。一个主库对应多个从库,主库数据实时同步到从库。
3)写数据库有多台,每台都可以提供多个应用共同使用,这样可以解决写库的性能瓶颈问题和单点故障问题。
也可以从这些方面考虑(前端优化、运用缓存、代理层、数据库层、负载均衡层、业务层)
前端优化:
具体参考:yahoo前端优化34条规则,针对12306平台,个人建议在没有多运营商链路接入(如BGP)的情况下继续使用CDN进行加速。动、静态应用分离,静态业务使用非12306.cn域名可以减少无用cookie带来的流量。任何一个小细节在高并发下都会被无限放大(截止目前发现平台还是以dynamic.12306.cn域名做静态引用)。查询页面的结果是通过Ajax异步返回填充iframe框架来实现,这对动态CDN加速是一个挑战,因为CDN节点并没有真正缓存页面中主要加速的内容。另外提高验证码的复杂度及多样性,可以缓解刷票机给平台带来的压力。
运用缓存:
缓存最大的好处是减少后端数据存储的I/O压力,从一个普通用户订票轨迹来看,查询读往往是入库写的好几倍,如何减少数据库的读I/O对提高平台的整体性能至关重要,比较流行的缓存技术有针对页面及数据级,页面级缓存有varnish、squid等,如使用CDN,页面级的缓存可以不用考虑,重点将精力放在数据级的缓存规划上,技术方面可以用Nosql来实现,比较成熟的Nosql有memcached、redis、mongodb等。可以根据班次、出发与目的地ID组合或出发日期进行hash分区,这样可以很好地提高缓存命中率,减少后端数据库的压力。
代理层:
引入代理层的目的是拆分业务,目前平台绝大部分功能都共用一组WEB服务器(从域名及URI结构猜测,不一定准确)来对外提供服务,比如登录、注册、车票查询、余票查询、列车时刻表查询、正晚点查询、订单管理等等,只要其中一个功能模块出现堵塞,影响必定是全局性的。一个好的方法是优化、规范各业务URI,在代理层实现业务的划分,可用的技术有Haproxy、Nginx等,如将/otsweb/regitNote/映射到注册组WEB服务器,/otsweb/AppQuery/映射到查询组WEB服务器,/otsweb/Pay/映射到支付组WEB服务器等等,如此一来,当查询业务出现延时堵塞时不会影响到用户支付。
数据库层:
之前接触过一些政府行业的业务,数据库服务器往往都使用一台高端的硬件,比如小型机,在互联网行业,尤其是类似于12306订票系统,这往往是最致命的,理由很简单,在大流量并发下处理能力再强的服务器也吐不出数据,因为受网络I/O、磁盘I/O、业务逻辑等方面的限制,所以必须将数据打散,方案有进行读写分离、分区、分片。主从模式可以很好实现读写分离,大部分数据库都支持这点,除此之外还建议使用分区模式,分区策略可以根据业务特点进行,按地域进行分区是一个好主意,因为每个区域都是一个大分区,还可以从业务层面对它做二级甚至三级的"扩展分区"。需要在细化拆分与运营成本上做好平衡。另外I/O密集的点尽量使用SSD代替。
负载均衡层:
保障一个业务平台的高可用性,采用负载均衡策略必不可少,即使是提供给CDN的源服务器。目前有商用的F5、NetScaler、Radware等,也有开源的LVS,看成本的投入来选择,此处不详细展开讨论。
业务层:
此次12306网站瘫痪事件,业务层面有无优化的空间?12306网站平台是铁道集团在互联网上对外服务的窗口,与电话订票、代售点都是平级的,后端肯定还关联着很多复杂的业务系统,在没有对整个集团业务系统做扩容的前提下(短期内估计不能实现),可以将网站业务平台剥离出来,当然,完全剥离也不太实际,至少可以延长同步、一致性校验的时间。时间的长短随班次的发车时间成正比,因为大部分的用户都是提前一周以上就着手预定车票。
列举常用的开源框架和开源类库,越多越好
spingmvc struts2 spring mybaits hibernate axis2 mina poi opencsv webwork jquery itext junit Commons-IO
Commons-Email log4j dom4j ant mail
说明内连接、左连接、右连接、全连接的含义和区别
内连接:把两个表中数据对应的数据查询出来。
外连接:以某个表为基础把对应数据查询出来(全连接是以多个表为基础),其中又包括左连接和右连接两种
内连接:
把两个表中数据对应的数据查询出来。在两个表中查询满足条件的对应数据
SELECT * FROM student INNER JOIN grade ON student.no=grade.no
左连接:
包含左表中所有数据,右表中满足条件的对应数据
SELECT * FROM student LEFT JOIN grade ON student.no=grade.no
右连接:包含右表中所有数据,左表中满足条件的对应数据。
SELECT * FROM student RIGHT JOIN grade ON student.no=grade.no
全连接:
左右表中所有数据全部查询出来。
SELECT * FROM student FULL JOIN grade ON student.no=grade.no
列举你用过的MQ;
LinkedList LinkedBlockingQueue SynchronizedQuery 框架(active MQ)
系统之间有哪些数据交互方式;
socket方式( Socket方式是最简单的交互方式。是典型才c/s 交互模式。一台客户机,一台服务器。服务器提供服务,通过ip地址和端口进行服务访问。而客户机通过连接服务器指定的端口进行消息交互。其中传输协议可以是tcp/UDP 协议。而服务器和约定了请求报文格式和响应报文格式 ,传输协议可以是tcp/UDP)
ftp/文件共享服务器方式(系统A和系统B通过连接同一个数据库服务器的同一张表进行数据交换。当系统A请求系统B处理数据的时候,系统A Insert一条数据,系统B select 系统A插入的数据进行处理。
)
webService方式(soap协议)
数据库共享数据方式(系统A和系统B通过连接同一个数据库服务器的同一张表进行数据交换。当系统A请求系统B处理数据的时候,系统A Insert一条数据,系统B select 系统A插入的数据进行处理。
)
message方式(Java消息服务(Java Message Service)是message数据传输的典型的实现方式。系统A和系统B通过一个消息服务器进行数据交换。系统A发送消息到消息服务器,如果系统B订阅系统A发送过来的消息,消息服务器会消息推送给B。双方约定消息格式即可。目前市场上有很多开源的jms消息中间件,比如 ActiveMQ, OpenJMS 。)
springMVC方面:
Struts2也是非常优秀的MVC构架,优点非常多比如良好的结构,拦截器的思想,丰富的功能。但这里想说的是缺点,Struts2由于采用了值栈、OGNL表达式、struts2标签库等,会导致应用的性能下降,应避免使用这些功能。
1. 机制:spring mvc的入口是servlet,而struts2是filter(这里要指出,filter和servlet是不同的。以前认为filter是servlet的一种特殊),这样就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。
2. 性能:spring会稍微比struts快。spring mvc是基于方法的设计,而sturts是基于类,每次发一次请求都会实例一个action,每个action都会被注入属性,而spring基于方法,粒度更细,但要小心把握像在servlet控制数据一样。spring3 mvc是方法级别的拦截,拦截到方法后根据参数上的注解,把request数据注入进去,在spring3 mvc中,一个方法对应一个request上下文。而 struts2框架是类级别的拦截,每次来了请求就创建一个Action,然后调用setter getter方法把request中的数据注入;struts2实际上是通过setter getter方法与request打交道的;struts2中,一个Action对象对应一个request上下文。
3. 参数传递:struts是在接受参数的时候,可以用属性来接受参数,这就说明参数是让多个方法共享的。
4. 设计思想上:struts更加符合oop的编程思想, spring就比较谨慎,在servlet上扩展。
5. intercepter的实现机制:struts有以自己的interceptor机制,spring mvc用的是独立的AOP方式。这样导致struts的配置文件量还是比spring mvc大,虽然struts的配置能继承,所以我觉得论使用上来讲,spring mvc使用更加简洁,开发效率Spring MVC确实比struts2高。spring mvc是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上spring3 mvc就容易实现restful url。struts2是类级别的拦截,一个类对应一个request上下文;实现restful url要费劲,因为struts2 action的一个方法可以对应一个url;而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。spring3 mvc的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架方法之间不共享变量,而struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码,读程序时带来麻烦。
6. 另外,spring3 mvc的验证也是一个亮点,支持JSR303,处理ajax的请求更是方便,只需一个注解@ResponseBody ,然后直接返回响应文本即可。送上一段代码:
@RequestMapping(value="/whitelists")
public String index(ModelMap map) {
Account account = accountManager.getByDigitId(SecurityContextHolder.get().getDigitId());
List groupList = groupManager.findAllGroup(account.getId());
map.put("account", account);
map.put("groupList", groupList);
return "/group/group-index";
}
// @ResponseBody ajax响应,处理Ajax请求也很方便
@RequestMapping(value="/whitelist/{whiteListId}/del")
@ResponseBody
public String delete(@PathVariable Integer whiteListId) {
whiteListManager.deleteWhiteList(whiteListId);
return "success";
}
servlet和filter的区别 :
Filter可认为是Servlet的一种“变种”,它主要用于对用户请求进行预处理,也可以对 HttpServletResponse进行后处理,是个典型的处理链。它与Servlet的区别在于:它不能直接向用户生成响应。完整的流程 是:Filter对用户请求进行预处理,接着将请求交给 Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
servlet 生命周期:
(1)容器加载servlet文件 (2)创建servlet实例 (3)调用servlet中的init()方法进行初始化资源 (4)调用servlet中的service()方法进行线程并发处理 (5)调用servlet 的destroy方法进行销毁操作
get与post:
1. get从服务器上获取数据post向服务器传送数据
2. get把参数数据队列加提交表单ACTION属性所指URL值和表单内各字段对应URL看post通过HTTP post机制表单内各字段与其内容放置HTML HEADER内起传送ACTION属性所指URL地址用户看过程
3. 对于get方式服务器端用Request.QueryString获取变量值对于post方式服务器端用Request.Form获取提交数据
4. get传送数据量较小能大于2KBpost传送数据量较大般被默认受限制理论上IIS4大量80KBIIS5100KB
5. get安全性非常低post安全性较高执行效率却比Post方法好
建议:
1、get方式安全性较Post方式要差些包含机密信息建议用Post数据提交方式;
2、做数据查询时建议用Get方式;而做数据添加、修改或删除时建议用Post方式;
悲观锁与乐观锁的引入:
在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题。
典型的冲突有:
l 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。
l 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6。
为了解决这些并发带来的问题。 我们需要引入并发控制机制。
并发控制机制
最常用的处理多用户并发访问的方法是加锁。当一个用户锁住数据库中的某个对象时,其他用户就不能再访问该对象。加锁对并发访问的影响体现在锁的粒度上。比 如,放在一个表上的锁限制对整个表的并发访问;放在数据页上的锁限制了对整个数据页的访问;放在行上的锁只限制对该行的并发访问。可见行锁粒度最小,并发 访问最好,页锁粒度最大,表锁介于2者之间。
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。 [1] 悲观锁假定其他用户企图访问或者改变你正在访问、更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直 到你提交了所作的更改之后才释放锁。悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并 发访问性不好。
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整 性。[1] 乐观锁不能解决脏读的问题。 乐观锁则认为其他用户企图改变你正在更改的对象的概率是很小的,因此乐观锁直到你准备提交所作的更改时才将对象锁住,当你读取以及改变该对象时并不加 锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能。但是如果第二个用户恰好在第一个用户提交更改之前读取了该对 象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。这说明在乐观锁环境中,会 增加并发用户读取对象的次数。
从数据库厂商的角度看,使用乐观的页锁是比较好的,尤其在影响很多行的批量操作中可以放比较少的锁,从而降低对资源的需求提高数据库的性能。再考虑聚集 索引。在数据库中记录是按照聚集索引的物理顺序存放的。如果使用页锁,当两个用户同时访问更改位于同一数据页上的相邻两行时,其中一个用户必须等待另一个 用户释放锁,这会明显地降低系统的性能。interbase和大多数关系数据库一样,采用的是乐观锁,而且读锁是共享的,写锁是排他的。可以在一个读锁上 再放置读锁,但不能再放置写锁;你不能在写锁上再放置任何锁。锁是目前解决多用户并发访问的有效手段。
乐观锁应用
1. 使用自增长的整数表示数据版本号。更新时检查版本号是否一致, 比如数据库中数据版本为6,更新提交时version=6+1,使用该version值(=7)与数据库version+1(=7)作比较,如果相等,则 可以更新,如果不等则有可能其他程序已更新该记录,所以返回错误。
2. 使用时间戳来实现.
注:对于以上两种方式,Hibernate自带实现方式:在使用乐观锁的字段前加annotation: @Version, Hibernate在更新时自动校验该字段。
悲观锁应用
需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。
SqlServer中使用
Begin Tran
select top 1 @TrainNo=T_NO
from Train_ticket with (UPDLOCK) where S_Flag=0
update Train_ticket
set T_Name=user,
T_Time=getdate(),
S_Flag=1
where T_NO=@TrainNo
commit
我们在查询的时候使用了with (UPDLOCK)选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对此记录进行更新. 注意更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.
结论
在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.
数据库的优化方案:
数据库性能最关键的因素在于IO,因为操作内存是快速的,但是读写磁盘的速度是很慢的,优化数据库最关键的问题在于减少磁盘的IO操作,个人理解应分为物理和逻辑的优化,物理是指oracle产品本身的一些优化,逻辑优化是指应用程序级别的优化
物理优化的一些原则:
1)Oracle的运行环境(网络,硬件等)
2)使用合适的优化器
3)合理配置oracle实例参数
4)建立合适的索引(减少IO)
5)将索引数据和表数据分开在不同的表空间上(降低IO冲突)
6)建立表分区,将数据分别存储在不同的分区上(以空间换取时间,减少IO)
逻辑上的一些优化
1)Sql语句使用占位符语句,并且开发时候必须按照规定编写sql语句(如全部大写,全部小写等),oracle解析语句后会放置到共享池中, 如:
select * from Emp where name=?这个语句只会在共享池中有一条,而如果是字符串的话,那就根据不同名字存在不同的语句,所以占位符效率较好
2) 数据库不仅仅是一个存储数据的地方,同样是一个编程的地方,一些耗时的操作,可以通过存储过程等在用户较少的情况下执行,从而错开系统使用的高峰时间,提高数据库性能
3) 尽量不使用*号,如select * from Emp,因为要转化为具体的列名是要查数据字典, 比较耗时
4) 选择有效的表名
对于多表连接查询,可能oracle的优化器并不会优化到这个程度, oracle 中多表查询是根据FROM字句从右到左的数据进行的,那么最好右边的表(也就是基础表)选 择数据较少的表,这样排序更快速,如果有link表(多对多中间表),那么将link表放最右边作为基础表,在默认情况下oracle会自动优化,但是如 果配置了优化器的情况下,可能不会自动优化,所以平时最好能按照这个方式编写sql
5 )Where字句规则:
Oracle 中Where字句时从右往左处理的,表之间的连接写在其他条件之前,能过滤掉非常多的数据的条件,放在where的末尾, 另外!=符号比较的列将不使用索引,列经过了计算(如变大写等)不会使用索引(需要建立起函数), is null、is not null等优化器不会使用索引
6)使用Exits Not Exits 替代 In Not in
7)合理使用事务,合理设置事务隔离性,数据库的数据操作比较消耗数据库资源的,尽量使用批量处理,以降低事务操作次数
也可以参考我们dba给的建议:
@. SQL语句越简单越好;
@. 要保持事务(和连接)短小;
@. 不使用trigger、存储过程、自定义 函数;
@. 不使用select * ;
@. 避免使用子查询;
@. Update的where 语句要使用索引, 且粒度要尽可能的小(估算);
@. 改写OR为 IN ;
@. 改写OR为 Union;
@. 避免%前缀模糊查询;
@. 避免count(*)操作;
@. 使用union all,避免使用union;
@. group by默认是进行排序的,如果结果无需排序,可最后加order by null;
@. 列的数据类型必须相同,再进行比较;
@. 避免大批量数据更新;
@. 分页写法:关于mysql的分页优化 写法: (先根据过滤条件取出主键id进行排序,再进行join操作取出其他相关字段)