2009-12-27

UI设计那些事

自从开始工作起,就不断听到身边的同事谈论UI设计的问题,林林总总,各种论调.

大家之所以热衷于谈论这个话题,根本原因在于UI设计本身是一件和用户交互的活动,从满意性质量角度考虑,它的设计就不存到唯一的答案,因此这里没有最好,只有更好.不同的用户,不同的行为方法,不同的思考方法都将决定不同的UI设计.

我一直没有过多思考什么是好的UI设计,毕竟做了这么长时间的数据库后台工作.长期的后台设计经历让我养成了寻求简单,直击根本的习惯,所以我认为的UI设计应当是简约,直接,能够实现展示价值.从我的第一个项目,我就这么认为,然而,这样的认识确实还经历了一个过程.

最初,为某电信国企做项目,web页面的设计只能用'粗糙'来形容,没有内容组织,没有风格要求,没有可用性可言,总之只要能够展示数据,一切都好谈.而在验收标准上,毅然用'用户web界面要友好'等字眼描述,这样的项目做到最后往往是扯皮.推动验收的往往是能侃的销售经理,常见他们很自信的告诉用户这就是'友好的web设计'.

随后几年的项目,web UI的设计趋于累积内容信息,当时的UI设计师只是在调整色彩,页面结构,并没有组织起内容,以至于一个web页面充满大量的文字,不相关的信息被罗列成章.这样的设计迎合了一大部分国企领导的好评,因为看起来专业,但是实际情况是,没有多少用户真的关注这些信息.
这些年的所见所闻,让我逐渐的又回到的最初的认识.正所谓的返璞归真.专业的UI设计更加注重的用户的体验,而非一时的效仿,简约并非意味着简单,专业UI设计的每一步骤都包含着众多的思考,UI设计并非是信息的堆积,而是正确的组织.从一个用户角度讲, 评价UI好坏的标准在于能够快速的定位到有价值的信息,信息量大了不好,小了也不好.这里至少要考虑几个方面的因素:
1.被关注的信息
2.信息的相关性
3.信息的组织
本着这样的原则去审视一些网站,都会找到不少让人不爽的设计.但同时,你也会发现,这些年不少网站也在不断优化UI,使之更贴近用户,比如淘宝,至少长期不用,你至于被UI给搞糊涂.

2009-5-11

business intelligence 2.0

 After long term of development, Business intelligence was moving into a new stage. that’s so-called BI2.0. The new term was introduced by BO corporation and trying to influence industry in an unnatural way.

After I studied the whole concept, I came up into these meaningful points, Clearly, I got rid of some tedious and unrealistic content pushed by vendors.

1.What’s business intelligence 2.0?

Business intelligence 2.0 is a term most likely named after web 2.0. Based on the BI of the first generation, BI2.0 provide more experience to meet user's requirements in a larger scope, like more easy-use UI, platformlise, .etc.

2. The new characters of BI 2.0

1. it enable user to query dynamically real time data

2. more web and browser-based approached to business data.

3.imply a trend towards moving away from the standard data warehouse that business intelligence tools has used,applying a new way to relate information quickly from many sources.

3. Why BI.20?

Will we continue to follow a new concept? Maybe. The new concept is the  marketing strategy commonly used by vendor. The key point for us is that we need to continue to involve client’s engagement and provide more business value for customers. I am still optimistic about BI field.

 1.the potential business opportunity is still huge.

The business heads always have a deep expectation for intelligence system. The desired system is help users to watch out their business in a real time, response to any change and do more further decision. However, in the traditional fashion, senior managers only depends on reports, that's why reporting functionality and querying tools characterized the previous business intelligence software.

As a industry expert, we should not ignore the real business value behind the technology. Generally, the first generation of BI software provide the wide use on data integration, the more valuable innovation is to help decision maker to do a right thing and give a rapid response to their business.

2. The essential technologies are getting mature.

SOA. XBRL , Semantic Web ontologies enable using data external to an organization, such as benchmarking type.

2009-5-2

通过sqlplus来查询系统cpu时间

oracle惯用的V$OSSTAT性能视图可以随时监控系统中的资源消耗,这其中包括系统cpu的使用率。

通常我们使用sar来完成这个工作,如果通过sqlplus来实现,可以编制一套package完成一体化监控分析目的。当然,对于监控还有很多中手段,区别在于复杂度和准确,即时性。每个人都可以选择不同方式实现,我个人认为使用自己最熟悉的方式最为妥当,这样可以避免无需的学习成本。

以下是监控脚本内容:
 
CREATE OR REPLACE TYPE osstat_record IS OBJECT (
  date_time TIMESTAMP,
  idle_time NUMBER,
  user_time NUMBER,
  sys_time NUMBER,
  iowait_time NUMBER,
  nice_time NUMBER
);
/

CREATE OR REPLACE TYPE osstat_table AS TABLE OF osstat_record;
/

CREATE OR REPLACE FUNCTION osstat(p_interval IN NUMBER, p_count IN NUMBER)
   RETURN osstat_table
   PIPELINED
IS
  l_t1 osstat_record;
  l_t2 osstat_record;
  l_out osstat_record;
  l_num_cpus NUMBER;
  l_total NUMBER;
BEGIN
  l_t1 := osstat_record(NULL, NULL, NULL, NULL, NULL, NULL);
  l_t2 := osstat_record(NULL, NULL, NULL, NULL, NULL, NULL);
  
  SELECT value 
  INTO l_num_cpus
  FROM v$osstat 
  WHERE stat_name = 'NUM_CPUS';
  
  FOR i IN 1..p_count
  LOOP
    SELECT sum(decode(stat_name,'IDLE_TIME', value, NULL)) as idle_time,
           sum(decode(stat_name,'USER_TIME', value, NULL)) as user_time,
           sum(decode(stat_name,'SYS_TIME', value, NULL)) as sys_time,
           sum(decode(stat_name,'IOWAIT_TIME', value, NULL)) as iowait_time,
           sum(decode(stat_name,'NICE_TIME', value, NULL)) as nice_time
    INTO l_t2.idle_time, l_t2.user_time, l_t2.sys_time, l_t2.iowait_time, l_t2.nice_time
    FROM v$osstat 
    WHERE stat_name in ('IDLE_TIME','USER_TIME','SYS_TIME','IOWAIT_TIME','NICE_TIME');

    l_out := osstat_record(systimestamp,
                           (l_t2.idle_time-l_t1.idle_time)/l_num_cpus/p_interval,
                           (l_t2.user_time-l_t1.user_time)/l_num_cpus/p_interval,
                           (l_t2.sys_time-l_t1.sys_time)/l_num_cpus/p_interval,
                           (l_t2.iowait_time-l_t1.iowait_time)/l_num_cpus/p_interval,
                           (l_t2.nice_time-l_t1.nice_time)/l_num_cpus/p_interval);
    l_total := l_out.idle_time+l_out.user_time+l_out.sys_time+l_out.iowait_time+l_out.nice_time;

    PIPE ROW(osstat_record(systimestamp,
                           l_out.idle_time/l_total*100,
                           l_out.user_time/l_total*100,
                           l_out.sys_time/l_total*100,
                           l_out.iowait_time/l_total*100,
                           l_out.nice_time/l_total*100));
    l_t1 := l_t2;
    
    dbms_lock.sleep(p_interval);

  END LOOP;
  RETURN;
END;
/

现在完成了创建,我们来验证脚本的效果

SQL> SET ARRAYSIZE 1

SQL> COLUMN user_time FORMAT 990.00
SQL> COLUMN nice_time FORMAT 990.00
SQL> COLUMN sys_time FORMAT 990.00
SQL> COLUMN iowait_time FORMAT 990.00
SQL> COLUMN idle_time FORMAT 990.00

SQL> SELECT to_char(date_time,'HH:MI:SS') as date_time, user_time, nice_time, sys_time, iowait_time, idle_time
  2  FROM table(osstat(5,100));

DATE_TIM USER_TIME NICE_TIME SYS_TIME IOWAIT_TIME IDLE_TIME
-------- --------- --------- -------- ----------- ---------
12:26:11
12:26:16      0.05      0.00     0.05        0.10     99.80
12:26:21      0.76      0.00     0.05        0.66     98.52
12:26:26      0.05      0.00     0.10        0.10     99.74
12:26:31      0.15      0.00     8.03        0.31     91.50
12:26:36      0.27      0.00    21.06       15.75     62.92
12:26:41      0.10      0.00     2.57        8.13     89.21
12:26:46      0.05      0.00     0.10        0.71     99.14
12:26:51      0.10      0.00     0.05        0.41     99.44
12:26:56     24.37      0.00     0.65        3.28     71.71
12:27:01     24.50      0.00     0.97        1.27     73.26
12:27:06     24.31      0.00     1.17        1.32     73.20
12:27:11     25.05      0.00     0.66        0.82     73.47
12:27:16     25.06      0.00     0.61        0.76     73.56
12:27:21     25.13      0.00     0.56        0.46     73.85
12:27:26     24.91      0.00     0.45        1.77     72.87
12:27:31     23.97      0.00     1.41        2.17     72.46
12:27:36     24.90      0.00     0.97        0.91     73.22
12:27:41     25.18      0.00     0.51        0.36     73.95
12:27:46     25.64      0.00     0.41        0.36     73.59
12:27:51     46.37      0.05     3.48        0.45     49.65
12:27:56     46.81      0.00     3.14        0.35     49.70
12:28:01     46.63      0.00     3.34        0.20     49.83
12:28:06     45.76      0.00     4.19        0.25     49.80
12:28:11     46.58      0.00     3.40        0.15     49.88
12:28:16     46.76      0.00     3.54        0.25     49.45
12:28:21     46.06      0.00     6.74        0.25     46.96
12:28:26     43.73      0.00     6.24        0.10     49.93
12:28:31     34.87      0.00     6.98        0.30     57.84
12:28:36     29.60      0.00     5.50        0.71     64.20
12:28:41     38.40      0.00     9.60        7.69     44.32
12:28:46     39.20      0.00     9.39        6.32     45.09
12:28:51     34.73      0.00     8.37       13.81     43.09

$sar 5 100
Linux 2.6.9-42.ELsmp (helicon.antognini.ch)     05/01/2009

12:26:11 AM       CPU     %user     %nice   %system   %iowait     %idle
12:26:16 AM       all      0.05      0.00      0.05      0.10     99.80
12:26:21 AM       all      0.77      0.00      0.10      0.67     98.46
12:26:26 AM       all      0.05      0.00      0.10      0.10     99.74
12:26:31 AM       all      0.15      0.00      8.31      0.31     91.23
12:26:36 AM       all      0.31      0.00     26.54     18.63     54.52
12:26:41 AM       all      0.10      0.00      3.74      8.41     87.75
12:26:46 AM       all      0.05      0.00      0.20      0.72     99.03
12:26:51 AM       all      0.26      0.00      0.05      0.41     99.28
12:26:56 AM       all     25.15      0.00      0.72      3.39     70.74
12:27:01 AM       all     24.87      0.00      0.98      1.29     72.86
12:27:06 AM       all     24.64      0.00      1.23      1.34     72.79
12:27:11 AM       all     25.23      0.00      0.67      0.82     73.27
12:27:16 AM       all     25.26      0.00      0.72      0.77     73.25
12:27:21 AM       all     25.19      0.00      0.62      0.46     73.73
12:27:26 AM       all     25.40      0.00      0.51      1.80     72.29
12:27:31 AM       all     24.46      0.00      1.44      2.21     71.88
12:27:36 AM       all     25.13      0.00      1.03      0.92     72.92
12:27:41 AM       all     25.26      0.00      0.56      0.36     73.82
12:27:46 AM       all     25.73      0.00      0.46      0.36     73.44
12:27:51 AM       all     46.58      0.05      3.50      0.45     49.43
12:27:56 AM       all     46.95      0.00      3.15      0.35     49.55
12:28:01 AM       all     46.70      0.00      3.45      0.20     49.65
12:28:06 AM       all     45.85      0.00      4.25      0.25     49.65
12:28:11 AM       all     46.65      0.00      3.45      0.15     49.75
12:28:16 AM       all     46.80      0.00      3.55      0.25     49.40
12:28:21 AM       all     46.20      0.00      6.80      0.25     46.75
12:28:26 AM       all     43.80      0.00      6.25      0.10     49.85
12:28:31 AM       all     35.05      0.00      7.01      0.30     57.64
12:28:36 AM       all     29.70      0.00      5.58      0.71     64.01
12:28:41 AM       all     41.18      0.00     11.29      8.20     39.33
12:28:46 AM       all     41.57      0.00     10.66      6.75     41.02
12:28:51 AM       all     39.96      0.00     10.62     15.87     33.55
效果上大致相同,这里可以解释为:oracle系统进程的执行时间和sar执行时间存在一定误差,所以造成某些数据匹配补上。

--EOF


2009-4-21

oracle终于买下了sun

oracle终于买下了sun

oracle 以74亿美元买了sun,而IBM最终错过了他的购买机会。

oracle买下sun是业界的大事,也对数据库领域起到了举足轻重的影响。我想对于oracle本身而言,正如larry Ellison所讲,oracle得到了sun的solaris 和java,将更加助力oracle成为贯穿前后台的,从底层到应用的系统集成商。可以预见不久,oracle吸纳了sun在硬件和软件上的优势,特别是mysql的优势,更加巩固oracle在数据库领域的霸主地位。

不知道Microsoft在想什么?oracle一直以来都有和ms争霸的决心和勇气,这几年的势头也处处证明了这一点。记得当初Ellison就公开坦言他比较妒忌盖茨,如今在数据信息领域,oracle已经达到了无法撼动的地位。似乎ellisson的愿望更近了一步。

有些地方还是值得关注和猜测的
1. 不久前,oracle联合HP发布的oracle exadata 和HP oracle database machine 计划。在sun的技术被购入之后,oracle会不会还一如既往的推动。希望不要成为鸡肋。

2.oracle无疑站在数据库领域的无法撼动的霸主地位,这包括开源和商业。那末mysql将来的命运如何?一方面mysql影响力极大,另一方面,照ellison的一贯手法,收购=毁灭,mysql是否也要步以前foxpro,dbase的后尘?只能用时间来验证.

3.oracle一旦完成了收购,IT格局将大有变化。将来我们一打开电脑,就是ms,看到数据没准就是oracle。

oracle成功之后,他的下一个目标会是谁?兴许oracle也有被反垄断法告的那一天。在此环境下,ms的应对策略如何?真希望有一天,ms也迫于竞争,忙于把sql server一直到solaris的上,呵呵。

2009-4-19

绿色计算

绿色计算

所谓绿色计算,当然是和环保连系在一起的。云计算被赋予绿色计算的头衔,其原由很容易被推理:大量的主机被集中化管理,人们只要向服务提供商买服务就可以了。被集中管理的主机有利于从整体上思考环保策略,如将计算中心移到离电厂近的地方,使用管理策略减少能源消耗,等等。总之被可运作的概念和理由很多。但是我还是质疑现实情况。

我们可以类比北京的冬日取暖,究竟是集中供暖更环保还是单户的天然气独立取暖更绿色?众说纷纭,莫衷一是。
支持集中供暖的人会讲:统一管理煤炭,集中燃烧,降低了污染,有利充分利用能源达到环保。
支持单户取暖的人会说:单户方式能够根据用户意愿决定是否使用,更agile,更有利于节约能源,更绿色。
究竟谁对?当然应当看实际数据,但这年头谁又能拿到靠普的数据。本人家里采用单户取暖,冬天取暖炉根本没用过,因为房子的保温效果不错,所以没必要。论其环保,我自然支持单户方式。

现在再回到绿色计算上来,所谓的计算中心,选择降低能源消耗策略应当是顺理成章的事,也符合商家的经济利益。但综合整个社会成本,绿色不绿色就很难讲了,比如,无论运转效果如何,你都需要一个固定的能源消耗,你还需要更多的配套设施,等。总之,这年头绿色就是炒作的话题,最终目的是推广自己的概念和产品。

环保是每个人应该做的,因为大家都住在一个地球上。你怎末推广自己的产品,只要合法,也没人干涉。不过在拿环保说事的同时,要衡量清楚自己有没有真的做到。

2009-4-16

云计算

云计算是一个新潮而且听起来很牛的名词,今天探其根源,俺还是有点体会。

云计算可以说是分布式计算,网格计算的近亲,或者干脆点,就是它们的衍生品。这里的概念模糊不清,关系也并不明确。总之,不同机构有不同解释。

讲讲我的经历。记得最早接触分布式计算是在第一份工作,一个关于电信计费的大型项目。谈到计费就往往少不了数据的问题,比如如何接入通信网络,如何收集数据,也就是ETL。由于数据量庞大,这里ETL暴露出几个问题:
1.如何保证扩展性?
2.如何保证24×7?
3.如何保证处理效率?
4.灾难发生,如何补偿?
回想起那个时候的技术架构,简直就是糟糕到底。一群学院派的老前辈用OLTP方式处理这些数据ETL的问题,数据处理全部集中到数据库,其后果也是恐怖到底。好了,几个月后,数据库就是永远的瓶颈。

现在看来,ETL是最适合分布式计算的应用场景。原因:
1.被处理数据无需考虑状态。可以很随意拆分处理。
2.ETL处理的数据量往往很大,需首要考虑性能问题。分布式可以更好的提高效率。
3.既然有分布式的结构存在,自然扩展性和可靠性就很高。

当时自己也在实践中看到了这一点,所以写了一个很小的通信模块用来实现扩展性。原理很简单,只要这个程序运行,一旦它接收到控制信号,就取出一部分原始数据,执行设定的业务逻辑代码。记得当时写了两个版本,一个用UNIX C,一个用delphi。以后逐渐地把原来放在数据库中的处理,如数据清洗, 移到这些分布模块去做。很大程度上减少了数据库压力,而且效率和扩展性上有了很好的提升,架构也清晰多了。后来又完成了一个程序,起了个名字叫控制面板,就是用来监控各个分布程序的状态。基本上,这样的系统就成了分布式的并发处理系统,尽管很多其它的问题还没有考虑。

以后,偶然机会看到了一个开源项目,叫巡天望远镜计划(http://lsstcorp.org/),关注了一段时间。发现其IT系统的一个模块,用来收集来自射电望远镜的数据,处理方式也是采用分布式并行处理方式,架构与我曾思考的类似。但这样的科研项目面临的挑战就大多了,其一是数据量大,50G/天。其二是,你如何找到足够的机器去完成分布运算环境。靠网友的捐助是比较好的方法,比如贡献你的机器,让它在晚上加入运算网络。当募捐到足够的机器时,一个庞大的计算网格形成了。

当回来商业环境中,我们发现类似的分布式系统还有很多,但总不能都让网友捐助吧。那么只能掏钱买机器,如果觉得贵,又想只临时用一用,怎末办?那就租好了,如同租房一样。如果租实际的机器还觉得麻烦,那该如何?从互联网上租些虚拟的,方便且便宜。正是看到了这个商机,提供此类服务的提供商如雨后春笋般出现。那末需要给这种模式起个名字,既然服务来源于互联网,而且画结构图时,大家都爱把互联网画成一朵云,那就叫’云计算‘吧。

既然可以租到机器,如果能够让这些机器在缺省状态下实现一些基本功能,操作将更加便捷。事实上也的确如此。那末新的问题是,该实现哪些功能?解决方法也比较简单,既然系统架构分为若干层,那末根据系统层次,提供不同的服务。比如某些人需要数据存储,那就有必要专门提供些类似数据存储的服务,于是再给个新名字,云存储(cloud storage),比如Amazon SimpleDB, Google big table. 如果你需要平台性质的计算资源,那末就提供类似的,再给个名字cloud platform,比如 Google App Engine.

总之,云计算更像是市场化的产物,市场行为驱使,人们需要什么,就必然有人提供什么。新的技术,新的名词也就此诞生。





2009-3-31

Pessimistic Locking & Optimistic Locking

转载的


锁( locking  
业务逻辑的实现过程中,往往需要保证数据访问的排他性。如在金融系统的日终结算 
处理中,我们希望针对某个 cut-off 时间点的数据进行处理,而不希望在结算进行过程中 
(可能是几秒种,也可能是几个小时),数据再发生变化。此时,我们就需要通过一些机 
制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓 
    ,即给我们选定的目标数据上锁,使其无法被其他程序修改。 
Hibernate
 支持两种锁机制:即通常所说的  悲观锁( Pessimistic Locking  
  乐观锁( Optimistic Locking    
悲观锁( 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 = session.createQuery(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 加载进来,所谓数据库加锁也就无从谈起。 
乐观锁( Optimistic Locking  
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依 
靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库 
性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。 
如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进 
行修改时(如更改用户帐户余额),如果采用悲观锁机制,也就意味着整个操作过 
程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作 
员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对几

百上千个并发,这样的情况将导致怎样的后果。 
乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本 
 Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于 
数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来 
实现。 
读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提 
交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据 
版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。 
对于上面修改用户帐户信息的例子而言,假设数据库中帐户信息表中有一个 
version
 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100  
操作员 此时将其读出( version=1 ),并从其帐户余额中扣除 $50
 $100-$50 )。 
在操作员 A 操作的过程中,操作员 B 也读入此用户信息( version=1 ),并 
从其帐户余额中扣除 $20  $100-$20 )。 
操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣 
除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大 
于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2  
操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数 
据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的 
数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足  提交版本必须大于记 
录当前版本才能执行更新  的乐观锁策略,因此,操作员 的提交被驳回。 
这样,就避免了操作员 用基于 version=1 的旧数据修改的结果覆盖操作 
 A 的操作结果的可能。 
从上面的例子可以看出,乐观锁机制避免了长事务中的数据库加锁开销(操作员 A
和操作员 B 操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系 
统整体性能表现。 
需要注意的是,乐观锁机制往往基于系统中的数据存储逻辑,因此也具备一定的局 
限性,如在上例中,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户 
余额更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。在 
系统设计阶段,我们应该充分考虑到这些情况出现的可能性,并进行相应调整(如 
将乐观锁策略在数据库存储过程中实现,对外只开放基于此存储过程的数据更新途 
径,而不是将数据库表直接对外公开)。 
Hibernate 
在其数据访问引擎中内置了乐观锁实现。如果不用考虑外部系统对数 
据库的更新操作,利用 Hibernate 提供的透明化乐观锁实现,将大大提升我们的 
生产力。 
Hibernate
 中可以通过 class 描述符的 optimistic-lock 属性结合 version
描述符指定。 
现在,我们为之前示例中的 TUser 加上乐观锁机制。

1  首先为 TUser  class 描述符添加 optimistic-lock 属性: 

name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
optimistic-lock="version"
>
……


optimistic-lock
 属性有如下可选取值: 
Ø none
无乐观锁 
Ø version
通过版本机制实现乐观锁 
Ø dirty
通过检查发生变动过的属性实现乐观锁 
Ø all
通过检查所有属性实现乐观锁 
其中通过 version 实现的乐观锁机制是 Hibernate 官方推荐的乐观锁实现,同时也 
 Hibernate 中,目前唯一在数据对象脱离 Session 发生修改的情况下依然有效的锁机 
制。因此,一般情况下,我们都选择 version 方式作为 Hibernate 乐观锁实现机制。 
2
  添加一个 Version 属性描述符 

name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
optimistic-lock="version"
>
name="id"
column="id"
type="java.lang.Integer"
>



column="version"
name="version"
type="java.lang.Integer"
/>
……


注意 version 节点必须出现在 ID 节点之后。 
这里我们声明了一个 version 属性,用于存放用户的版本信息,保存在 TUser 表的 
version
 字段中。 
此时如果我们尝试编写一段代码,更新 TUser 表中记录数据,如: 
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Erica"));
List userList = criteria.list();
TUser user =(TUser)userList.get(0);
Transaction tx = session.beginTransaction();
user.setUserType(1); //
 更新 UserType 字段 
tx.commit();
每次对 TUser 进行更新的时候,我们可以发现,数据库中的 version 都在递增。 
而如果我们尝试在 tx.commit 之前,启动另外一个 Session ,对名为 Erica 的用 
户进行操作,以模拟并发更新时的情形: 
Session session= getSession();
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Erica"));
Session session2 = getSession();
Criteria criteria2 = session2.createCriteria(TUser.class);
criteria2.add(Expression.eq("name","Erica"));
List userList = criteria.list();
List userList2 = criteria2.list();TUser user =(TUser)userList.get(0);
TUser user2 =(TUser)userList2.get(0);
Transaction tx = session.beginTransaction();
Transaction tx2 = session2.beginTransaction();
user2.setUserType(99);
tx2.commit();
user.setUserType(1);
tx.commit();
执行以上代码,代码将在 tx.commit() 处抛出 StaleObjectStateException  
常,并指出版本检查失败,当前事务正在试图提交一个过期数据。通过捕捉这个异常,我 
们就可以在乐观锁校验失败时进行相应处理