java如何解决⽀付⾼并发_Java⾼并发,如何解决,什么⽅式
解决
java⾼并发:
并发:当有多个线程在操作时,如果系统只有⼀个CPU,则它根本不可能真正同时进⾏⼀个以上的线程,它只能把CPU运⾏时间划分成若⼲个时间段,再将时间 段分配给各个线程执⾏,在⼀个时间段的线程代码运⾏时,其它线程处于挂起状。.这种⽅式我们称之为并发(Concurrent)。
对于我们开发的⽹站,如果⽹站的访问量⾮常⼤的话,那么我们就需要考虑相关的并发访问问题了。⽽并发问题是绝⼤部分的程序员头疼的问题,
、同步和异步的区别和联系
所谓同步,可以理解为在执⾏完⼀个函数或⽅法之后,⼀直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到
返回的值或消息后才往下执⾏其它的命令。
异步,执⾏完函数或⽅法后,不必阻塞性地等待返回值或消息,只需要向系统委托⼀个异步过程,那么当系统接收到返回
值或消息时,系统会⾃动触发委托的异步过程,从⽽完成⼀个完整的流程。
同步在⼀定程度上可以看做是单线程,这个线程请求⼀个⽅法后就待这个⽅法给他回复,否则他不往下执⾏(死⼼眼)。
同步就是⼀件事,⼀件事情⼀件事的做。
异步就是,做⼀件事情,不引响做其他事情。异步在⼀定程度上可以看做是多线程的(废话,⼀个线程怎么叫异步),请求⼀个⽅法后,就不管了,继续执⾏其他的⽅法。
脏数据
脏读就是指当⼀个事务正在访问数据,并且对数据进⾏了修改,⽽这种修改还没有提交到数据库中,这时,另外⼀个事务也访问这个数据,然后使⽤了这
个数据。因为这个数据是还没有提交的数据,那么另外⼀个事务读到的这个数据是脏数据(Dirty Data),依据脏数据所做的操作可能是不正确的。
不可重复读
不可重复读是指在⼀个事务内,多次读同⼀数据。在这个事务还没有结束时,另外⼀个事务也访问该同⼀数据。那么,在第⼀个事务中的两次读数据之间,由于第⼆个事务的修改,那么第⼀个事务两次读到的数据可能是不⼀样的。这样就发⽣了在⼀个事务内两次读到的数据是不⼀样的,因此称为是不可重复读
2、如何处理并发和同步
今天讲的如何处理并发和同同步问题主要是通过锁机制。
我们需要明⽩,锁机制有两个层⾯。
⼀种是代码层次上的,如java中的同步锁,典型的就是同步关键字synchronized,这⾥我不在做过多的讲解,
另外⼀种是数据库层次上的,⽐较典型的就是悲观锁和乐观锁。这⾥我们重点讲解的就是悲观锁(传统的物理锁)和乐观锁。
悲观锁(Pessimistic Locking):
悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来⾃ 外部系统的事务处理)修改持保守态度,因此,
在整个数据处理过程中,将数据处于锁定状态。
悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能 真正保证数据访问的排他性,否则,即使在本系统
中实现了加锁机制,也⽆法保证外部系 统不会修改数据)。
⼀个典型的倚赖数据库的悲观锁调⽤:
select * from account where name=”Erica” for update
这条 sql 语句锁定了 account 表中所有符合检索条件( name=”Erica” )的记录。
本次事务提交之前(事务提交时会释放事务过程中的锁),外界⽆法修改这些记录。
Hibernate 的悲观锁,也是基于数据库的锁机制实现。
下⾯的代码实现了对查询记录的加锁:
String hqlStr ="from TUser as user where user.name='Erica'";
Query query = ateQuery(hqlStr);
query.setLockMode("user",LockMode.UPGRADE); // 加锁
List userList = query.list();// 执⾏查询,获取数据
query.setLockMode 对查询语句中,特定别名所对应的记录进⾏加锁(我们为 TUser 类指定了⼀个别名 “user” ),这⾥也就是对返回的所有 user 记录进⾏加锁。
观察运⾏期 Hibernate ⽣成的 SQL 语句:
select tuser0_.id as id, tuser0_.name as name, tuser0_.group_id
as group_id, tuser0_.user_type as user_type, tuser0_.sex as sex
from t_user tuser0_ where (tuser0_.name='Erica' ) for update
这⾥ Hibernate 通过使⽤数据库的 for update ⼦句实现了悲观锁机制。
Hibernate 的加锁模式有:
Ø LockMode.NONE : ⽆锁机制。
Ø LockMode.WRITE : Hibernate 在 Insert 和 Update 记录的时候会⾃动获取
Ø LockMode.READ : Hibernate 在读取记录的时候会⾃动获取。
以上这三种锁机制⼀般由 Hibernate 内部使⽤,如 Hibernate 为了保证 Update
过程中对象不会被外界修改,会在 save ⽅法实现中⾃动为⽬标对象加上 WRITE 锁。
Ø LockMode.UPGRADE :利⽤数据库的 for update ⼦句加锁。
Ø LockMode. UPGRADE_NOWAIT : Oracle 的特定实现,利⽤ Oracle 的 for
update nowait ⼦句实现加锁。
上⾯这两种锁机制是我们在应⽤层较为常⽤的,加锁⼀般通过以下⽅法实现:
Criteria.setLockMode
Query.setLockMode
Session.lock
注意,只有在查询开始之前(也就是 Hiberate ⽣成 SQL 之前)设定加锁,才会
真正通过数据库的锁机制进⾏加锁处理,否则,数据已经通过不包含 for update
⼦句的 Select SQL 加载进来,所谓数据库加锁也就⽆从谈起。
为了更好的理解 for update的锁表的过程,本⼈将要以mysql为例,进⾏相应的讲解
1、要测试锁定的状况,可以利⽤MySQL的Command Mode ,开⼆个视窗来做测试。
需要注意的是for update要放到mysql的事务中,即begin和commit中,否者不起作⽤。
乐观锁(Optimistic Locking):
相对悲观锁⽽⾔,乐观锁机制采取了更加宽松的加锁机制。悲观锁⼤多数情况下依 靠数据库的锁机制实现,以保证操作最⼤程度的独占性。但随之
⽽来的就是数据库 性能的⼤量开销,特别是对长事务⽽⾔,这样的开销往往⽆法承受。 如⼀个⾦融系统,当某个操作员读取⽤户的数据,并在读出的⽤户数
据的基础上进 ⾏修改时(如更改⽤户帐户余额),如果采⽤悲观锁机制,也就意味着整个操作过 程中(从操作员读出数据、开始修改直⾄提交修改结果的全
过程,甚⾄还包括操作 员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果⾯对⼏ 百上千个并发,这样的情况将导致怎样的后果。 乐
观锁机制在⼀定程度上解决了这个问题。
乐观锁,⼤多是基于数据版本  Version )记录机制实现。何谓数据版本?即为数据增加⼀个版本标识,在基于数据库表的版本解决⽅案中,⼀般是通
过为数据库表增加⼀个 “version” 字段来 实现。 读取出数据时,将此版本号⼀同读出,之后更新时,对此版本号加⼀。此时,将提 交数据的版本数据与数据
库表对应记录的当前版本信息进⾏⽐对,如果提交的数据 版本号⼤于数据库表当前版本号,则予以更新,否则认为是过期数据。对于上⾯修改⽤户帐户信息
的例⼦⽽⾔,假设数据库中帐户信息表中有⼀个 version 字段,当前值为 1 ;⽽当前帐户余额字段( balance )为 $100 。操作员 A 此时将其读出
( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。 2 在操作员 A 操作的过程中,操作员 B 也读⼊此⽤户信息( version=1 ),并 从其帐
户余额中扣除 $20 ( $100-$20 )。 3 操作员 A 完成了修改⼯作,将数据版本号加⼀( version=2 ),连同帐户扣 除后余额(
balance=$50 ),提交
⾄数据库更新,此时由于提交数据版本⼤ 于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。 4 操作员 B 完成了操作,也将版本号加⼀
( version=2 )试图向数据库提交数 据( balance=$80 ),但此时⽐对数据库记录版本时发现,操作员 B 提交的 数据版本号为 2 ,数据库记录当前版
本也为 2 ,不满⾜ “ 提交版本必须⼤于记 录当前版本才能执⾏更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。 这样,就避免了操作员 B ⽤基于
version=1 的旧数据修改的结果覆盖操作 员 A 的操作结果的可能。 从上⾯的例⼦可以看出,乐观锁机制避免了长事务中的数据库加锁开销(操作员 A
和操作员 B 操作过程中,都没有对数据库数据加锁),⼤⼤提升了⼤并发量下的系 统整体性能表现。 需要注意的是,乐观锁机制往往基于系统中的数据存储
逻辑,因此也具备⼀定的局 限性,如在上例中,由于乐观锁机制是在我们的系统中实现,来⾃外部系统的⽤户 余额更新操作不受我们系统的控制,因此可能
update是什么会造成脏数据被更新到数据库中。在 系统设计阶段,我们应该充分考虑到这些情况出现的可能性,并进⾏相应调整(如 将乐观锁策略在数据库存储过程中实
现,对外只开放基于此存储过程的数据更新途 径,⽽不是将数据库表直接对外公开)。 Hibernate 在其数据访问引擎中内置了乐观锁实现。如果不⽤考虑外
部系统对数 据库的更新操作,利⽤ Hibernate 提供的透明化乐观锁实现,将⼤⼤提升我们的 ⽣产⼒。
⼀:⾼并发⾼负载类⽹站关注点之数据库
没错,⾸先是数据库,这是⼤多数应⽤所⾯临的⾸个SPOF。尤其是Web2.0的应⽤,数据库的响应是⾸先要解决的。
⼀般来说MySQL是最常⽤的,可能最初是⼀个mysql主机,当数据增加到100万以上,那么,MySQL
的效能急剧下降。常⽤的优化措施是M-S(主-从)⽅式进⾏同步复制,将查询和操作和分别在不同的服务器上进⾏操作。我推荐的是M-M-Slaves⽅式,2个主Mysql,多个Slaves,需要注意的是,虽然有2个Master,但是同时只有1个是Active,我们可以在⼀定时候切换。之所以⽤2个M,是保证M不会⼜成为系统的SPOF。
Slaves可以进⼀步负载均衡,可以结合LVS,从⽽将select操作适当的平衡到不同的slaves上。
以上架构可以抗衡到⼀定量的负载,但是随着⽤户进⼀步增加,你的⽤户表数据超过1千万,这时那个M变成了SPOF。你不能任意扩充Slaves,否则复制同步的开销将直线上升,怎么办?我的⽅法是表分区,从业务层⾯上进⾏分区。最简单的,以⽤户数据为例。根据⼀定的切分⽅式,⽐如id,切分到不同的数据库集去。
全局数据库⽤于meta数据的查询。缺点是每次查询,会增加⼀次,⽐如你要查⼀个⽤户nightsailer,你⾸先要到全局数据库到nightsailer对应的cluster id,然后再到指定的cluster到nightsailer的实际数据。
每个cluster可以⽤m-m⽅式,或者m-m-slaves⽅式。这是⼀个可以扩展的结构,随着负载的增加,你可以简单的增加新的mysql cluster 进去。
⼆:⾼并发⾼负载⽹站的系统架构之HTML静态化
三:⾼并发⾼负载类⽹站关注点之缓存、负载均衡、存储
缓存是另⼀个⼤问题,我⼀般⽤memcached来做缓存集,⼀般来说部署10台左右就差不多(10g内存池)。需要注意⼀点,千万不能⽤使⽤
swap,最好关闭linux的swap。
负载均衡/加速
可能上⾯说缓存的时候,有⼈第⼀想的是页⾯静态化,所谓的静态html,我认为这是常识,不属于要点了。页⾯的静态化随之带来的是静态服务的
负载均衡和加速。我认为Lighttped+Squid是最好的⽅式了。
LVS lighttped====>squid(s) ====lighttpd
上⾯是我经常⽤的。注意,我没有⽤apache,除⾮特定的需求,否则我不部署apache,因为我⼀般⽤php-fastcgi配合lighttpd,
性能⽐apache+mod_php要强很多。
squid的使⽤可以解决⽂件的同步等等问题,但是需要注意,你要很好的监控缓存的命中率,尽可能的提⾼的90%以上。
存储
存储也是⼀个⼤问题,⼀种是⼩⽂件的存储,⽐如图⽚这类。另⼀种是⼤⽂件的存储,⽐如搜索引擎的索引,⼀般单⽂件都超过2g以上。
⼩⽂件的存储最简单的⽅法是结合lighttpd来进⾏分布。或者⼲脆使⽤Redhat的GFS,优点是应⽤透明,缺点是费⽤较⾼。我是指
你购买盘阵的问题。我的项⽬中,存储量是2-10Tb,我采⽤了分布式存储。这⾥要解决⽂件的复制和冗余。
这样每个⽂件有不同的冗余,这⽅⾯可以参考google的gfs的论⽂。
⼤⽂件的存储,可以参考nutch的⽅案,现在已经独⽴为hadoop⼦项⽬。(你可以google it)
四:⾼并发⾼负载⽹站的系统架构之图⽚服务器分离
五:⾼并发⾼负载⽹站的系统架构之数据库集和库表散列
在数据库集⽅⾯,很多数据库都有⾃⼰的解决⽅案,Oracle、Sybase等都有很好的⽅案,常⽤的MySQL提供的Master/Slave也是类似的⽅案,您使⽤了什么样的DB,就参考相应的解决⽅案来实施即可。
上⾯提到的数据库集由于在架构、成本、扩张性⽅⾯都会受到所采⽤DB类型的限制,于是我们需要从应⽤程序的⾓度来考虑改善系统架构,库表散列是常⽤并 且最有效的解决⽅案。我们在应⽤程序中安装业务和应⽤或者功能模块将数据库进⾏分离,不同的模块对应不同的数据库或者表,再按照⼀定的策略对某个页⾯或者 功能进⾏更⼩的数据库散列,⽐如⽤户表,按照⽤户ID进⾏表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采⽤了这样的 架构,将论坛的⽤户、设置、帖⼦等信息进⾏数据库分离,然后对帖⼦、⽤户按照板块和ID进⾏散列数据库和表,最终可以在配置⽂件中进⾏简单的配置便能让系 统随时增加⼀台低成本的数据库进来补充系统性能。
集软件的分类:
⼀般来讲,集软件根据侧重的⽅向和试图解决的问题,分为三⼤类:⾼性能集(High performance cluster,HPC)、负载均衡集(Load balance cluster, LBC),⾼可⽤性集(High availability cluster,HAC)。
⾼性能集(High performance cluster,HPC),它是利⽤⼀个集中的多台机器共同完成同⼀件任务,
使得完成任务的速度和可靠性都远远⾼于单机运⾏的效果。弥补了单机性能上的不⾜。该集在天⽓预报、环境监控等数据量⼤,计算复杂的环境中应⽤⽐较多;
负载均衡集(Load balance cluster, LBC),它是利⽤⼀个集中的多台单机,完成许多并⾏的⼩的⼯作。⼀般情况下,如果⼀个应⽤使⽤的⼈多了,那么⽤户请求的响应时间就会增⼤,机器的性能也会受到影响,如果使⽤负载均衡集,那么集中任意⼀台机器都能响应⽤户的请求,这样集就会在⽤户发出服务请求之后,选择当时负载最⼩,能够提供最好的服务的这台机器来接受请求并相应,这样就可⽤⽤集来增加系统的可⽤性和稳定性。这类集在⽹站中使⽤较多;
⾼可⽤性集(High availability cluster,HAC),它是利⽤集中系统 的冗余,当系统中某台机器发⽣损坏的时候,其他后备的机器可以迅速的接替它来启动服务,等待故障机的维修和返回。最⼤限度的保证集中服务的可⽤性。这类系统⼀般在银⾏,电信服务这类对系统可靠性有⾼的要求的领域有着⼴泛的应⽤。
六:⾼并发⾼负载⽹站的系统架构之缓存
最基本的两种缓存。⾼级和分布式的缓存在后⾯讲述。
架构⽅⾯的缓存,对Apache⽐较熟悉的⼈都能知道Apache提供了⾃⼰的缓存模块,也可以使⽤外加的Squid模块进⾏缓存,这两种⽅式均可以有效的提⾼Apache的访问响应能⼒。
⽹站程序开发⽅⾯的缓存,Linux上提供的Memory Cache是常⽤的缓存接⼝,可以在web开发中使⽤,⽐如⽤Java开发的时候就可以调⽤MemoryCache对⼀些数据进⾏缓存和通讯共享,⼀些⼤ 型社区使⽤了这样的架构。另外,在使⽤web语⾔开发的时候,各种语⾔基本都有⾃⼰的缓存模块和⽅法,PHP有Pear的Cache模块,Java就更多 了,不是很熟悉,相信也肯定有。
Java开源缓存框架
JBossCache/TreeCache JBossCache是⼀个复制的事务处理缓存,它允许你缓存企业级应⽤数据来更好的改善性能。缓存数据被⾃动复制,让你轻松进⾏Jboss服务器之间的集⼯作。JBossCache能够通过Jboss应⽤服务或其他J2EE容器来运⾏⼀个Mbean服务,当然,它也能独⽴运⾏。 JBossCache包括两个模块:TreeCache和TreeCacheAOP。 TreeCache --是⼀个树形结构复制的事务处理缓存。TreeCacheAOP --是⼀个“⾯向对象”缓存,它使⽤AOP来动态管理POJO
OSCache OSCache标记库由OpenSymphony设计,它是⼀种开创性的JSP定制标记应⽤,提供了在现有JSP页⾯之内实现快速内存缓冲的功能。OSCache是个⼀个⼴泛采⽤的⾼性能的J2EE缓存框架,OSCache能⽤于任何Java应⽤程序的普通的缓存解决⽅案。OSCache 有以下特点:缓存任何对象,你可以不受限制的缓存部分jsp页⾯或HTTP请求,任何java对象都可以缓存。 拥有全⾯的API--OSCache API给你全⾯的程序来控制所有的OSCache特性。 永久缓存--缓存能随意的写⼊硬盘,因此
允许昂贵的创建(expensive-to-create)数据来保持缓存,甚⾄能让应⽤重启。 ⽀持集--集缓存数据能被单个的进⾏参数配置,不需要修改代码。 缓存记录的过期--你可以有最⼤限度的控制缓存对象的过期,包括可插⼊式的刷新策略(如果默认性能不需要时)。
JCACHE JCACHE是⼀种即将公布的标准规范(JSR 107),说明了⼀种对Java对象临时在内存中进⾏缓存的⽅法,包括对象的创建、共享访问、假脱机(spooling)、失效、各JVM的⼀致性等。它可被⽤于缓存JSP内最经常读取的数据,如产品⽬录和价格列表。利⽤JCACHE,多数查询的反应时间会因为有缓存的数据⽽加快(内部测试表明反应时间⼤约快15倍)。
Ehcache Ehcache出⾃Hibernate,在Hibernate中使⽤它作为数据缓存的解决⽅案。
Java Caching System JCS是Jakarta的项⽬Turbine的⼦项⽬。它是⼀个复合式的缓冲⼯具。可以将对象缓冲到内存、硬盘。具有缓冲对象时间过期设定。还可以通过JCS构建具有缓冲的分布式构架,以实现⾼性能的应⽤。 对于⼀些需要频繁访问⽽每访问⼀次都⾮常消耗资源的对象,可以临时存放在缓冲区中,这样可以提⾼服务的性能。⽽JCS正是⼀个很好的缓冲⼯具。缓冲⼯具对于读操作远远多于写操作的应⽤性能提⾼⾮常显著。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。