<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_Richie</title><subtitle type="text">Sometimes at night when I look up at the stars, and see the whole sky just laid out there, don't you think I ain't remembering it all. I still got dreams like anybody else, and ever so often, I am thinking about how things might of been. And then, all of a sudden, I'm forty, fifty, sixty years old, you know?</subtitle><id>http://feed.cnblogs.com/blog/u/21032/rss</id><updated>2011-09-16T11:47:14Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/21032/rss"/><entry><id>http://www.cnblogs.com/RicCC/archive/2010/09/26/1836174.html</id><title type="text">SQL Server用错关联方式</title><summary type="text">SQL Server中不少怪异问题都是由用错关联方式引起的，从2000到2005有所改善，但2005的查询优化引擎还是存在&amp;#8220;犯傻&amp;#8221;的时候1. 问题1现象：一个存储过程，通过一个服务程序调用，长时间不能结束，数据库服务器显示该存储过程执行到某个语句时一直等待在那，数据库服务器内存充足，CPU消耗几乎没有。把这个存储过程拿出来直接在查询分析器中执行，参数跟程序调用时完全类似，立...</summary><published>2010-09-26T09:51:00Z</published><updated>2010-09-26T09:51:00Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><link rel="alternate" href="http://www.cnblogs.com/RicCC/archive/2010/09/26/1836174.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/archive/2010/09/26/1836174.html"/><content type="html">SQL Server中不少怪异问题都是由用错关联方式引起的，从2000到2005有所改善，但2005的查询优化引擎还是存在&amp;#8220;犯傻&amp;#8221;的时候&lt;br /&gt;&lt;br /&gt;1. 问题1&lt;br /&gt;现象：一个存储过程，通过一个服务程序调用，长时间不能结束，数据库服务器显示该存储过程执行到某个语句时一直等待在那，数据库服务器内存充足，CPU消耗几乎没有。把这个存储过程拿出来直接在查询分析器中执行，参数跟程序调用时完全类似，立即结束且结果正确&lt;br /&gt;解决方案：排除了阻塞等原因，因为放在查询分析器中执行时一切正常，从执行计划等方面无法看出任何问题，也排除了磁盘IO等方面的原因，实在想不到其他的了。最后怀疑是SQL Server查询引擎JOIN方式选的不对，强制使用HASH JOIN后，程序调用恢复正常&lt;br /&gt;疑点：一直没有发现程序调用与直接使用查询分析器执行，这2者之间存在哪些差别，会影响到SQL Server查询优化决策&lt;br /&gt;&lt;br /&gt;2. 问题2&lt;br /&gt;现象：一个不算复杂的查询，用到了row_number函数分页，一执行就会导致服务器8个CPU全部100%，很长时间（好几分钟）不能结束。使用临时表实现同样的效果，几秒钟完成&lt;br /&gt;语句1：&lt;br /&gt;&amp;nbsp;&amp;nbsp; SELECT &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ROW_NUMBER() OVER(ORDER BY&amp;nbsp; COLUMNNAME1&amp;nbsp; ASC) as FC_ROWNUMBER&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ,COUNT(1) OVER() AS FC_COUNT&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ,* FROM ( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SELECT 产品 as COLUMNNAME1,产品描述 as COLUMNNAME2,入库日期 as COLUMNNAME7,预期数量 as COLUMNNAME8&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,入库数量 as COLUMNNAME9,行状态 as COLUMNNAME10,供应商 as COLUMNNAME11,供应商名称 as COLUMNNAME12&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FROM V_收货明细查询&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&amp;nbsp;&amp;nbsp; 入库日期 &amp;gt;=&amp;nbsp; '2010-7-1'&lt;br /&gt;&amp;nbsp;&amp;nbsp; ) TT_MAINKEY_TMP &lt;br /&gt;执行计划：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/error-QEP-query-execution1.JPG" height="251" width="990" /&gt;&lt;br /&gt;&lt;br /&gt;语句2：&lt;br /&gt;SELECT * FROM (&lt;br /&gt;&amp;nbsp;&amp;nbsp; SELECT &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ROW_NUMBER() OVER(ORDER BY&amp;nbsp; COLUMNNAME1&amp;nbsp; ASC) as FC_ROWNUMBER&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ,COUNT(1) OVER() AS FC_COUNT&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ,* FROM ( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SELECT 产品 as COLUMNNAME1,产品描述 as COLUMNNAME2,入库日期 as COLUMNNAME7,预期数量 as COLUMNNAME8&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,入库数量 as COLUMNNAME9,行状态 as COLUMNNAME10,供应商 as COLUMNNAME11,供应商名称 as COLUMNNAME12&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; FROM V_收货明细查询&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&amp;nbsp;&amp;nbsp; 入库日期 &amp;gt;=&amp;nbsp; '2010-7-1'&lt;br /&gt;&amp;nbsp;&amp;nbsp; ) TT_MAINKEY_TMP &lt;br /&gt;) TT_RET_TMP &lt;br /&gt;WHERE FC_ROWNUMBER BETWEEN 1 AND 1000 &lt;br /&gt;ORDER BY&amp;nbsp; COLUMNNAME1&amp;nbsp; ASC&lt;br /&gt;执行计划：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/error-QEP-query-execution2.JPG" height="251" width="746" /&gt;&lt;br /&gt;&lt;br /&gt;仅仅是把SQL作为一个子查询，在外面多包装了一下，整个查询计划就不一样了。排除了统计信息不准确、索引碎片等状况&lt;br /&gt;2个查询计划中对3个表使用的都是聚集索引扫描，基本上就是关联算法不一样&lt;br /&gt;因为用的并行查询，三个表数据都有几十万和一百多万，嵌套循环需要执行几十万次，所以单个查询导致所有CPU都100%。估计高CPU是由于Lazy Spool操作造成的&lt;br /&gt;&lt;br /&gt;解决方案：&lt;br /&gt;强制用HASH JOIN，或者加索引避免SQL Server出错，或者用临时表绕过去&lt;img src="http://www.cnblogs.com/RicCC/aggbug/1836174.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/09/26/1836174.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/RicCC/archive/2010/09/15/Transactional-Information-Systems-Part1.html</id><title type="text">事务信息系统-并发控制与恢复的理论, 算法与实践-计算模型, 并发控制部分</title><summary type="text">事务信息系统-并发控制与恢复的理论, 算法与实践页的存储结构图：数据库页的存储布局页是磁盘与主存间传输数据的最小单元，也是内存中进行缓存的单元页头（page header）包含页内空间管理的一些信息，比如空闲空间字节数、最大空闲区域大小等页槽Slot Array的作用：varchar等变长类型字段的更新操作等，可能导致记录在页内移动，如果外部直接以数据记录的物理地址进行引用，记录移动时处理非常复杂...</summary><published>2010-09-15T14:51:00Z</published><updated>2010-09-15T14:51:00Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><link rel="alternate" href="http://www.cnblogs.com/RicCC/archive/2010/09/15/Transactional-Information-Systems-Part1.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/archive/2010/09/15/Transactional-Information-Systems-Part1.html"/><content type="html">&lt;a target="_blank" href="http://book.douban.com/subject/1503812/"&gt;事务信息系统-并发控制与恢复的理论, 算法与实践&#xD;
&lt;/a&gt;&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;&#xD;
页的存储结构&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/database-page-storage-layout.jpg" height="376" width="467" /&gt;&lt;br /&gt;&#xD;
图：数据库页的存储布局页是磁盘与主存间传输数据的最小单元，也是内存中进行缓存的单元&lt;br /&gt;&#xD;
页头（page header）包含页内空间管理的一些信息，比如空闲空间字节数、最大空闲区域大小等&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;页槽Slot Array的作用：&lt;/strong&gt;&lt;br /&gt;&#xD;
varchar等变长类型字段的更新操作等，可能导致记录在页内移动，如果外部直接以数据记录的物理地址进行引用，记录移动时处理非常复杂，所以在中间添加一个隔离层Slot Array。Slot Array中存放页内数据的实际地址，以页号+槽号形成RID，外部均以RID引用数据记录&lt;br /&gt;&#xD;
数据在页内移动时只需要更新Slot Array中的实际地址即可，页间移动可以使用一个Forwarding RID实现，例如上图中的示例&lt;br /&gt;&#xD;
对于包含BLOB等长字段类型的记录，简单技巧可以将列的实际数据存储在其他多个页中，而在记录实际数据位置存储页号列表&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
一次磁盘IO可以读取一个或多个连续的页，可以一次读取的固定数目的页称作块（block），在数据库理论中块和页等同对待&lt;br /&gt;&#xD;
数据库系统通常以区间（extent）为单位预先分配磁盘空间（一次预先分配一个或多个区间），区间是多个连续页。注意区间和块的区别&lt;br /&gt;&#xD;
存储层需要保存必要的元数据，例如将页号转换成物理磁盘地址、空闲空间管理等。通常使用区间表（extent table）来完成页号与物理磁盘地址的转换&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;计算模型 Computational Models&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
&lt;strong&gt;页模型page model&lt;/strong&gt;&lt;br /&gt;&#xD;
也称作读写模型read/write model，是一种简单模型。例如事务t使用页模型定义类似如下：t=r(x)w(x)r(y)r(u)w(y)，其中r表示读取数据页，w表示写数据页，对于存储层而言一个事务就是这样一个读写序列或偏序&lt;br /&gt;&#xD;
页模型通过简明优雅的方式抓住并发控制与恢复的实质，可以描述很多系统实现中的重要问题，局限在于仅以低层页的读写操为元素，没有表达数据访问操作的语义&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;对象模型object model&lt;/strong&gt;&lt;br /&gt;&#xD;
对象模型在页模型的基础上考虑高层操作，在对象模型中事务由ADT（abstract data type）操作构成，最终仍归于低层页的读写&lt;br /&gt;&#xD;
下面是一个对象模型示例图：&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/transaction-object-model-example.JPG" height="196" width="459" /&gt;&lt;br /&gt;&#xD;
图：对象模型示例示例场景说明如下：&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp; * 事务t1中先执行一个select语句，找出住在城市Austin的人员，然后执行一条insert语句，插入一条住在城市Austin的人员信息&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp; * 假设表中只有一个索引，位于城市这个字段，使用的B+ tree结构，索引结构只有2层&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
ADT操作以及页的读写说明如下：&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 1. Search('Austin'): select语句使用城市的索引查找符合Austin的索引项，得到RID列表&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * r(r): 读取B+ tree索引根节点页&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * r(l): 读取B+ tree索引叶节点页&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 2. Fetch(x), Fetch(y): 根据RID列表加载数据x、y所在的数据页，得到数据记录x、y&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * r(p), r(q): 分别读取RID x和y所在的数据页p和q&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 3. Store(z): 插入记录z&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * r(f): 读取存储层的元数据页f，其中记录了空闲空间信息&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * r(p): 从页f上得到页p具有足够空间插入记录z，这里读取这个页p&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * w(p): 把记录z写入页p，并将页p写回磁盘&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * r(r), r(l), w(l): 更新城市字段的索引，读取索引根节点页r、叶节点页l，更新叶节点页l并写回磁盘&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
对象模型事务t是一棵树，用事务标识符标记根节点，用被调用的操作名称、参数标记非叶节点，用页模型的读写操作标记叶节点&lt;br /&gt;&#xD;
如果将事务的操作序列看成全序，则这个事务只能串行执行；如果将其看作偏序，则其中某些操作可以并行执行&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;经典并发问题&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
脏读dirty-read、不一致读inconsistent-read、幻象读phantom-read参考&lt;a target="_blank" href="http://www.cnblogs.com/RicCC/archive/2010/03/05/transaction-lock-isolation-level.html"&gt;Transaction, Lock, Isolation Level&lt;/a&gt;&lt;br /&gt;&#xD;
更新丢失lost-update：&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/lost-update-problem.JPG" height="196" width="421" /&gt;&lt;br /&gt;&#xD;
图：丢失更新问题&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;页模型上的并发控制 - 调度协议&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
页模型的可串行性理论有终态可串行性、视图可串行性、冲突可串行性等，由于判定终态可串行性和视图可串行性的复杂度太高，因此商业数据库基本都采用冲突可串行性CSR类进行判定&lt;br /&gt;&#xD;
概念：&lt;br /&gt;&#xD;
历史history：指一个完整、已经结束的事务，即事务要么使用commit提交了，要么使用abort终止了&lt;br /&gt;&#xD;
调度schedule：指未完成的事务，即历史的一个前缀&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/transaction-scheduler.JPG" height="274" width="444" /&gt;&lt;br /&gt;&#xD;
图：Transaction Scheduler图：事物调度器 Transaction Scheduler&lt;br /&gt;&#xD;
client 1、client 2等代表不同事物的操作序列&lt;br /&gt;&#xD;
事务管理器TM主要任务是事物登记，管理trans、commit、abort、active列表，维护ready-to-execute列表等&lt;br /&gt;&#xD;
调度器从TM接收输入调度，将其转化为可串行化的输出调度&lt;br /&gt;&#xD;
调度器分为乐观的、悲观的&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;死锁处理&lt;/strong&gt;&lt;br /&gt;&#xD;
一般基于等待图waits-for graph WFG概念，WFG中存在环路时即发生死锁&lt;br /&gt;&#xD;
死锁检测：持续检测continuous detection、周期检测periodic detection&lt;br /&gt;&#xD;
死锁处理方法一：允许发生死锁，从死锁中选出牺牲者消除WFG环路；处理方法二：死锁预防，即不允许死锁情况发生&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;两阶段封锁协议 - two-phase locking protocol - 2PL&lt;/strong&gt;&lt;br /&gt;&#xD;
对每个事物，加锁阶段严格区别于紧接着的释放阶段，该封锁协议就是两阶段的&lt;br /&gt;&#xD;
即可以明确的将事务分成前后两个部分，在前面部分只存在加锁，不存在锁释放，而在后面部分则只存在锁释放操作，没有加锁操作。商业数据库考虑性能问题不会严格按照2PL协议实现。SQL92不同的隔离级别本身就是性能与可串行性之间的一个取舍平衡，另外页模型并没有考虑到语义方面，商业数据库部分结合语义方面之后也能对封锁协议进行改造优化以换取性能&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/transaction-2pl.JPG" height="131" width="262" /&gt;&lt;br /&gt;&#xD;
图：2PL下锁增长和缩减阶段示意图图：2PL下锁增长和缩减阶段示意图&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
2PL协议具有C2PL、S2PL、SS2PL几种变体&lt;br /&gt;&#xD;
下面是一个2PL调度器的调度示例：&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/transaction-scheduler-2pl-example.JPG" height="213" width="464" /&gt;&lt;br /&gt;&#xD;
图：调度示例图：2PL调度器调度示例&lt;br /&gt;&#xD;
顶部的s是输入，底部3行是经过2PL调度器的一种可行的输出&lt;br /&gt;&#xD;
下表1、2、3等表示不同的事物t1、t2、t3等，wl表示加写锁，wu表示释放写锁，rl表示加读锁，ru表示释放读锁，c表示事物结束（commit）&lt;br /&gt;&#xD;
中间部分锁的持有时序图中，虚线部分表示锁冲突时的等待时间&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;保守2PL - Conservative 2PL - C2PL&lt;/strong&gt;&lt;br /&gt;&#xD;
在事物开始时对所有需要访问的数据获取锁。这个协议只能在有限的应用场景下使用，他不存在死锁问题，因为事物要么等待不能开始，要么就已经得到了全部所需的锁了&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/transaction-c2pl.JPG" height="132" width="268" /&gt;&lt;br /&gt;&#xD;
图：C2PL下锁增长和缩减阶段示意图图：C2PL下锁增长和缩减阶段示意图&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;严格2PL - Strict 2PL - S2PL&lt;/strong&gt;&lt;br /&gt;&#xD;
事物一直持有已经获得的所有写锁，直到事物终止&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/transaction-s2pl.JPG" height="132" width="264" /&gt;&lt;br /&gt;&#xD;
图：S2PL下锁增长和缩减阶段示意图图：S2PL下锁增长和缩减阶段示意图（假设事物中的锁都是写锁）&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;强2PL - Strong 2PL - SS2PL&lt;/strong&gt;&lt;br /&gt;&#xD;
事物获得的所有锁（包括读锁和写锁）被一直保持直到事物终止。这种情况下锁的增长和缩减阶段与S2PL相同，不过不局限于写锁&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;有序共享2PL - Ordered Sharing 2PL - O2PL&lt;/strong&gt;&lt;br /&gt;&#xD;
在2PL中，如果对同一个数据项申请的锁，与这个数据项上面已经存在的锁不相容时，就只能等待&lt;br /&gt;&#xD;
有序共享相容性规则：同一个数据项上的两个锁无论是否冲突，只要锁操作与相应的数据操作均以相同的顺序执行就可以被不同事物同时持有。这个规则以固定的顺序对数据项进行操作为前提，从而放宽了锁相容性规则，理论上证明是可行的，但他必须强制数据访问的顺序，需要额外的数据结构和运行时代价，实际中比较少采用&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;利他锁 - Altruistic Locking - AL&lt;/strong&gt;&lt;br /&gt;&#xD;
是2PL的一种扩展协议。某些事物时间可能很长，持有大量的锁，这样可能阻塞大量其他短事物。如果短事物访问的仅仅是长事物已经处理过的数据项的子集，则长事物将这部分数据项捐赠给其他事物访问&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;非两阶段封锁协议&lt;/strong&gt;&lt;br /&gt;&#xD;
有只写封锁树 write-only tree locking WTL、读写封锁树read/write tree locking RWTL等，他们只能在对数据项的访问遵从特定的顺序的情况下使用，比如对数据项的访问呈树状方式&lt;br /&gt;&#xD;
WTL在封锁规则的基础上添加了两条附加规则：只有在持有父项数据写锁的情况下，才能获取子项数据的写锁；事物对释放某个数据项的写锁之后，不能在获得该数据项的写锁。这确保了数据更新处理将沿着树的某一路径从根节点向叶节点进行，锁的范围可以限制在某个子树范围内。锁的申请和释放并不是两阶段的，沿着数的路径向子树进行时，父项的锁可以释放，即锁的申请和释放可以交替进行&lt;br /&gt;&#xD;
WTL协议是无死锁的，对RWTL协议规则稍作修改可以导出DAG封锁协议DAG locking protocol&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;时间戳排序协议 - timestamp ordering protocol - TO&lt;/strong&gt;&lt;br /&gt;&#xD;
不使用封锁，基本规则是对不同事物冲突的操作，严格按照事物开始时间的先后关系进行调度&lt;br /&gt;&#xD;
在封锁协议中通过锁机制检测冲突，在TO中也需要有额外的信息用于检测冲突。例如Ti在Tj之前开始，调度器先对Tj输出了一个调度qj(x)，但后来 Ti发送过来一个pi(x)的操作，与qj(x)冲突（即按照TO规则，pi(x)必须在qj(x)之前执行，但在pi(x)之前qj(x)已经被调度输出了），所以调度器需要记录信息以检测这种冲突并作出相应处理（例如阻塞Ti，等待Tj结束后再开始）&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;SGT协议 - Serialization Graph Tester - 可串行化图检测器&lt;/strong&gt;&lt;br /&gt;&#xD;
维护一张SGT图，图中并发的每个事物拥有一个节点，事物Ti的操作与Tj冲突，如果Tj中发生冲突的操作已经输出了，则由Tj向Ti添加一条有向边，Ti进入等待状态。如果事物Ti的某个操作导致SGT图中存在环路，则输出是不可串行化的，事物Ti需要取消&lt;br /&gt;&#xD;
实际中SGT协议需要维护的额外数据量以及运算量都比较大，对于调度器而言无法接收，所以很少采用&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;对象模型上的并发控制，搜索结构上的并发控制&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
......&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;多版本并发控制 - multiversion concurrency control&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
单版本时数据库中的每个数据项只有一个副本，而多版本时则会为数据项同时维护多个版本，而多版本对于client是透明的，对client而言数据库仍然只维护了数据项的一个副本。通过对并发控制算法的扩展调整，多版本同样可以达到可串行性目标，他仅仅是并发控制的另一种处理方式。他的优点是可以降低封锁协议中锁的使用，极大的提高并发处理性能，并给数据恢复等方面带来好处&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;多版本时间戳排序协议 - multiversion timestamp ordering protocol - MVTO&lt;/strong&gt;&lt;br /&gt;&#xD;
本质上类似FIFO方式处理并发操作。每个事物按照事物开始时的时间戳排序，基于这个顺序处理调度和冲突&lt;br /&gt;&#xD;
下面对的规则描述中，Ti表示第i个事物；ts(Ti)表示第i个事物的时间戳；Ri(X)表示Ti读取数据项X；Xk则表示数据项X的某一个版本，他是由事物Tk写入的；ts(Xk)表示数据项的Xk这个版本的时间戳，他就是事物Tk的时间戳，即ts(Xk)=ts(Tk)。下面是MVTO规则：&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 1. Ri(X)将被转化为Ri(Xk)，即需要明确读取数据项X的哪一个版本，其中Xk应该为ts(Xk)&amp;lt;ts(Ti)的一个最新版本。另外Xk可能是已提交的版本，也可能是未提交的版本&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 2. 处理Wi(X)时，如果已经存在一个Rj(Xk)操作，并且时间戳关系为ts(Xk)&amp;lt;ts(Ti)&amp;lt;ts(Tj)，则Wi(X)被拒绝，Ti将被取消，因为这样存在冲突，将造成不可串行化的结果。否则，Wi(X)被转化为Wi(Xi)并执行，即为数据项X生成一个新的版本Xi&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 3. 可选规则：延迟提交。这是在不允许脏读的情况下用来避免产生脏读的一个机制。如果事物Ti读取了Tj写入的某个数据项，即存在Ri(Xj)操作，则事物Ti的提交操作Ci需要被推迟到事物Tj成功提交之后再执行。如果事物Tj失败则事物Ti也失败（或重做）&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
下面是MVTO协议下的一个执行示例，可以使用上面的规则进行解释：&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/transaction-mvto-example.JPG" /&gt;&lt;br /&gt;&#xD;
图：MVTO协议下的执行示例&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp; * 事物t3中因为存在r3(x2)操作，即读取了x2这个未提交的版本（按照规则1，他应该读取x2的），所以他的提交被延迟（虚线表示等待），在t2提交之后再提交&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp; * t4被终止是因为规则2的作用，因为t5中存在r5(y2)这个操作，如果让w4(y4)成功，则事物t5可能出现不一致读的情况，不是一个可串行化的调度。图来自原书，图中应该有个错误，r5(y2)不可能出现在w2(y2)前面的，他应该位于w2(y2)与w4(y4)之间才对&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp; * 最后看一下t1，在读取y的时候，根据MVTO的规则1，应该读取y0这个版本。这样在没有加锁的情况下实现了事物t1的一致性读，但他并没有利用锁阻塞t2、t4（两阶段封锁协议的情况下他们的写操作与t1的读锁冲突），而是让t2、t4并行执行。根据MVTO规则，t1不能有写x、y的操作，否则按照规则2应该取消t1。这样从理论上看存储层还是可以保证x、y的可串行性结果的，但存在一个疑问是：&lt;strong&gt;如果t1有其他写操作，比如 w1(u1)，而u1是基于x、y的运算而来，即u1=f0(u)f1(x)f2(y)，如何确保数据项u的可串行性结果呢？还有对外部而言t1实现了对 x、y的一致性读，但w2(y2)成功了，外部基于x、y得来的计算结果可能就是一个不正确的值，如何确保一致性？&lt;/strong&gt;&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
另外还有多版本两阶段封锁协议 - multiversion 2PL locking protocol - MV2PL、只读多版本协议 - read-only multiversion protocol - ROMV等&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;暂态版本化 - Transient Versioning&lt;/strong&gt;&lt;br /&gt;&#xD;
暂态版本化的做法是将数据项的最新版本放在数据记录位置上，数据项以前的旧版本都放在版本池version pool中，版本池可以驻留内存或者磁盘。这样当访问最新版本数据时没有性能损失，版本池也方便对旧版本的垃圾回收。有些系统中把版本池跟用于恢复的日志项合并在一起。版本池也叫做回滚段rollback segment&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/data-structure-of-transient-versioning.JPG" height="233" width="352" /&gt;&lt;br /&gt;&#xD;
图：暂态版本化的存储组织图：暂态版本化的存储组织&lt;br /&gt;&#xD;
数据项的每个版本包括两个额外的字段，一个是创建时间戳，另一个是指向前一版本的指针&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp; * 对于新增的数据项，还没有旧版本，因此在当前版本中指针为空，例如图中的1135&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp; * 删除的数据仍然保留在当前版本位置上，但会打上删除标记，例如图中的1141&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp; * 同一个数据项的各个版本组成一个链表&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
另一种方法是在数据项的当前版本中维护一个小的版本选择表，其中包含每个版本的时间戳以及一个指向该版本位置的指针&lt;br /&gt;&#xD;
如果一个数据项的旧版本再也不会被当前活动的事务使用到，则这些旧版本就成为垃圾，可以被回收掉&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;关系数据库的并发控制&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
&lt;strong&gt;面向谓词的并发控制 - Predicate-Oriented Concurrency Control&lt;/strong&gt;&lt;br /&gt;&#xD;
封锁协议是针对页、数据项加锁，而面向谓词的并发控制是针对操作加锁，更具体的是针对操作的谓词加锁。例如：&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp; select name from persons where age&amp;gt;30&lt;br /&gt;&#xD;
则将age&amp;gt;30与锁关联&lt;br /&gt;&#xD;
面向谓词的并发控制是比较早提出来的一种想法，后来也经过了不少研究，他的问题在于检测两个谓词是否兼容是一个NP完全问题，成本太高，但在特定场景下，例如B+树上的并发控制，具有实际意义&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;实现和实用性问题&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
通用的锁管理器数据结构&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/tx/data-structure-of-lock-manager.JPG" height="382" width="614" /&gt;&lt;br /&gt;&#xD;
图：锁管理器的数据结构&lt;br /&gt;&#xD;
锁管理器需要达成的目标：&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 1. 请求锁时可以检验冲突&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 2. 释放锁时，需要将资源授予被该锁阻塞的其他锁&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 3. 事务终止时释放该事务的所有锁&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
锁管理器的数据结构常驻内存，由resource ID索引的哈希表存放已经加锁以及需要加锁的资源ID&lt;br /&gt;&#xD;
每个哈希表项指向一个资源控制块RCB，RCB中的hash chain用于解决hash冲突&lt;br /&gt;&#xD;
对于共享锁，多个锁可以同时被同一资源持有，多个锁请求（例如冲突的写锁）可能正在等待被授予该资源上，这样在同一个资源上将形成一个已持有的共享锁列表以及等待被授予的排他锁与共享锁队列，因此在RCB上会指向一个锁控制块LCB的链接表，即上图中的FirstInQueue, NextInQueue链表&lt;br /&gt;&#xD;
为了达成第3点，为每个活动事务维护一个事务控制块TCB，同样使用链表将该事务的所有锁链接起来，即上图中的LCB chain构成的链表&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
锁的数据结构将是数据库中访问最频繁的地方之一，因此需要控制这个内存数据结构的大小来避免性能问题，所以一般数据库中存在锁粒度升级的处理方式。如果一个事务已经持有了大量细粒度的行锁，数据库可能会将锁粒度升级为页锁或者表锁，以减小锁管理器的内存空间&lt;br /&gt;&#xD;
使用了不同粒度的锁，就存在不同粒度之间相容性的判断问题，所以引入了意向锁，参考&lt;a target="_blank" href="http://www.cnblogs.com/RicCC/archive/2010/09/15/Database-System-An-Application-Oriented-Approach.html"&gt;数据库系统 - 面向应用的方法&lt;/a&gt;&lt;img src="http://www.cnblogs.com/RicCC/aggbug/1827449.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/09/15/Transactional-Information-Systems-Part1.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/RicCC/archive/2010/09/15/Database-System-An-Application-Oriented-Approach.html</id><title type="text">数据库系统 - 面向应用的方法</title><summary type="text">数据库系统-面向应用的方法一些概念关系、元组、属性在关系数据库理论中，关系 (relation)对应于数据库技术的表 (table)，元组 (tuple)对应于行 (row)，属性 (attribute)对应于列 (column)元组的元 (arity)为属性数量，即列的数量，一元unitary，二元binary，三元ternary等关系的势 (cardinality)为关系中的元组数量，即行数P...</summary><published>2010-09-15T14:25:00Z</published><updated>2010-09-15T14:25:00Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><link rel="alternate" href="http://www.cnblogs.com/RicCC/archive/2010/09/15/Database-System-An-Application-Oriented-Approach.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/archive/2010/09/15/Database-System-An-Application-Oriented-Approach.html"/><content type="html">&lt;a target="_blank" href="http://book.douban.com/subject/1914952/"&gt;数据库系统-面向应用的方法&lt;/a&gt;&#xD;
&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 18pt;"&gt;&#xD;
&lt;/span&gt;&lt;strong style="font-size: 18pt;"&gt;一些概念&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
&lt;strong&gt;关系、元组、属性&lt;/strong&gt;&lt;br /&gt;&#xD;
在关系数据库理论中，&lt;strong&gt;关系 (relation)&lt;/strong&gt;对应于数据库技术的表 (table)，&lt;strong&gt;元组 (tuple)&lt;/strong&gt;对应于行 (row)，&lt;strong&gt;属性 (attribute)&lt;/strong&gt;对应于列 (column)&lt;br /&gt;&#xD;
元组的&lt;strong&gt;元 (arity)&lt;/strong&gt;为属性数量，即列的数量，一元unitary，二元binary，三元ternary等&lt;br /&gt;&#xD;
关系的&lt;strong&gt;势 (cardinality)&lt;/strong&gt;为关系中的元组数量，即行数&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;Projection operator 投影运算&lt;/strong&gt;&lt;br /&gt;&#xD;
即选取仅选取部分列的查询&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;Materialized view 物化视图&lt;/strong&gt;&lt;br /&gt;&#xD;
即被高速缓存的视图&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;RAID&lt;/strong&gt;&lt;br /&gt;&#xD;
Redundant Array of Independent Disks，廉价磁盘冗余阵列。利用拆分（striping）提高性能，使用冗余提高可靠性。参考RAID on wikipedia&lt;br /&gt;&#xD;
&lt;strong&gt;RAID0&lt;/strong&gt;&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/RAID0.png" height="185" width="120" /&gt;&lt;br /&gt;&#xD;
RAID0块级拆分，没有冗余。拆分的文件支持并发存取，但任何一块磁盘损坏将导致所有文件损坏&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;RAID1&lt;/strong&gt;&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/RAID1.png" height="185" width="120" /&gt;&lt;br /&gt;&#xD;
RAID1仅做冗余，没有拆分。支持并发读取，提高了读取性能，写入时需要同时写入多块磁盘&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;RAID2&lt;/strong&gt;&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/RAID2.png" height="200" width="400" /&gt;&lt;br /&gt;&#xD;
RAID2字节级拆分（文件相邻的字节均被拆分存储到不同磁盘中），有一块或多块奇偶校验盘（图中的Disk 4, 5, 6）用于容错&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;RAID3&lt;/strong&gt;&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/RAID3.png" height="222" width="300" /&gt;&lt;br /&gt;&#xD;
RAID3字节级拆分，有一块独立的奇偶校验盘（图中的Disk 3）&lt;br /&gt;&#xD;
&lt;strong&gt;&lt;br /&gt;&#xD;
RAID4&lt;/strong&gt;&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/RAID4.png" height="222" width="300" /&gt;&lt;br /&gt;&#xD;
RAID4块级拆分，有一块独立的奇偶校验盘。任何一块磁盘损坏不会造成数据丢失，2块盘损坏时将丢失数据&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;RAID5&lt;/strong&gt;&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/RAID5.png" /&gt;&lt;br /&gt;&#xD;
RAID5块级拆分，具备奇偶校验，但没有独立的奇偶盘，奇偶校验与文件数据一起被拆分在多个盘中。任何一块磁盘损坏不会造成数据丢失，2块盘损坏时将丢失数据&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;RAID6&lt;/strong&gt;&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/RAID6.png" height="200" width="340" /&gt;&lt;br /&gt;&#xD;
RAID6块级拆分，有2块奇偶盘，最多在2块磁盘损坏时不会造成数据丢失&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;存储&lt;/strong&gt;&lt;br /&gt;&#xD;
&lt;strong&gt;堆文件 heap file&lt;/strong&gt;：记录无序，仅在末尾添加，删除记录时不释放空间，不适合查询&lt;br /&gt;&#xD;
&lt;strong&gt;顺序文件 sorted file&lt;/strong&gt;：按某些列的顺序存放，可运用二分查找等优化算法&lt;br /&gt;&#xD;
&lt;strong&gt;填充因子 fill factor&lt;/strong&gt;：每页预留空间供插入操作，预留空间满了以后使用溢出链overflow chain支持插入操作&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;索引&lt;/strong&gt;&lt;br /&gt;&#xD;
&lt;strong&gt;集成索引 integrated index&lt;/strong&gt;：索引条目跟数据记录集成在一起&lt;br /&gt;&#xD;
&lt;strong&gt;聚集索引 clustered index&lt;/strong&gt;：对于有序索引（B+ tree等），如果索引条目与数据记录都是按照相同的search key排序的，则为聚集索引，集成索引是一种聚集索引；对于散列（hash）索引，如果散列索引条目就是数据记录则为聚集的，此时hash bucket本身就是数据文件的存储结构，数据文件就是hash bucket的序列&lt;br /&gt;&#xD;
聚集索引通常又称为main index，非聚集索引又称为secondary index&lt;br /&gt;&#xD;
&lt;strong&gt;稠密索引 dense idnex&lt;/strong&gt;：索引条目与数据记录一一对应&lt;br /&gt;&#xD;
&lt;strong&gt;稀疏索引 sparse index&lt;/strong&gt;：索引条目与数据页一一对应（与数据记录为一对多关系）&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 12pt;"&gt;ISAM: Indexed Sequential Access Method&lt;/strong&gt;&lt;br style="font-size: 12pt;" /&gt;&#xD;
ISAM结构示意图：&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/ISAM-structure.jpg" height="211" width="600" /&gt;&lt;br /&gt;&#xD;
ISAM结构示意图非页节点称为分隔符级，一个分隔符节点示意图如下：&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/ISAM-separator-node.jpg" height="54" width="300" /&gt;&lt;br /&gt;&#xD;
ISAM分隔符节点示意图其中Pn为指向下一级节点的指针，Kn为搜索码的值。Pi-1指向的K值&amp;lt;Ki&amp;lt;=Pi指向的K值&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
ISAM的分隔符级节点一旦创建便不再更改，因此ISAM被称为静态索引。叶子页中的条目被除时其空间不会释放，相关的分隔符级也不会更新，即可能出现分隔符级页中出现的某些search key在叶子节点上没有对应条目。插入时叶子页使用fill factor和overflow chain&lt;br /&gt;&#xD;
ISAM对相对静态的表有效，动态表通常不用ISAM索引&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;B+ Tree&lt;/strong&gt;&lt;br /&gt;&#xD;
B+ tree结构示意图：&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/B-tree-structure.jpg" height="173" width="658" /&gt;&lt;br /&gt;&#xD;
B+ tree结构示意图根节点到达任何一个叶子页的级数是一样的。叶子页添加了兄弟指针，方便range search，支持范围搜索、等值搜索、部分码搜索方式&lt;br /&gt;&#xD;
叶子页至少包含(n-1)/2个值，最多包含n-1个值。维护B+ Tree结构（插入、删除操作等）算法复杂&lt;br /&gt;&#xD;
节点与ISAM类似：&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/B-tree-separator-node.jpg" height="88" width="400" /&gt;&lt;br /&gt;&#xD;
B+ tree节点示意图&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;Hash索引&lt;/strong&gt;&lt;br /&gt;&#xD;
只能等值搜索，这种情况下比B+ tree更有效&lt;br /&gt;&#xD;
也需要使用overflow chain&lt;br /&gt;&#xD;
有静态散列算法、动态散列算法（包括可扩展散列、线性散列）&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;Bit map 位图索引&lt;/strong&gt;&lt;br /&gt;&#xD;
位图索引一般用于低势属性（取值范围很小的属性）&lt;br /&gt;&#xD;
为每一个取值建立一个位图向量，每一位对应于数据的某一行。例如有PERSON表SEX列，取值范围为(MALE, FEMALE)，数据4000行，则将建立2个位图向量V(male)、V(female)，每个向量有4000位。如果V(male)i=1则表示第i 行记录SEX列取值MALE&lt;br /&gt;&#xD;
现在假如有这样一个查询：SEX='MALE' AND SMOKE='YES'，如果这2个列都有位图索引，则可以用V(sex-male) &amp;amp; V(smoker-yes)这个运算（位AND运算）得到满足条件的行&lt;br /&gt;&#xD;
位图索引存在空间浪费，但特定情况下效率很高。另外位图索引的维护成本比较高，因此不适合频繁插入、更新的表&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;查询优化器&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
&lt;strong&gt;Rule based optimizer&lt;/strong&gt;：基于既定的规则优化查询计划&lt;br /&gt;&#xD;
&lt;strong&gt;Cost-based optimizer&lt;/strong&gt;：在RBO的基础上增加统计信息，结合评估成本与既定规则优化查询计划&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;事物&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&#xD;
有关事务的部分内容可以参考&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/03/05/transaction-lock-isolation-level.html"&gt;事物、锁、隔离等级&lt;/a&gt;&lt;br /&gt;&#xD;
在数据库理论中检验事物正确性以完全串行化执行结果为基准，考虑并发处理后采用两阶段封锁协议(two-phase locking protocol, 2PL)，访问资源（读、写）时先申请锁&lt;br /&gt;&#xD;
考虑并发性能问题，锁分成不同的粒度，典型的为表级锁、页级锁、行级锁&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;为什么需要意向锁？&lt;/strong&gt;&lt;br /&gt;&#xD;
因为有不同粒度的锁存在，为了优化锁冲突的判断处理而设计出意向锁&lt;br /&gt;&#xD;
比如事物t1更新了某条记录，对这条记录加了写锁。而事物t2需要读取这个表的所有记录，需要对表加读锁。事物控制器如何确定这2个事物的加锁请求是否冲突？&lt;br /&gt;&#xD;
有了意向锁之后，事物t1在更新记录时，对这个表加意向排他锁IX（如果有页锁，则对数据记录所在页也加IX锁）；事物t2申请这个表上的共享锁S时，就只需要检测表（以及页）上面已存在的锁是否与S锁冲突，而IX与S是冲突的，所以t2只有等待。这样避免了锁冲突判断时需要理解并处理加锁对象的类型以及之间的关系&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;意向锁类型&lt;/strong&gt;&lt;br /&gt;&#xD;
IS, Intention Shared, 意向共享锁：对行取读锁S时，则必须获取表的IS锁&lt;br /&gt;&#xD;
IX, Intention Exclusive, 意向排他锁：要更新某一行，则必须先获取该表的IX锁&lt;br /&gt;&#xD;
SIX, Shared Intention Exclusive, 共享意向排他锁：例如要更新某些行，但必须执行表扫描才能确定具体需要更新的行，则加SIX锁，它是共享锁与意向排他锁的结合&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;先写式日志 Write-ahead log&lt;/strong&gt;&lt;br /&gt;&#xD;
更新记录 update record：用于确保事物的原子性、持久性机制，事物终止（rollback）时可以根据更新记录回滚。更新日志中包含before image，即记录被更新之前的状态，因此可以用它来撤销更改。因此更新记录也被称为undo record&lt;br /&gt;&#xD;
日志示意图：&lt;br /&gt;&#xD;
&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/Database/dis/database-log-example.JPG" height="53" width="556" /&gt;&lt;br /&gt;&#xD;
数据库日志示意图 Ui：事物Ti的更新记录 update record&lt;br /&gt;&#xD;
Bi：事物Ti的开始记录 begin record&lt;br /&gt;&#xD;
Ci：事物Ti的开始记录 commit record&lt;br /&gt;&#xD;
Ai：事物Ti的开始记录 abort record&lt;br /&gt;&#xD;
CK：Check Point&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 1. 终止事物时，在日志中反向扫描处理到该事物的begin record就完成了&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; 2. 系统崩溃时需要将当时所有活动事物全部回滚（系统崩溃时内存信息已经丢失，只能根据日志操作），所以用commit record、abort record标识那些已经结束了的事物&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 另外，除非反向扫描完整个日志文件，否则无法确定崩溃时哪些事物为活动事物，因此在日志中定期记录check point record，用来记录检查点时刻系统中所有活动的事物id。这样崩溃恢复时只需要反向扫描到任何一个check point record就有方法确定扫描的终止条件&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
更新数据时，先更新数据文件还是先写日志？&lt;br /&gt;&#xD;
如果先更新数据文件，在日志文件还没有写入时发生崩溃，则这个数据可能无法恢复；如果先写日志，在数据未更新时发生崩溃，则恢复处理不会造成数据错误。因此叫做write-ahead log&lt;br /&gt;&#xD;
除了before image外，日志可能还包括after image，即记录更新之后的状态，用于重做事物，因此也叫redo record。例如某时刻数据库损坏，则可以用之前的某个备份加上redo log恢复到崩溃之前的某个状态，将损失降到最低&lt;img src="http://www.cnblogs.com/RicCC/aggbug/1827433.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/09/15/Database-System-An-Application-Oriented-Approach.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/RicCC/archive/2010/09/13/Data-Modeling-Technologies.html</id><title type="text">Data Modeling Technologies - ER, IE, Barker, IDEF1X, EXPRES-G, ORM</title><summary type="text">各种数据建模技术，包括ER, IE, Barker, IDEF1X, EXPRES-G, ORM</summary><published>2010-09-13T02:10:00Z</published><updated>2010-09-13T02:10:00Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><link rel="alternate" href="http://www.cnblogs.com/RicCC/archive/2010/09/13/Data-Modeling-Technologies.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/archive/2010/09/13/Data-Modeling-Technologies.html"/><content type="html">&lt;strong style="font-size: 18pt;"&gt;Entity Relationship Model - ER模型 - 实体关系模型&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;1976年Peter Chen首次提出了Entity Relationship Modeling（实体关系建模）概念，并发明了陈氏表示法Peter Chen's Notation，因此ER模型也可以叫做Chen's Model（陈氏模型）。下面是一个ER模型（ERD - ER diagram - Entity Relationship diagram）示例：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ERD-ER-Model.jpg" height="360" width="596" /&gt;&lt;br /&gt;图：ER模型 - Peter Chen's Notation图：ER模型 - Peter Chen's Model，实际上这是一个EER - Enhanced Entity-Relationship Model，扩展ER模型&lt;br /&gt;&lt;strong&gt;Entity 实体&lt;/strong&gt;：使用方框表示&lt;br /&gt;&lt;strong&gt;Attribute 属性&lt;/strong&gt;：使用圆或椭圆表示。实体和实体关系都可以拥有属性，例如图中的Order-Line关系拥有3个属性&lt;br /&gt;&lt;strong&gt;&lt;br /&gt;Relationship 关联关系&lt;/strong&gt;：使用菱形表示，菱形中写上关联关系的名字&lt;br /&gt;ER模型中关联关系也可以拥有属性，在多对多关联关系中不需要使用额外中间关联实体来表示，关联关系本身就可以作为这个中间实体。另外ER模型的关联关系不限于2个实体之间，可以在多个实体间使用一个关联关系&lt;br /&gt;&lt;strong&gt;&lt;br /&gt;Unique Identifier 唯一标识&lt;/strong&gt;&lt;br /&gt;陈氏表示法没有很好的解决唯一标识问题，仅使用一种简单的标记方法，下图表示Party的ID作为Purchase Order唯一标识一员的情况，关联关系名称改为E，朝依赖实体方使用一个箭头，依赖实体使用一个额外的方框括起来&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ERD-peter-chen-unique-id.jpg" height="40" width="345" /&gt;&lt;br /&gt;图：ER模型中唯一标识的表示方法图：ER模型中唯一标识的表示方法&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Sub-type 子类型&lt;/strong&gt;&lt;br /&gt;最初的陈氏表示法中不包含子类型概念，后来Robert Brown和Mat Flavin添加了子类型表示法，这种ER模型称为扩展ER模型。上图ER模型中有一个子类型例子，超类Party派生出子类Organization和 Person&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Constraints between relationships 关联关系的约束&lt;/strong&gt;&lt;br /&gt;最初的陈氏表示法中关联关系的每一端只使用一个数字表示，比如一对多的关联关系，在一端使用1，另一端实体上使用n表示，这对关联关系约束不充分。上图的 ER模型在关联关系每一端使用2个数字表示，这与Crow's Foot表示法以及UML中的optionality（可选项）、cardinality（关联基数）有些类似又有较大的区别，详细说明如下：&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #808080;"&gt;上图中一个Purchase Order必须关联一个Party，必须关联一个或多个Order Line，每个Order Line要么是一个Product要么是一个Service&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Purchase Order右边的(1,)1表示一个Purchase Order有且必须有一个Party&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Party左边的(0,)n表示一个Party可以拥有多个Purchase Order，也可以没有&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Purchase Order左边的(1,)n表示Purchase Order必须有一个或多个Order Line&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Order Line右边的(1,)1表示每个Order Lien必须属于一个Purchase Order&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Order Line下面的(1,)1和菱形符号一起表示每个Order Line要么是一个Product要么是一个Service&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Product和Service上面的(0,)n表示Product和Service可以属于0个或多个Order Line&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Event和Event Category的关联关系稍特殊（实际中这种 n:1 的关系不多见），他不是一个普通的多对一关系。Event可以关联一个Event Category也可以不关联，而Event Category则必须关联一个或多个Event&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Order-Line和Product、Service之间是一个exclusive or约束（异或、互斥约束，disjunctive mandatory约束）关系，使用超类和子类一样的表示法来表示。因为exclusive or的语义本身已经已经表明Order Line必须是Product或者Service其中之一，因此上图中Order Line右边和下面的(1,)1是多余的&lt;br /&gt;&lt;br /&gt;&lt;strong style="font-size: 18pt;"&gt;EER - Enhanced Entity-Relationship Model 扩展ER模型&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;上面已经有些内容不属于最初的陈氏表示法，是后来其他人扩展的。陈氏表示法出现的早也存在一些不足，因此存在一些扩展以弥补缺陷，不同文档中使用的ER图也不尽相同，例如下图是wiki上的一个示例ER模型&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ERD-ER-in-wiki.jpg" height="579" width="650" /&gt;&lt;br /&gt;图：Wiki上的ER模型示例图：Wiki上的ER模型示例&lt;br /&gt;属性带下划线表示主键属性；关联关系连接线为两条线的表示"最少一个，或多个"（用于n的一端）；实体和关联关系使用两个框的，可能是上面讲到的唯一标识表示法，也可能是"最少一个，或多个"表示法的一部分（这一点有待确认）。还有一些，例如属性与实体间用两条线连接的表示该属性为多值属性（上图中Region实体的Foliage属性）。图中的部分解释如下：&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color: #808080;"&gt; 1 Account has n(at least one) Character, Account的AcctName属性将成为Has的唯一标识的一员;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808080;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1 Region contains n(at least one) Character, Region的RegionName属性将成为Character的唯一标识的一员;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong style="font-size: 18pt;"&gt;Information Engineering - IE模型&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;Information Engineering采用Crow's Foot表示法（也有叫做James Martin表示法的），中文翻译中对使用了Crow's Foot表示法的模型也有笼统的称做鸭掌模型的（关联关系的关联基数中采用到了一个鸭掌形的三叉线来表示）。他由Clive Finkelstein发明，与James Martin一起推广，后来两人各自做了些修正形成两份版本&lt;br /&gt;前面示例模型的Information Engineering表示如下：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ERD-IE-Model.jpg" height="134" width="536" /&gt;&lt;br /&gt;图：Information Engineering - IE模型 - Crow's Foot Model - 鸭掌模型图：Information Engineering - IE模型&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #808080;"&gt;注意IE模型与ER模型的区别。Purchase Order与Party是多对一 n:1 的关联关系，在ER模型中n被放置在了Party的左边，而IE模型中n被放置在了Purchase Order的右边。两种表示法的形式（相当于语法）不一样，但语义是一致的。这一点也只有ER模型是特殊的，其他模型表示法中都与IE 模型一致&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Entity &amp;amp; Attribute&lt;/strong&gt;：实体属性并不出现在IE模型中，而是单独使用另外的文档记录&lt;br /&gt;&lt;strong&gt;Relationship&lt;/strong&gt;&lt;br /&gt;Crow's Foot的可选项optionality和关联基数cardinality 表示法：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ERD-crows-foot-notation.jpg" height="93" width="484" /&gt;&lt;br /&gt;图：Crow's Foot的可选项optionality和关联基数cardinality 表示法图：Crow's Foot的可选项optionality和关联基数cardinality 表示法&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Optionality 可选项&lt;/strong&gt;：用来表示该关联关系是可选的，还是必须的。对于可选的关联关系，通常表现为用于关联的外键字段允许为null值，或者对于使用中间关联关系表的情况下可以不出现关联数据，而必须的关联关系则不允许外键为null或者必须存在关联数据&lt;br /&gt;&lt;strong&gt;Cardinality 关联基数&lt;/strong&gt;：用来表示关联实体的数量上限，为1、n等&lt;br /&gt;图中右边部分表示的意义如下：1个A必须关联到1个或多个B，一个B可以关联0个或1个A&lt;br /&gt;&lt;br /&gt;关联的约束如图所示，Product和Service通过一个圆连接到Order Line。如果是实心圆则表示Product和Service是exclusive or；如果是空心圆则表示Product和Service是inclusive or（相容的，conjunctive），表示可以是其中之一或者多个&lt;br /&gt;在上面IE模型图中，Order Line右侧是Finkelstein的一个特殊符号，表示一个Purchase Order初始时有0或n个Order Line，但最终必须有1或n个Order Line&lt;br /&gt;Martin以动词命名关联关系，只命名一个方向（遵循从左到右、从上往下的方式），而Finkelstein不对关联关系命名&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Sub-type&lt;/strong&gt;：图中Party子类的表示方法由Martin采用，Finkelstein则对每个子类使用单独的实体，使用ISA关联关系（关联关系名称为ISA，也有采用类似UML继承的三角形符合，在关联线上使用一个三角形的）表示其为子类&lt;br /&gt;&lt;br /&gt;&lt;strong style="font-size: 18pt;"&gt;Richard Barker's Notation&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;这个表示法最早是英国咨询公司CACI发明，经过了Richard Barker的推广，后来Richard Barker去了Oracle，开发了相关的建模工具，因此也叫做Oracle表示法（Oracle's Notation）&lt;br /&gt;示例模型的Barker表示法如下：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ERD-Barker-Model.jpg" height="250" width="556" /&gt;&lt;br /&gt;Richard Barker's Model图：Richard Barker模型&lt;br /&gt;&lt;br /&gt;Entity &amp;amp; Attribute：实体试用圆角的方框表示，属性出现在实体框中。可选属性（允许null）前面带一个空心圆，必须的属性（不允许null）前面带一个实心圆，唯一标识属性前面带一个#符号（因为制图工具原因，有时可选属性前面不使用任何符号，必须属性前使用一个点）&lt;br /&gt;&lt;br /&gt;Relationship：&lt;br /&gt;Barker表示法中可选项通过半边连接线的虚实线表示，表示法如下图所示。上面的模型中Purchase Order必须关联到一个Party，所以关联线在Party一侧的那一半是实线表示；而Party可以关联到0或多个Purchase Order，所以关联线在Purchase Order一侧的那一半是虚线表示&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ERD-Barker-notation.jpg" height="134" width="219" /&gt;&lt;br /&gt;图：Richard Barker's Notation图：Richard Barker's Notation&lt;br /&gt;&lt;br /&gt;关联基数为n时采用一个三叉线，线条末端没有符号时表示关联基数为1&lt;br /&gt;另外Barker表示法中有一个表示aggregation、composition的特殊符号，例如示例模型中的Purchase Order和Order Line的关系，在Order Line右侧的三叉线边上添加一个竖线，注意与Crow's Foot表示法之间的区别&lt;br /&gt;Sub-type：Barker表示法中子类型显示在父类型的实体框中&lt;br /&gt;Constraint：Barker表示法仅支持exclusive or约束，如示例模型中所示，用一条弧线划过2个关联关系&lt;br /&gt;&lt;br /&gt;&lt;strong style="font-size: 18pt;"&gt;IDEF1X模型&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;IDEF1X是美国联邦政府广泛使用的一种模型，前面示例模型的IDEF1X等效模型如下：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ERD-IDEF1X-Model.jpg" height="364" width="641" /&gt;&lt;br /&gt;图：IDEF1X模型图：IDEF1X模型&lt;br /&gt;&lt;br /&gt;Entity &amp;amp; Attribute：实体使用方框和圆角框表示，独立实体（Independent entities，主键不包含其他实体主键值）使用方框，非独立实体（dependent entities，主键包含其他实体主键值）使用圆角框。属性出现在实体框中，主键用线隔开&lt;br /&gt;&lt;br /&gt;Relationship：&lt;br /&gt;外键不是使用关联线表示，必须在实体属性中明确的标注外键属性&lt;br /&gt;如果关联关系一方的唯一标识将作为另一方唯一标识的一部分（即identifying relationship），关联线使用实线，否则（即non-identifying relationship）使用虚线&lt;br /&gt;&lt;br /&gt;不同于IE模型，IDEF1X中可选项和关联基数是分开表示的，关联线的一端表示关联基数，另一端表示可选项&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ERD-IDEF1X-notation.jpg" height="130" width="401" /&gt;&lt;br /&gt;图：IDEF1X Cardinality 关联基数图示图：IDEF1X Cardinality 关联基数图示&lt;br /&gt;如IDEF1X示例模型中，关联基数的图示都是出现在关联关系的左端或者上面，可选项出现在右端或者下面。对于可选的关联关系（即类似外键允许为null 值的情况），在可选项一端使用一个菱形，例如示例模型中Event Category左边的菱形；对于必须的关联关系（即外键必须为有效的实体标识值的情况），则在可选项一端直接将关联线与实体连接。对于多对多的情况，两端均使用关联基数符号，可选项问题在模型中通过其他文档标注&lt;br /&gt;&lt;br /&gt;关联关系的名称有几种表示方法。默认情况下遵循从左至右从上至下的顺序，关联关系从左至右的名称放在关联线上方，从右至左的名称放在关联线下方。也可以使用一个反斜杠将两个方向的名称分开，从左至右从上至下的在反斜杠前面，否则在后面&lt;br /&gt;&lt;br /&gt;Sub-type &amp;amp; Constraint&lt;br /&gt;IDEF1X中的子类和关联约束分别如示例模型中所示。另外一点，示例模型中的子类和关联约束图例中，小圆圈下面都是使用2条横线，这表示模型中已经列举了所有的子类和约束情况，如果模型只是部分列举子类和约束情况，则使用1条横线&lt;br /&gt;Domain：IDEF1X中定义了domain，domain即数据类型的定义，比如数据类型、取值范围等各种需要运用到属性值上的约束&lt;br /&gt;详尽的IDEF1X规范参考IDEF1X标准：&lt;a target="_blank" href="http://www.itl.nist.gov/fipspubs/idef1x.doc"&gt;Integration Definition for Information Modeling&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong style="font-size: 18pt;"&gt;EXPRESS-G表示法&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;EXPRESS-G是一个ISO标准ISO 10303-11。示例模型的EXPRESS-G表示法如下（省略了Event、Event Category部分）：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ERD-EXPRESS-G-Model.jpg" height="243" width="653" /&gt;&lt;br /&gt;图：EXPRESS-G表示法图：EXPRESS-G表示法&lt;br /&gt;&lt;br /&gt;Entity &amp;amp; Attribute：&lt;br /&gt;实体使用方框表示，实体名称出现在方框中&lt;br /&gt;属性通过空心圆结束的线条连接到属性值类型，属性名称出现在线条上。可选属性使用虚线条连接，必须属性使用实现连接&lt;br /&gt;属性值类型使用右边多一条竖线的方框表示，ISO规范中确定的简单数据类型（String, Binary, Logical, Boolean, Number, Integer, Real等）均使用上图中所示的实体框表示。扩展或者自定义的数据类型使用虚线框表示，例如上图示例中order_date属性的DATE类型。枚举类型的表示方法如下图：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ERD-EXPRESS-G-enum.jpg" height="40" width="253" /&gt;&lt;br /&gt;图：EXPRESS-G的枚举表示方法图：EXPRESS-G的枚举表示方法&lt;br /&gt;&lt;br /&gt;Relationship：&lt;br /&gt;使用空心圆结束的线条连接关联实体&lt;br /&gt;关联关系的名称出现在关联线上，朝空心圆一端的方向为正向，相反则为反向，反向的关联关系名称前面使用(INV)表示&lt;br /&gt;关联基数紧随关联名称之后，第一个字符可以是S、B、L、A，分别表示Set、Bag、List、Array，后面中括号的内容即为关联基数，问号表示多个。默认情况下（没有明确标注）关联基数都为[1:1]，因此上图中出现[1:1]的地方都可以省略&lt;br /&gt;&lt;br /&gt;前面提到过的exclusive or约束，EXPRESS-G中使用Select（可选类型）表示，如上图中的order_line_item&lt;br /&gt;&lt;br /&gt;Sub-type：如上图中Party、Person、Organization所示，连接线使用粗线条&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;ORM - Object-Role Modeling&lt;/strong&gt;&lt;br /&gt;下面是ORM2的部分表示法&lt;br /&gt;&lt;strong&gt;基本元素&lt;/strong&gt;&lt;br /&gt;下面是ORM模型几个基本元素图示：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ORM-notation-basis.jpg" height="277" width="666" /&gt;&lt;br /&gt;图：ORM模型基本元素图：ORM模型基本元素&lt;br /&gt;&lt;br /&gt;唯一性约束表示法&lt;br /&gt;ORM中唯一性约束是在相应角色上使用带箭头的线条表示，线条在哪些角色上，则这些角色的组合必须唯一，例如下图：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ORM-UC-internal.jpg" height="89" width="322" /&gt;&lt;br /&gt;图：ORM模型唯一性约束表示法图：ORM模型唯一性约束表示法&lt;br /&gt;图中Person is of Gender的关系中，Person必须唯一，因此这是一个n:1的关系。同样Person was born in Country是1:n，Person speaks Language是n:m，Person is president of Country是1:1&lt;br /&gt;&lt;br /&gt;下面表格是用于理解Person is of Gender、Person was born in Country的数据示例：&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ORM-unique-data-case.JPG" height="87" width="221" /&gt;&lt;br /&gt;图：数据示例&lt;br /&gt;&lt;br /&gt;关联关系中必须的角色使用带圆点的线条表示，如上图中Person was born in Country&lt;br /&gt;上面示例中的唯一性约束都是位于一个关联关系（包括二元和多元关系）中，这种唯一约束叫做内部唯一约束Internal UC。多个关联关系组合起来形成的唯一约束称为外部唯一约束External UC，例如下图所示&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ORM-UC-external.JPG" height="121" width="199" /&gt;&lt;br /&gt;图：ORM模型External UC图：ORM模型External UC&lt;br /&gt;图中State具有2个外部唯一约束，一个是Country+StateCode，圆圈中使用2跟线条表示这个唯一约束作为State的主键；另一个是 Country+StateName&lt;br /&gt;&lt;br /&gt;Inclusive or ((disjunctive mandatory role)&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ORM-inclusive-or.JPG" height="97" width="206" /&gt;&lt;br /&gt;图：ORM Inclusive or表示每个Visitor必须有护照Passport或者驾驶执照DriverLicence，或者两样都有&lt;br /&gt;&lt;br /&gt;Exclusive&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ORM-exlusive.JPG" height="97" width="224" /&gt;&lt;br /&gt;图：ORM Exclusive约束表示Person不可能同时是married和widowed状态&lt;br /&gt;&lt;br /&gt;exclusive or则是inclusive or和exclusive的结合，即必须是其中之一&lt;br /&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/design/ER/ORM-exlusive-or.JPG" height="91" width="224" /&gt;&lt;br /&gt;图：ORM Exclusive or约束&lt;br /&gt;&lt;br /&gt;详细的ORM2图形表示法参考&lt;a target="_blank" href="http://www.orm.net/pdf/ORM2GraphicalNotation.pdf"&gt;ORM 2 Graphical Notation Summary&lt;/a&gt;&lt;br /&gt;使用ORM建模的过程示例参考&lt;a target="_blank" href="http://www.orm.net/pdf/ORMwhitePaper.pdf"&gt;Object Role Modeling: An Overview&lt;/a&gt;&lt;br /&gt;ORM2的完整介绍参考&lt;a target="_blank" href="http://www.orm.net/pdf/ORM2.pdf"&gt;ORM2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong style="font-size: 18pt;"&gt;参考&lt;/strong&gt;&lt;br style="font-size: 18pt;" /&gt;&lt;a target="_blank" href="http://en.wikipedia.org/wiki/Entity-relationship_model"&gt;Wiki - Entity-relationship model&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://en.wikipedia.org/wiki/IDEF1X"&gt;Wiki - IDEF1X&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://www.agiledata.org/essays/dataModeling101.html"&gt;Data Modeling 101&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://en.wikipedia.org/wiki/EXPRESS_%28data_modeling_language%29"&gt;Wiki - EXPRESS (data modeling language)&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://tc3.iec.ch/txt/xpress.pdf"&gt;Information modelling - Getting started with EXPRESS-G&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://en.wikipedia.org/wiki/Object_Role_Modeling"&gt;Wiki - Object-Role Modeling&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://www.orm.net/"&gt;Object Role Modeling&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://www.essentialstrategies.com/publications/modeling/compare.htm"&gt;A Comparison of Data Modeling Techniques&lt;/a&gt;&lt;img src="http://www.cnblogs.com/RicCC/aggbug/1824771.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/09/13/Data-Modeling-Technologies.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/RicCC/archive/2010/09/13/1824742.html</id><title type="text">计划清单</title><summary type="text">备案不是一般的麻烦，个人博客上线暂缓确定重点关注的领域为：数据库、编译原理、伸缩性架构，以及看完Windows Internals 5th Edition数据库方面到此为止了，接下来是Antlr计划清单：1. 数据库 对关系数据库理论以及事物并发控制和恢复理论进行了一定了解，数据库方面到此为止。以后如果再有计划应该考虑看看MySQL源码了。Finished: 2010-09-132. 编译原理 a). 研究NHibernate HQL解析机制 b). 看完《The Definitive Antlr Reference》 - Finished: 2010-12-13 c). 看完龙书的后面部分 </summary><published>2010-09-13T01:45:00Z</published><updated>2010-09-13T01:45:00Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><link rel="alternate" href="http://www.cnblogs.com/RicCC/archive/2010/09/13/1824742.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/archive/2010/09/13/1824742.html"/><content type="html">备案不是一般的麻烦，个人博客上线暂缓&lt;br /&gt;确定重点关注的领域为：数据库、编译原理、伸缩性架构，以及看完Windows Internals 5th Edition&lt;br /&gt;数据库方面到此为止了，接下来是Antlr&lt;br /&gt;&lt;br /&gt;计划清单：&lt;br /&gt;&lt;span style="color: #339966;"&gt;1. 数据库&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #339966;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;对关系数据库理论以及事物并发控制和恢复理论进行了一定了解，数据库方面到此为止。以后如果再有计划应该考虑看看MySQL源码了。Finished: 2010-09-13&lt;br /&gt;&lt;/span&gt;2. 编译原理&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #ff6600;"&gt;a). 研究NHibernate HQL解析机制&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: #339966; "&gt;b). 看完《The Definitive Antlr Reference》 - Finished: 2010-12-13&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; c). 看完&lt;/span&gt;龙书的后面部分&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; d). 研究一个完整的编译器，候选：Firefox的JS、SSCLI&lt;br /&gt;3. 伸缩性架构&lt;br /&gt;4. Windows Internals 5th Edition&lt;img src="http://www.cnblogs.com/RicCC/aggbug/1824742.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/09/13/1824742.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/RicCC/archive/2010/08/26/1808836.html</id><title type="text">声明：***本博客停止更新维护***</title><summary type="text">因为：1. 博客园内容的组织方式无法满足自己的要求；2. 系统速度经常很慢；3. 鱼龙混杂太吵太闹，新文章中有价值的越来越少，无谓的争吵讨论越来越多所以自己花时间写了个博客系统，以后本博客停止任何更新维护。本以为我的地盘我做主，可以更好的管理自己的内容，但内容的组织确实是个麻烦事，blog、 wiki、bbs、twiter等各种方式都无法达到理想的状况，只能说目前的web技术对内容组织呈现还是太过局限了。目前博客系统的主要功能已经好了，剩下的事情包括功能的完善、博客园文章内容的导入，以及个人网站的空间购买、备案等，估计2、3个月后会开通个人网站。</summary><published>2010-08-26T02:37:00Z</published><updated>2010-08-26T02:37:00Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><link rel="alternate" href="http://www.cnblogs.com/RicCC/archive/2010/08/26/1808836.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/archive/2010/08/26/1808836.html"/><content type="html">因为：&lt;br /&gt;&#xD;
1. 博客园内容的组织方式无法满足自己的要求；&lt;br /&gt;&#xD;
2. 系统速度经常很慢；&lt;br /&gt;&#xD;
3. 鱼龙混杂太吵太闹，新文章中有价值的越来越少，无谓的争吵讨论越来越多&lt;br /&gt;&#xD;
所以自己花时间写了个博客系统，以后本博客停止任何更新维护。本以为我的地盘我做主，可以更好的管理自己的内容，但内容的组织确实是个麻烦事，blog、wiki、bbs、twiter等各种方式都无法达到理想的状况，只能说目前的web技术对内容组织呈现还是太过局限了。&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
目前博客系统的主要功能已经好了，剩下的事情包括功能的完善、博客园文章内容的导入，以及个人网站的空间购买、备案等，估计2、3个月后会开通个人网站。&lt;br /&gt;&#xD;
系统预览：&lt;br /&gt;&#xD;
&lt;a href="http://images.cnblogs.com/cnblogs_com/riccc/1.JPG" target="_blank" title="点击查看大图"&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/1.JPG" width="700" /&gt;&lt;/a&gt;&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;a href="http://images.cnblogs.com/cnblogs_com/riccc/2.JPG" target="_blank" title="点击查看大图"&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/2.JPG" width="700" /&gt;&lt;/a&gt;&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;a href="http://images.cnblogs.com/cnblogs_com/riccc/3.JPG" target="_blank" title="点击查看大图"&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/3.JPG" width="700" /&gt;&lt;/a&gt;&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;a href="http://images.cnblogs.com/cnblogs_com/riccc/4.JPG" target="_blank" title="点击查看大图"&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/4.JPG" width="700" /&gt;&lt;/a&gt;&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;a href="http://images.cnblogs.com/cnblogs_com/riccc/5.JPG" target="_blank" title="点击查看大图"&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/riccc/5.JPG" width="700" /&gt;&lt;/a&gt;&lt;img src="http://www.cnblogs.com/RicCC/aggbug/1808836.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/08/26/1808836.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/RicCC/archive/2010/05/23/antlr-sql-parser.html</id><title type="text">ANTLR SQL解析器代码</title><summary type="text">博客里有一篇文章ANTLR实现的SQL解析器 - OQL，大概描述了一下用antlr实现的一个简单的sql解析器有不少人对antlr感兴趣，希望提供这个项目的源代码作为参考，一直没有放出来，原因有以下几个方面：1. antlr版本问题。开发时使用的antlr版本不久之后就更新了，与之前的老版本（包括项目中使用的）不兼容，项目中的语法文件已经无法在目前的antlr版本下使用这次上传的项目中包括当时使...</summary><published>2010-05-23T05:37:00Z</published><updated>2010-05-23T05:37:00Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><link rel="alternate" href="http://www.cnblogs.com/RicCC/archive/2010/05/23/antlr-sql-parser.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/archive/2010/05/23/antlr-sql-parser.html"/><content type="html">博客里有一篇文章&lt;a href="http://www.cnblogs.com/RicCC/archive/2008/03/09/OQL-ANTLR-SQL-Parser.html" target="_blank"&gt;ANTLR实现的SQL解析器 - OQL&lt;/a&gt;，大概描述了一下用&lt;a href="http://www.antlr.org/" target="_blank"&gt;antlr&lt;/a&gt;实现的一个简单的sql解析器&lt;br /&gt;&#xD;
有不少人对antlr感兴趣，希望提供这个项目的源代码作为参考，一直没有放出来，原因有以下几个方面：&lt;br /&gt;&lt;br /&gt;&#xD;
1. antlr版本问题。&lt;br /&gt;开发时使用的antlr版本不久之后就更新了，与之前的老版本（包括项目中使用的）不兼容，项目中的语法文件已经无法在目前的antlr版本下使用&lt;br /&gt;这次上传的项目中包括当时使用的antlr runtime dll文件（应该是3.0.1版本吧），代码中有生成好的lexer和parser cs文件。但如果要从.g语法文件重新生成lexer和parser，得使用相应的antlr java package，我这边已经没有了，可以看看&lt;a href="http://www.antlr.org/" target="_blank"&gt;antlr&lt;/a&gt;网站是否还提供下载&lt;br /&gt;&lt;br /&gt;&#xD;
2. 实现上很乱。&#xD;
&lt;br /&gt;这是创业的几年里为做项目而写的，这个东西的开发本身不在项目计划之内，因此从了解antlr到这部分代码只用了一个星期的时间赶出来，后来也只是稍微改改bug，没有结构性的调整过，因此实在不具备什么参考作用&lt;br /&gt;主要思路倒是非常简单，用antlr生成AST，然后用visitor模式遍历AST生成sql，visitor的接口也是为了便于实现ORM映射而设计的。但是因为时间仓促，对antlr也没有深入了解，代码实现和语法文件都只能用一个字形容：乱。原来打算好好研究一下antlr，把这个项目也整理一下，不过一直懒的动&lt;br /&gt;前2个星期看nhibernate的HQL解析，他的结构就好多了，建议真正想了解用antlr做sql解析器的，可以好好参考一下nhibernate或者hibernate&lt;br /&gt;&lt;br /&gt;因为经常有人发消息留言希望学习一下，请在这里下载：&lt;a href="http://files.cnblogs.com/RicCC/antlr-oql.zip" target="_blank"&gt;ANTLR SQL解析器代码&lt;/a&gt;&lt;br /&gt;说明：&lt;br /&gt;1. 输出类型设置成了windows application形式，里面有个form用来测试解析效果&lt;br /&gt;2. 只支持标准的select查询语句&lt;img src="http://www.cnblogs.com/RicCC/aggbug/1742025.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/05/23/antlr-sql-parser.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/RicCC/archive/2010/04/14/hibernate-shards-3-architecture.html</id><title type="text">Hibernate Shards 数据的水平、垂直切割（三）- Hibernate Shards结构</title><summary type="text">主要处理方式hibernate shards的主要工作方式如下图: 他在hibernate的基础上实现了一层数据切分的处理逻辑。不需要切分的数据直接使用hibernate的SessionFactory和Session进行操作；需要切分的数据，则使用hibernate shards的ShardedSessionFactory和ShardedSession进行操作hibernate shards的主要...</summary><published>2010-04-14T02:41:00Z</published><updated>2010-04-14T02:41:00Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><link rel="alternate" href="http://www.cnblogs.com/RicCC/archive/2010/04/14/hibernate-shards-3-architecture.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/archive/2010/04/14/hibernate-shards-3-architecture.html"/><content type="html">&lt;strong style="font-size: 18pt;"&gt;主要处理方式&lt;/strong&gt;&lt;br /&gt;&#xD;
hibernate shards的主要工作方式如下图:&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;img src="http://images.cnblogs.com/cnblogs_com/riccc/frameworks/hibernate-shards-architecture.jpg" alt="" /&gt;&#xD;
&lt;br /&gt;&#xD;
他在hibernate的基础上实现了一层数据切分的处理逻辑。不需要切分的数据直接使用hibernate的SessionFactory和Session进行操作；需要切分的数据，则使用hibernate shards的ShardedSessionFactory和ShardedSession进行操作&lt;br /&gt;&#xD;
hibernate shards的主要任务:&lt;br /&gt;&#xD;
1. 对shards配置的处理&lt;br /&gt;&#xD;
2. 提供一个可扩展的sharding机制，能方便的运用各种sharding策略以及resharding支持等要求&lt;br /&gt;&#xD;
3. 根据配置和sharding机制正确的存取数据&lt;br /&gt;&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;shards的配置主要包含每个shard对应的数据库连接信息，以及定义一个shard id&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;为了充分实现伸缩性以及简化业务逻辑的开发，sharding策略应当以实体id为基础，这样在类似hibernate Session这样的数据操作接口上实现&#xD;
sharding处理会比较容易，对业务逻辑代码的开发也能比较透明&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;上图中get, update, delete等操作都可以取到实体id，然后根据sharding策略的算法计算出shard id，从对应的数据库中执行这些操作。hibernate shards中与这一操作相关的接口是ShardResolutionStrategy&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;sharding环境下的save操作涉及到id的生成机制，以及将数据按照sharding策略insert到正确的shard中。hibernate shards中与这一操作相关的接口是ShardSelectionStrategy&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;query操作通过hql、Criteria等方式查询结果集，这些查询有可能需要在某个shard或某几shards中进行，也可能需要在全部的shards中进行，与具体业务相关。hibernate shards中与这一操作相关的接口是ShardAccessStrategy&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;在resharding的支持方面，hibernate shards实现了virtual shards&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;在resharding时主要任务有2个，其一是对sharding算法、配置的修改，其二是对已有数据的重分区处理。&#xD;
如果最开始进行sharding设计时考虑不够完整，resharding时可能会很麻烦。&#xD;
virtual shards方式在于在一开始就考虑到后续的resharding需求，估算出最大需要的shards数量，以估算出的这个数量配置虚拟shard，然后将虚拟shard映射到当前的实际shard中&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;比如对某个数据，目前使用2个shard就足够了，但考虑到后续的业务增长，设置100个虚拟shard，sharding算法均以这100个虚拟shard为基础，然后将虚拟shard 50个一组映射到当前使用的实际shard上。&#xD;
这样在后续需要resharding时，只需要对数据重分区以及修改虚拟shard和实际shard的映射配置就可以，风险比较小&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;Configuration的处理&lt;/strong&gt;&lt;br /&gt;&#xD;
configuration部分类图如下:&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;img src="http://images.cnblogs.com/cnblogs_com/riccc/frameworks/hibernate-shards-configuration.jpg" alt="" /&gt;&#xD;
&lt;br /&gt;&#xD;
从前面一篇&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/04/10/hibernate-shards-2-shards-demo.html " target="_blank"&gt;Hibernate Shards 数据的水平、垂直切割（二）- Hibernate Shards基本演示&lt;/a&gt;已经看到了&#xD;
加载配置文件以及创建ShardedSessionFactory的过程&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;ShardConfiguration&lt;/strong&gt;用来表示每个shard特有的配置信息，包括：&lt;br /&gt;&#xD;
hibernate.connection.url&lt;br /&gt;&#xD;
hibernate.connection.username&lt;br /&gt;&#xD;
hibernate.connection.password&lt;br /&gt;&#xD;
hibernate.session_factory_name&lt;br /&gt;&#xD;
hibernate.connection.datasource&lt;br /&gt;&#xD;
hibernate.cache.region_prefix&lt;br /&gt;&#xD;
hibernate.connection.shard_id&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong&gt;ShardedConfiguration&lt;/strong&gt;则持有所有shards对应的ShardConfiguration列表、一个作为原型的Configuration对象以及一个sharding策略的工厂对象ShardStrategyFactory&lt;br /&gt;&#xD;
构造ShardedConfiguration时，他从各个配置文件读取shard配置信息，得到virtual shards与实际shards列表以及之间的映射关系&lt;br /&gt;&#xD;
ShardedConfiguration还具有创建ShardedSessionFactory的职责。在创建ShardedSessionFactory时，hibernate &#xD;
shards循环为每个shard创建一个hibernate的SessionFactory对象，丢给ShardedSessionFactory构造函数&lt;br /&gt;&#xD;
每一个hibernate的SessionFactory对象都是通过原型Configuration来创建的，只是在循环中会将每个shard特有的配置信息（ShardConfiguration的属性）设置到原型Configuration中，再用它来创建SessionFactory对象，这是一种简单的处理方式，避免再去建立额外的配置文件&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;关键实现&lt;/strong&gt;&lt;br /&gt;&#xD;
hibernate shards的关键实现部分类图如下:&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;img src="http://images.cnblogs.com/cnblogs_com/riccc/frameworks/hibernate-shards-core.jpg" alt="" /&gt;&#xD;
&lt;br /&gt;&#xD;
处理上非常简单，hibernate shards重新实现了hibernate的Session、SessionFactory接口。在前面的configuration处理中，ShardedConfiguration在创建&#xD;
ShardedSessionFactory时将每个shard对应的hibernate SessionFactory列表传给了ShardedSessionFactory。ShardedSession的主要工作就是调用相关的sharding策略类确定目标shard，&#xD;
使用与他对应的SessionFactory创建hibernate的Session对象，执行操作。对于使用了sharding之后不支持的方法则抛出异常。当然其中会有像query这样特殊的操作（涉及到合并结果集、排序、distinct等一些较麻烦的处理），还要处理一些hibernate lazy&#xD;
loading、detached object等相关的问题，以及其他一些sharding中必须处理的问题，例如cross-shard等&lt;br /&gt;&#xD;
对于hibernate的Criteria、Query等对象，hibernate shards也都实现了Sharded***的版本，用于支持针对shard的query操作&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;Shards Strategy&lt;/strong&gt;&lt;br /&gt;&#xD;
shards策略的类图:&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;img src="http://images.cnblogs.com/cnblogs_com/riccc/frameworks/hibernate-shards-strategy.jpg" alt="" /&gt;&lt;br /&gt;&#xD;
&lt;strong&gt;ShardSelectionStrategy&lt;/strong&gt;: 为新增的实体选择shard id，用于save操作的场景，接口方法中传入要执行操作的实体对象&lt;br /&gt;&#xD;
&lt;strong&gt;ShardResolutionStrategy&lt;/strong&gt;: 根据实体类名和id计算出与其对应的shard id，一般用于get、load、update、delete等操的场景，接口方法中会传入实体类名和实体id&lt;br /&gt; &#xD;
&lt;strong&gt;ShardAccessStrategy&lt;/strong&gt;: 主要用于hql、Criteria等查询情况，因其设计机制以及需要处理的事情方面存在些共同性，因此所有的get方法最终也通过ShardAccessStrategy来加载数据。&#xD;
他只是循环为每个目标shard调用ShardOperation&amp;lt;T&amp;gt;的execute方法，把结果丢给ExitStrategy&amp;lt;T&amp;gt;处理。get操作时目标shard是一个明确的shard（通过ShardResolutionStrategy得到），而query操作时则是全部的shard列表（可以通过自定义的ShardAccessStrategy改变）&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;hibernate shards提供了2个ShardAccessStrategy的实现:&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;strong&gt;SequentialShardAccessStrategy&lt;/strong&gt;: 逐个shard顺序执行，由ExitStrategy&amp;lt;T&amp;gt;决定是否结束执行过程以及如何处理结果集&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;strong&gt;ParallelShardAccessStrategy&lt;/strong&gt;: 并行的在各个shard执行查询操作，由ExitStrategy&amp;lt;T&amp;gt;决定如何处理结果集&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;ShardAccessStrategy加载数据时用到了下面一些接口:&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;strong&gt;ShardOperation&amp;lt;T&amp;gt;&lt;/strong&gt;: 这个是在每个shard上真正执行get或者query动作的接口，get和query操作时通过匿名类实现这个接口&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;strong&gt;ExitStrategy&amp;lt;T&amp;gt;&lt;/strong&gt;: 这个接口的职责是解决如何处理结果集的问题，以及在SequentialShardAccessStrategy这样的场景下是否该结束对后续shard的执行&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;对每个shard执行完读取操作后调用该接口的方法addResult，把执行结果丢给他；整个执行结束后调用该接口的compileResults方法处理结果集，并返回给调用者&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;FirstNonNullResultExitStrategy&lt;/strong&gt;: 查询到对象即返回，用于get的情况&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;ConcatenateListsExitStrategy&lt;/strong&gt;: 语义上是合并结果集，返回给调用者，用于hql和Criteria查询。他使用ExitOperationsCollector来完成这一任务&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;strong&gt;ExitOperationsCollector&lt;/strong&gt;: 处理如何合并结果集的逻辑。目前对于hql查询均使用&lt;strong&gt;ExitOperationsQueryCollector&lt;/strong&gt;，他把从各个shard执行的结果集合并返回给调用者；对于criteria查询使用&lt;strong&gt;ExitOperationsCriteriaCollector&lt;/strong&gt;，他在处理结果集时使用另一接口ExitOperation的实现，来处理count、sum、distinct等操作。目前hibernate shards没有对hql进行解析，因此hql中的count、sum、distinct等这样的操作还无法支持&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;限制&lt;/strong&gt;&lt;br /&gt;&#xD;
1. 还有很多hibernate的API没有实现&lt;br /&gt;&#xD;
2. 不支持cross-shard的对象关系，比如A、B之间存在关联关系，而A、B位于不同的shard中。hibernate shards提供了CrossShardRelationshipDetectingInterceptor，以hibernate Interceptor的方式来检测cross-shard的对象关系问题，但这个拦截器有一定的开销（比如需要查映射的元数据、有可能过早的触发延迟加载行为等），可以用于测试环境来检测cross-shard关系的问题&lt;br /&gt;&#xD;
3. hibernate shards本身不支持分布式事务，若要使用分布式事务需要采用其他解决方案&lt;br /&gt;&#xD;
4. hql、criteria存在不少限制，相比于hql，criteria支持的特性更多一些&lt;br /&gt;&#xD;
5. Session或者SessionFactory上面有状态的拦截器，在shard环境下面会存在一些问题。拿session来说，在hibernate中拦截器是对单个session上的多次sql执行事件进行拦截，而在shard情况下hibernate shards的ShardedSession会对应每个shard建立一个session，这时拦截器就是跨多个session了，因此hibernate shards要求有状态的拦截器必须通过实现StatefulInterceptorFactory来提供新的实例。如果拦截器需要使用到目标shard的session，则必须实现hibernate shards的RequiresSession接口&lt;br /&gt;&#xD;
&lt;img src="http://www.cnblogs.com/RicCC/aggbug/1711633.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/04/14/hibernate-shards-3-architecture.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/RicCC/archive/2010/04/10/hibernate-shards-2-shards-demo.html</id><title type="text">Hibernate Shards 数据的水平、垂直切割（二）- Hibernate Shards基本演示</title><summary type="text">准备1. 以Hibernate Shards 数据的水平、垂直切割（一）- Hibernate测试环境的项目为基础2. Hibernate Shards使用了commons logging，下载个项目最新的release版本。这里用的版本为1.1.13. 在Hibernate Shards 数据的水平、垂直切割（一）- Hibernate测试环境中，我们在mysql中建立了一个hbshards数据...</summary><published>2010-04-10T15:53:00Z</published><updated>2010-04-10T15:53:00Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><link rel="alternate" href="http://www.cnblogs.com/RicCC/archive/2010/04/10/hibernate-shards-2-shards-demo.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/archive/2010/04/10/hibernate-shards-2-shards-demo.html"/><content type="html">&lt;strong style="font-size: 18pt;"&gt;准备&lt;/strong&gt;&lt;br /&gt;&#xD;
1. 以&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/04/09/hibernate-shards-1-hbtest.html" target="_blank"&gt;Hibernate Shards 数据的水平、垂直切割（一）- Hibernate测试环境&lt;/a&gt;的项目为基础&lt;br /&gt;&#xD;
2. Hibernate Shards使用了&lt;a href="http://commons.apache.org/logging/" target="_blank"&gt;commons logging&lt;/a&gt;，下载个项目最新的release版本。这里用的版本为1.1.1&lt;br /&gt;&#xD;
3. 在&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/04/09/hibernate-shards-1-hbtest.html" target="_blank"&gt;Hibernate Shards 数据的水平、垂直切割（一）- Hibernate测试环境&lt;/a&gt;中，我们在mysql中建立了一个hbshards数据库，为了测试shards再建立一个hbshards2的数据库，contact表的结构一样&lt;br /&gt;&#xD;
4. 将Hibernate Shards发布包中的hibernate-shards.jar放到lib目录&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;项目引用添加hibernate-shards.jar&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;将commons logging发布包中的commons-logging-1.1.1.jar放到lib目录&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CLASSPATH中添加hibernate-shards.jar、commons-logging-1.1.1.jar&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;Hibernate Shards测试项目&lt;/strong&gt;&lt;br /&gt;&#xD;
出于结构上的简单考虑，定义一个ShardableEntity的接口，让ContactEntity实现这个接口:&lt;br /&gt;&#xD;
&lt;pre &gt;public interface ShardableEntity {&#xD;
    public String getIdentifier();&#xD;
}&#xD;
public class ContactEntity implements ShardableEntity {&#xD;
    public String getIdentifier(){&#xD;
        return this._id;&#xD;
    }&#xD;
    //other code omitted is the same with previous post&#xD;
}&lt;/pre&gt;&#xD;
映射文件ContactEntity.hbm.xml与上一篇文章中的一样&lt;br /&gt;&#xD;
因为我们需要将contact的数据以切片的形式存入2个数据库中，因此需要配置这2个数据库的信息。Hibernate Shards通过定义2个hibernate的配置文件解决这个问题:&lt;br /&gt;&#xD;
shard0.hibernate.cfg.xml:&#xD;
&lt;pre &gt;&amp;lt;?xml version='1.0' encoding='utf-8'?&amp;gt;&#xD;
&amp;lt;!DOCTYPE hibernate-configuration PUBLIC &#xD;
    "-//Hibernate/Hibernate Configuration DTD//EN" &#xD;
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"&amp;gt;&#xD;
&amp;lt;hibernate-configuration&amp;gt;&#xD;
&amp;lt;session-factory&amp;gt;&#xD;
      &amp;lt;property name="connection.driver_class"&amp;gt;com.mysql.jdbc.Driver&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.url"&amp;gt;jdbc:mysql://localhost/hbshards&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.username"&amp;gt;root&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.password"&amp;gt;dev&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.pool_size"&amp;gt;10&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="show_sql"&amp;gt;true&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="dialect"&amp;gt;org.hibernate.dialect.MySQLInnoDBDialect&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="hbm2ddl.auto"&amp;gt;validate&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="hibernate.connection.shard_id"&amp;gt;0&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="hibernate.shard.enable_cross_shard_relationship_checks"&amp;gt;false&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;mapping resource="ContactEntity.hbm.xml" /&amp;gt;&#xD;
&amp;lt;/session-factory&amp;gt;&#xD;
&amp;lt;/hibernate-configuration&amp;gt;&lt;/pre&gt;&#xD;
shard1.hibernate.cfg.xml:&#xD;
&lt;pre &gt;&amp;lt;?xml version='1.0' encoding='utf-8'?&amp;gt;&#xD;
&amp;lt;!DOCTYPE hibernate-configuration PUBLIC &#xD;
    "-//Hibernate/Hibernate Configuration DTD//EN" &#xD;
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"&amp;gt;&#xD;
&amp;lt;hibernate-configuration&amp;gt;&#xD;
&amp;lt;session-factory&amp;gt;&#xD;
      &amp;lt;property name="connection.driver_class"&amp;gt;com.mysql.jdbc.Driver&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.url"&amp;gt;jdbc:mysql://localhost/hbshards2&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.username"&amp;gt;root&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.password"&amp;gt;dev&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="dialect"&amp;gt;org.hibernate.dialect.MySQLInnoDBDialect&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="hibernate.connection.shard_id"&amp;gt;1&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="hibernate.shard.enable_cross_shard_relationship_checks"&amp;gt;false&amp;lt;/property&amp;gt;&#xD;
&amp;lt;/session-factory&amp;gt;&#xD;
&amp;lt;/hibernate-configuration&amp;gt;&lt;/pre&gt;&#xD;
其中shard0.hibernate.cfg.xml作为主要的配置文件，针对每个shard创建的SessionFactory对象，除了数据库连接信息的配置之外，都来自shard0.hibernate.cfg.xml&lt;br /&gt;&#xD;
针对其他每个shard创建的SessionFactory，数据库连接信息从相应的配置文件（例如shard1.hibernate.cfg.xml）中读取，读取的内容包括connection.url、connection.username、connection.password、connection.datasource。为了hibernate加载其他shard的配置文件时不抛异常，在其他shard的配置文件中我们还是把必要的配置属性写上去了&lt;br /&gt;&#xD;
hibernate.connection.shard_id属性为每个shard定义一个id，必须有一个shard_id为0的shard，其他的shard_id可以定义为任意整数&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
我们假定contact的id都是由数字组成的一个8位长度的字符串&lt;br /&gt;&#xD;
对contact的切割策略简单定义为: contact id第一个字符为0-4的存入shard0中，为5-9的存入shard1中&lt;br /&gt;&#xD;
为了实现这一策略，我们需要实现hibernate shards的2个接口&lt;br /&gt;&#xD;
ShardResolutionStrategy接口的实现:&#xD;
&lt;pre &gt;import java.util.List;&#xD;
import java.util.ArrayList;&#xD;
import org.hibernate.shards.strategy.resolution.ShardResolutionStrategy;&#xD;
import org.hibernate.shards.strategy.selection.ShardResolutionStrategyData;&#xD;
import org.hibernate.shards.ShardId;&#xD;
/*&#xD;
 * a simple ShardResolutionStrategy implementation for our ContactEntity&#xD;
 */&#xD;
public class MyShardResolutionStrategy implements ShardResolutionStrategy {&#xD;
    private List&amp;lt;ShardId&amp;gt; _shardIds;&#xD;
    public MyShardResolutionStrategy(List&amp;lt;ShardId&amp;gt; shardIds){&#xD;
        this._shardIds = shardIds;&#xD;
    }&#xD;
    public List selectShardIdsFromShardResolutionStrategyData(&#xD;
            ShardResolutionStrategyData arg0){&#xD;
        List ids = new ArrayList();&#xD;
        String id = (String)arg0.getId();&#xD;
        if(id==null || id.isEmpty()) ids.add(this._shardIds.get(0));&#xD;
        else{&#xD;
            //our shard selection is identified by the &#xD;
            //first char(number) in contact id&#xD;
            //0-4 =&amp;gt; shards0, 5-9 =&amp;gt; shards1&#xD;
            Integer i = new Integer(id.substring(0, 1));&#xD;
            ids.add(this._shardIds.get(i/5));&#xD;
        }&#xD;
        return ids;&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
ShardSelectionStrategy接口的实现:&#xD;
&lt;pre &gt;import java.util.List;&#xD;
import org.hibernate.shards.ShardId;&#xD;
import org.hibernate.shards.strategy.selection.ShardSelectionStrategy;&#xD;
/*&#xD;
 * a simple ShardSelectionStrategy implementation for our ContactEntity&#xD;
 */&#xD;
public class MyShardSelectionStrategy implements ShardSelectionStrategy {&#xD;
       private List&amp;lt;ShardId&amp;gt; _shardIds;&#xD;
       public MyShardSelectionStrategy(List&amp;lt;ShardId&amp;gt; shardIds){&#xD;
           this._shardIds=shardIds;&#xD;
       }&#xD;
       public ShardId selectShardIdForNewObject(Object obj) {&#xD;
           if(obj instanceof ShardableEntity) {&#xD;
               String id = ((ShardableEntity)obj).getIdentifier();&#xD;
               if(id==null || id.isEmpty()) return this._shardIds.get(0);&#xD;
               Integer i = new Integer(id.substring(0, 1));&#xD;
               //our shard selection is identified by the &#xD;
               //first char(number) in contact id&#xD;
               //0-4 =&amp;gt; shards0, 5-9 =&amp;gt; shards1&#xD;
               return this._shardIds.get(i/5);&#xD;
           }&#xD;
           //for non-shardable entities we just use shard0&#xD;
           return this._shardIds.get(0);&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
接下来就是怎么使用hibernate shards的测试代码了:&#xD;
&lt;pre &gt;import java.util.Iterator;&#xD;
import java.util.List;&#xD;
import java.util.ArrayList;&#xD;
import org.hibernate.Session;&#xD;
import org.hibernate.SessionFactory;&#xD;
import org.hibernate.Transaction;&#xD;
import org.hibernate.cfg.Configuration;&#xD;
import org.hibernate.shards.*;&#xD;
import org.hibernate.shards.cfg.*;&#xD;
import org.hibernate.shards.strategy.*;&#xD;
import org.hibernate.shards.strategy.access.*;&#xD;
import org.hibernate.shards.strategy.resolution.*;&#xD;
import org.hibernate.shards.strategy.selection.*;&#xD;
&lt;br /&gt;&#xD;
public class Main {&#xD;
    public static void main(String[] args) {&#xD;
        HibernateShardsTest(args);&#xD;
    }&#xD;
    private static SessionFactory createSessionFactory() {&#xD;
        //加载主配置文件，为每个shard创建SessionFactory对象时将&#xD;
        //以他作为原型&#xD;
        Configuration prototypeCfg = new Configuration()&#xD;
            .configure("shard0.hibernate.cfg.xml");&#xD;
        //每个shard的配置文件&#xD;
        List&amp;lt;ShardConfiguration&amp;gt; shardCfgs = new ArrayList&amp;lt;ShardConfiguration&amp;gt;();&#xD;
        shardCfgs.add(buildShardConfig("shard0.hibernate.cfg.xml"));&#xD;
        shardCfgs.add(buildShardConfig("shard1.hibernate.cfg.xml"));&#xD;
        //数据切片策略的工厂对象&#xD;
        ShardStrategyFactory strategyFactory = buildShardStrategyFactory();&#xD;
        ShardedConfiguration shardedConfig = new ShardedConfiguration(&#xD;
            prototypeCfg, shardCfgs, strategyFactory);&#xD;
        //返回一个ShardedSessionFactory对象&#xD;
       return shardedConfig.buildShardedSessionFactory();&#xD;
   }&#xD;
    private static ShardStrategyFactory buildShardStrategyFactory() {&#xD;
       ShardStrategyFactory factory = new ShardStrategyFactory() {&#xD;
           //测试用的自定义数据切片策略的工厂类&#xD;
           public ShardStrategy newShardStrategy(List&amp;lt;ShardId&amp;gt; shardIds) {&#xD;
               ShardSelectionStrategy ss = new MyShardSelectionStrategy(shardIds);&#xD;
               ShardResolutionStrategy rs = new MyShardResolutionStrategy(shardIds);&#xD;
               ShardAccessStrategy as = new SequentialShardAccessStrategy();&#xD;
               return new ShardStrategyImpl(ss, rs, as);&#xD;
           }&#xD;
       };&#xD;
       return factory;&#xD;
    }&#xD;
    private static ShardConfiguration buildShardConfig(String configFile) {&#xD;
        Configuration config = new Configuration().configure(configFile);&#xD;
        return new ConfigurationToShardConfigurationAdapter(config);&#xD;
    }&#xD;
    private static void HibernateShardsTest(String[] args){&#xD;
        String loginId = "RicCC@cnblogs.com";&#xD;
        String password = "123";&#xD;
        if(args!=null &amp;&amp; args.length==2){&#xD;
            loginId = args[0];&#xD;
            password = args[1];&#xD;
        }&#xD;
        SessionFactory factory = null;&#xD;
        try{&#xD;
            factory = createSessionFactory();&#xD;
            ShardsTestCreate(factory);&#xD;
            ShardsTestLogin(factory, loginId, password);&#xD;
            ShardsTestDelete(factory);&#xD;
        }catch(Exception e){&#xD;
            System.out.println(e.getMessage());&#xD;
            e.printStackTrace();&#xD;
        }finally{&#xD;
            if(factory!=null) factory.close();&#xD;
        }&#xD;
    }&#xD;
    private static void ShardsTestCreate(SessionFactory factory){&#xD;
        Session session = null;&#xD;
        Transaction transaction = null;&#xD;
        System.out.println("===Create Contacts===");&#xD;
        try{&#xD;
            session = factory.openSession();&#xD;
            transaction = session.beginTransaction();            &#xD;
            session.save(new ContactEntity("01111111","RicCC@cnblogs.com"&#xD;
                    , "123", "Richie", "RicCC@cnblogs.com"));&#xD;
            session.save(new ContactEntity("91111111","a@cnblogs.com"&#xD;
                    , "123", "AAA", "a@cnblogs.com"));&#xD;
            session.save(new ContactEntity("81111111","b@cnblogs.com"&#xD;
                    , "123", "BBB", "b@cnblogs.com"));&#xD;
            session.save(new ContactEntity("31111111","c@cnblogs.com"&#xD;
                    , "123", "CCC", "c@cnblogs.com"));&#xD;
            transaction.commit();&#xD;
        }catch(Exception e){&#xD;
            if(transaction!=null) transaction.rollback();&#xD;
            System.out.println(e.getMessage());&#xD;
            e.printStackTrace();&#xD;
        }finally{&#xD;
            if(session!=null) session.close();&#xD;
        }&#xD;
    }&#xD;
    private static void ShardsTestLogin(SessionFactory factory&#xD;
            , String loginId, String password){&#xD;
        Session session = null;&#xD;
        ContactEntity c = null;&#xD;
        System.out.println("\n===Login Test===");&#xD;
        try{&#xD;
            session = factory.openSession();&#xD;
            List contacts = session.createQuery("from ContactEntity where LoginId=:loginId")&#xD;
                .setString("loginId", loginId)&#xD;
                .list();&#xD;
            if(contacts.isEmpty())&#xD;
                System.out.println("Contact \"" + loginId + "\" not found!");&#xD;
            else{&#xD;
                c = (ContactEntity)contacts.get(0);&#xD;
                if(c.getPassword().equals(password))&#xD;
                    System.out.println("Contact \"" + loginId + "\" login successful");&#xD;
                else &#xD;
                    System.out.println("Password is incorrect (should be: "&#xD;
                        + c.getPassword() + ", but is: " + password + ")");&#xD;
            }&#xD;
            System.out.println("\n===Get Contact by Id===");&#xD;
            c = (ContactEntity)session.get(ContactEntity.class, "81111111");&#xD;
            System.out.println(c.toString());&#xD;
            c = (ContactEntity)session.get(ContactEntity.class, "31111111");&#xD;
            System.out.println(c.toString());&#xD;
        }catch(Exception e){&#xD;
            System.out.println(e.getMessage());&#xD;
            e.printStackTrace();&#xD;
        }finally{&#xD;
            if(session!=null) session.close();&#xD;
        }&#xD;
    }&#xD;
    private static void ShardsTestDelete(SessionFactory factory){&#xD;
        Session session = null;&#xD;
        Transaction transaction = null;&#xD;
        System.out.println("\n===Delete Contacts===");&#xD;
        try{&#xD;
            session = factory.openSession();&#xD;
            transaction = session.beginTransaction();&#xD;
            List contacts = session.createQuery("from ContactEntity").list();&#xD;
            Iterator it = contacts.iterator();&#xD;
            while(it.hasNext()){&#xD;
                session.delete(it.next());&#xD;
            }&#xD;
            transaction.commit();&#xD;
        }catch(Exception e){&#xD;
            if(transaction!=null) transaction.rollback();&#xD;
            System.out.println(e.getMessage());&#xD;
            e.printStackTrace();&#xD;
        }finally{&#xD;
            if(session!=null) session.close();&#xD;
        }&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
项目结构图:&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;img src="http://images.cnblogs.com/cnblogs_com/riccc/frameworks/hibernate-shards-test-shards-demo-1.JPG"  alt="" /&gt;&#xD;
&lt;br /&gt;&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;运行测试&lt;/strong&gt;&lt;br /&gt;&#xD;
用Eclipse编译class文件，将生成的所有class文件以及相关的配置、映射文件&#xD;
拷贝到lib目录中，运行Main.class结果如下:&lt;br /&gt;&#xD;
D:\Work\research\Java\Hibernate-Test\lib&gt;java Main&lt;br /&gt;&#xD;
===Create Contacts===&lt;br /&gt;&#xD;
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)&lt;br /&gt;&#xD;
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)&lt;br /&gt;&#xD;
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)&lt;br /&gt;&#xD;
Hibernate: insert into CONTACT (EMAIL, NAME, LOGIN_ID, PASSWORD, ID) values (?,?, ?, ?, ?)&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
===Login Test===&lt;br /&gt;&#xD;
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_ where contactent0_.LOGIN_ID=?&lt;br /&gt;&#xD;
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_ where contactent0_.LOGIN_ID=?&lt;br /&gt;&#xD;
Contact "RicCC@cnblogs.com" login successful&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
===Get Contact by Id===&lt;br /&gt;&#xD;
Hibernate: select contactent0_.ID as ID0_0_, contactent0_.EMAIL as EMAIL0_0_, contactent0_.NAME as NAME0_0_, contactent0_.LOGIN_ID as LOGIN4_0_0_, contactent0_.PASSWORD as PASSWORD0_0_ from CONTACT contactent0_ where contactent0_.ID=?&lt;br /&gt;&#xD;
{ Id="81111111", LoginId="b@cnblogs.com", Name="BBB", EMail="b@cnblogs.com" }&lt;br /&gt;&#xD;
Hibernate: select contactent0_.ID as ID0_0_, contactent0_.EMAIL as EMAIL0_0_, contactent0_.NAME as NAME0_0_, contactent0_.LOGIN_ID as LOGIN4_0_0_, contactent0_.PASSWORD as PASSWORD0_0_ from CONTACT contactent0_ where contactent0_.ID=?&lt;br /&gt;&#xD;
{ Id="31111111", LoginId="c@cnblogs.com", Name="CCC", EMail="c@cnblogs.com" }&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
===Delete Contacts===&lt;br /&gt;&#xD;
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_&lt;br /&gt;&#xD;
Hibernate: select contactent0_.ID as ID0_, contactent0_.EMAIL as EMAIL0_, contactent0_.NAME as NAME0_, contactent0_.LOGIN_ID as LOGIN4_0_, contactent0_.PASSWORD as PASSWORD0_ from CONTACT contactent0_&lt;br /&gt;&#xD;
Hibernate: delete from CONTACT where ID=?&lt;br /&gt;&#xD;
Hibernate: delete from CONTACT where ID=?&lt;br /&gt;&#xD;
Hibernate: delete from CONTACT where ID=?&lt;br /&gt;&#xD;
Hibernate: delete from CONTACT where ID=?&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
1. 通过id加载实体的时候，hibernate shards使用ShardSelectionStrategy来决定应当从哪个shard加载数据&lt;br /&gt;&#xD;
2. 新增数据的时候，hibernate shards使用ShardResolutionStrategy来决定应当将数据insert到哪个shard中&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;这里为什么不能使用ShardSelectionStrategy呢？因为与id的生成机制相关，比如说可以让数据库来生成id（hibernate中的native方式），例如自增id，在shard的应用场景下可以简单的给每个shard一个起止范围，只是在insert数据的时候必须通过特定的算法决定将数据insert到哪个shard中，这样的情况下ShardSelectionStrategy是无法运用的。而对于assigned等类似的id生成机制，还是可以运用ShardSelectionStrategy策略的&lt;br /&gt;&#xD;
3. 执行hql或者Criteria查询的时候，则使用ShardAccessStrategy从shard中查询数据&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;上面示例中的buildShardStrategyFactory方法中，我们使用了hibernate shards项目提供的SequentialShardAccessStrategy策略，这个策略在所有shards中逐个执行查询语句，然后对各个结果进行合并&#xD;
&lt;br /&gt;&lt;br /&gt;&#xD;
基于上面几点，我们可以对上面测试运行后的输出做出解释了:&lt;br /&gt;&#xD;
两个使用了hql的地方，都产生了两次sql查询，这是因为使用了SequentialShardAccessStrategy缘故，他从我们定义的两个shard中分别执行sql查询，合并结果集返回给调用者&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
把测试代码中的删除语句注释掉，运行测试后查询数据库，可以确认数据都是正确的insert在相应的shard中的&lt;br /&gt;&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;总结&lt;/strong&gt;&lt;br /&gt;&#xD;
上面演示了hibernate shards的基本用法，总体来看使用上非常简单，只需要针对实施shard的实体使用hibernate shards提供的ShardedSessionFactory即可&lt;br /&gt;&#xD;
使用hibernate shards也存在一些限制，但大部分都是使用shards后的一些设计要求，或者是目前的it技术背景下对shards设计上的约束，在后面一篇中再详细整理一下这方面内容&lt;br /&gt;&#xD;
文中使用的测试项目源文件在这里: &lt;a href="http://files.cnblogs.com/RicCC/Hibernate.Shards.Test.rar" target="_blank"&gt;下载&lt;/a&gt;。引用到的jar文件需要从相关项目网站去下载&#xD;
&lt;img src="http://www.cnblogs.com/RicCC/aggbug/1709306.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/04/10/hibernate-shards-2-shards-demo.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/RicCC/archive/2010/04/09/hibernate-shards-1-hbtest.html</id><title type="text">Hibernate Shards 数据的水平、垂直切割（一）- Hibernate测试环境</title><summary type="text">准备需要用到的东西如下，下载这几个项目最新的release版本:1. Hibernate Core，这里用的版本为3.5.0-final2. Hibernate Shards，这里用的版本为3.0.0-Beta23. MySql java connector，这里用的版本为5.1.124. slf4j，这里用的版本为1.5.115. 安装并启动mysql服务，在mysql中建立数据库hbshard...</summary><published>2010-04-09T14:44:00Z</published><updated>2010-04-09T14:44:00Z</updated><author><name>riccc</name><uri>http://www.cnblogs.com/RicCC/</uri></author><link rel="alternate" href="http://www.cnblogs.com/RicCC/archive/2010/04/09/hibernate-shards-1-hbtest.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/RicCC/archive/2010/04/09/hibernate-shards-1-hbtest.html"/><content type="html">&lt;strong style="font-size: 18pt;"&gt;准备&lt;/strong&gt;&lt;br /&gt;&#xD;
需要用到的东西如下，下载这几个项目最新的release版本:&lt;br /&gt;&#xD;
1. &lt;a href="http://www.hibernate.org/downloads.html" target="_blank"&gt;Hibernate Core&lt;/a&gt;，这里用的版本为3.5.0-final&lt;br /&gt;&#xD;
2. &lt;a href="http://www.hibernate.org/subprojects/shards/download.html" target="_blank"&gt;Hibernate Shards，这里用的版本为3.0.0-Beta2&lt;/a&gt;&lt;br /&gt;&#xD;
3. &lt;a href="http://www.mysql.com/downloads/connector/j/" target="_blank"&gt;MySql java connector&lt;/a&gt;，这里用的版本为5.1.12&lt;br /&gt;&#xD;
4. &lt;a href="http://www.slf4j.org/download.html" target="_blank"&gt;slf4j&lt;/a&gt;，这里用的版本为1.5.11&lt;br /&gt;&#xD;
5. 安装并启动mysql服务，在mysql中建立数据库hbshards，在里面如下建立测试用的contact表:&#xD;
&lt;pre &gt;CREATE TABLE contact (  &#xD;
    ID varchar(8) NOT NULL,  &#xD;
    LOGIN_ID varchar(18) NOT NULL,  &#xD;
    PASSWORD varchar(12) NOT NULL,  &#xD;
    NAME varchar(40) NOT NULL,  &#xD;
    EMAIL varchar(40) NOT NULL,  &#xD;
    PRIMARY KEY (ID)&#xD;
) ENGINE=InnoDB DEFAULT CHARSET=utf8&lt;/pre&gt;&#xD;
6. 设置好java环境变量&lt;br /&gt;&#xD;
   这里用于这个测试的CLASSPATH设置如下&lt;br /&gt;&#xD;
.;&lt;br /&gt;&#xD;
D:\Work\research\Java\Hibernate-Test\lib\antlr-2.7.6.jar;&lt;br /&gt;&#xD;
D:\Work\research\Java\Hibernate-Test\lib\dom4j-1.6.1.jar;&lt;br /&gt;&#xD;
D:\Work\research\Java\Hibernate-Test\lib\jta-1.1.jar;&lt;br /&gt;&#xD;
D:\Work\research\Java\Hibernate-Test\lib\javassist-3.9.0.GA.jar;&lt;br /&gt;&#xD;
D:\Work\research\Java\Hibernate-Test\lib\commons-collections-3.1.jar;&lt;br /&gt;&#xD;
D:\Work\research\Java\Hibernate-Test\lib\slf4j-nop-1.5.11.jar;&lt;br /&gt;&#xD;
D:\Work\research\Java\Hibernate-Test\lib\slf4j-api-1.5.11.jar;&lt;br /&gt;&#xD;
D:\Work\research\Java\Hibernate-Test\lib\mysql-connector-java-5.1.12-bin.jar;&lt;br /&gt;&#xD;
D:\Work\research\Java\Hibernate-Test\lib\hibernate3.jar;&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;Hibernate测试项目&lt;/strong&gt;&lt;br /&gt;&#xD;
用Eclipse建一个java项目Hibernate-Test，在项目文件夹下面建立lib目录&lt;br /&gt;&#xD;
将hibernate3.jar放入lib目录中，把Hibernate发布包中lib\required目录下除了slf4j-api-1.5.8.jar之外的其他jar文件放入lib目录中&lt;br /&gt;&#xD;
将mysql java connector发布包中的mysql-connector-java-5.1.12-bin.jar放入lib目录&lt;br /&gt;&#xD;
将slf4j发布包中的slf4j-api-1.5.11.jar、slf4j-nop-1.5.11.jar放入lib目录&lt;br /&gt;&#xD;
项目从lib目录中引用hibernate3.jar&lt;br /&gt;&lt;br /&gt;&#xD;
在src目录下添加测试用的实体类ContactEntity:&#xD;
&lt;pre &gt;public class ContactEntity {&#xD;
    private String _id;&#xD;
    private String _name;&#xD;
    private String _email;&#xD;
    private String _loginId;&#xD;
    private String _password;&#xD;
&lt;br /&gt;&#xD;
    public ContactEntity(){}&#xD;
    public ContactEntity(String id, String loginId, String password&#xD;
            ,String name, String email){&#xD;
        this._id = id;&#xD;
        this._loginId = loginId;&#xD;
        this._password = password;&#xD;
        this._name = name;&#xD;
        this._email = email;&#xD;
    }    &#xD;
    public String getId(){&#xD;
        return this._id;&#xD;
    }&#xD;
    public void setId(String id){&#xD;
        this._id=id;&#xD;
    }&#xD;
    public String getEMail(){&#xD;
        return this._email;&#xD;
    }&#xD;
    public void setEMail(String email){&#xD;
        this._email=email;&#xD;
    }&#xD;
    public String getName(){&#xD;
        return this._name;&#xD;
    }&#xD;
    public void setName(String name){&#xD;
        this._name=name;&#xD;
    }&#xD;
    public String getLoginId(){&#xD;
        return this._loginId;&#xD;
    }&#xD;
    public void setLoginId(String loginId){&#xD;
        this._loginId=loginId;&#xD;
    }&#xD;
    public String getPassword(){&#xD;
        return this._password;&#xD;
    }&#xD;
    public void setPassword(String password){&#xD;
        this._password=password;&#xD;
    }&#xD;
    public String toString(){&#xD;
        return "{ Id=\"" + this._id + "\""&#xD;
            + ", LoginId=\"" + this._loginId + "\""&#xD;
            + ", Name=\"" + this._name + "\""&#xD;
            + ", EMail=\"" + this._email + "\" }";&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
测试实体的映射文件ContactEntity.hbm.xml:&#xD;
&lt;pre &gt;&amp;lt;?xml version="1.0"?&amp;gt;&#xD;
&amp;lt;!DOCTYPE hibernate-mapping PUBLIC &#xD;
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN" &#xD;
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"&amp;gt;&#xD;
&amp;lt;hibernate-mapping&amp;gt;&#xD;
    &amp;lt;class name="ContactEntity" table="CONTACT"&amp;gt;&#xD;
        &amp;lt;id name="Id" column="ID" &amp;gt;&#xD;
            &amp;lt;generator /&amp;gt;&#xD;
        &amp;lt;/id&amp;gt;        &#xD;
        &amp;lt;property name="EMail" column="EMAIL" /&amp;gt;&#xD;
        &amp;lt;property name="Name" column="NAME" /&amp;gt;&#xD;
        &amp;lt;property name="LoginId" column="LOGIN_ID" /&amp;gt;&#xD;
        &amp;lt;property name="Password" column="PASSWORD" /&amp;gt;&#xD;
    &amp;lt;/class&amp;gt;&#xD;
&amp;lt;/hibernate-mapping&amp;gt;&lt;/pre&gt;&#xD;
测试用的类:&#xD;
&lt;pre &gt;import java.util.Iterator;&#xD;
import java.util.List;&#xD;
import org.hibernate.Session;&#xD;
import org.hibernate.SessionFactory;&#xD;
import org.hibernate.Transaction;&#xD;
import org.hibernate.cfg.Configuration;&#xD;
&lt;br /&gt;&#xD;
public class Main {&#xD;
    public static void main(String[] args) {&#xD;
        String loginId = "RicCC@cnblogs.com";&#xD;
        String password = "123";&#xD;
        if(args!=null &amp;amp;&amp;amp; args.length==2){&#xD;
            loginId = args[0];&#xD;
            password = args[1];&#xD;
        }&#xD;
        HibernateTest(loginId, password);&#xD;
    }&#xD;
    private static void HibernateTest(String loginId, String password){&#xD;
        SessionFactory factory = null;&#xD;
        Session session = null;&#xD;
        Transaction transaction = null;&#xD;
        List contacts = null;&#xD;
        Iterator it = null;&#xD;
        try{&#xD;
            factory = new Configuration().configure().buildSessionFactory();&#xD;
            session = factory.openSession();&#xD;
            transaction = session.beginTransaction();&#xD;
&lt;br /&gt;&#xD;
            System.out.println("===Create Contacts===");            &#xD;
            ContactEntity c = new ContactEntity("01111111","RicCC@cnblogs.com"&#xD;
                    , "123", "Richie", "RicCC@cnblogs.com");&#xD;
            session.save(c);&#xD;
            c = new ContactEntity("91111111","a@cnblogs.com"&#xD;
                    , "123", "AAA", "a@cnblogs.com");&#xD;
            session.save(c);&#xD;
            c = new ContactEntity("81111111","b@cnblogs.com"&#xD;
                    , "123", "BBB", "b@cnblogs.com");&#xD;
            session.save(c);&#xD;
            c = new ContactEntity("31111111","c@cnblogs.com"&#xD;
                    , "123", "CCC", "c@cnblogs.com");&#xD;
            session.save(c);&#xD;
            session.flush();&#xD;
&lt;br /&gt;&#xD;
            System.out.println("\n===Login Test===");&#xD;
            contacts = session.createQuery("from ContactEntity where LoginId=:loginId")&#xD;
                .setString("loginId", loginId)&#xD;
                .list();&#xD;
            if(contacts.isEmpty())&#xD;
                System.out.println("contact " + loginId + " not found!");&#xD;
            else{&#xD;
                c = (ContactEntity)contacts.get(0);&#xD;
                if(c.getPassword()==password)&#xD;
                    System.out.println("user " + loginId + " login successful");&#xD;
                else &#xD;
                    System.out.println("password is incorrect, login failed!");&#xD;
            }&#xD;
&lt;br /&gt;&#xD;
            System.out.println("\n===Delete Contacts===");&#xD;
            contacts = session.createQuery("from ContactEntity").list();&#xD;
            it = contacts.iterator();&#xD;
            while(it.hasNext()){&#xD;
                session.delete(it.next());&#xD;
            }&#xD;
            transaction.commit();&#xD;
        }catch(Exception e){&#xD;
            if(transaction!=null) transaction.rollback();&#xD;
            System.out.println(e.getMessage());&#xD;
        }finally{&#xD;
            if(session!=null) session.close();&#xD;
            if(factory!=null) factory.close();&#xD;
        }&#xD;
    }&#xD;
}&lt;/pre&gt;&#xD;
Hibernate配置文件hibernate.cfg.xml:&#xD;
&lt;pre &gt;&amp;lt;?xml version='1.0' encoding='utf-8'?&amp;gt;&#xD;
&amp;lt;!DOCTYPE hibernate-configuration PUBLIC &#xD;
    "-//Hibernate/Hibernate Configuration DTD//EN" &#xD;
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"&amp;gt;&#xD;
&amp;lt;hibernate-configuration&amp;gt;&#xD;
&amp;lt;session-factory&amp;gt;&#xD;
      &amp;lt;property name="connection.driver_class"&amp;gt;com.mysql.jdbc.Driver&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.url"&amp;gt;jdbc:mysql://localhost/hbshards&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.username"&amp;gt;root&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.password"&amp;gt;dev&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="connection.pool_size"&amp;gt;10&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="show_sql"&amp;gt;true&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="dialect"&amp;gt;org.hibernate.dialect.MySQLDialect&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;property name="hbm2ddl.auto"&amp;gt;validate&amp;lt;/property&amp;gt;&#xD;
      &amp;lt;mapping resource="ContactEntity.hbm.xml" /&amp;gt;&#xD;
&amp;lt;/session-factory&amp;gt;&#xD;
&amp;lt;/hibernate-configuration&amp;gt;&lt;/pre&gt;&#xD;
项目结构如下图:&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;img src="http://images.cnblogs.com/cnblogs_com/riccc/frameworks/hibernate-shards-test-hbdemo1.JPG" alt="" /&gt;&#xD;
&lt;br /&gt;&lt;br /&gt;&#xD;
&lt;strong style="font-size: 18pt;"&gt;运行测试&lt;/strong&gt;&lt;br /&gt;&#xD;
用Eclipse编译class文件，将Main.class、ContactEntity.class以及hibernate.cfg.xml、ContactEntity.hbm.xml&#xD;
拷贝到lib目录中，运行Main.class结果如下:&lt;br /&gt;&#xD;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;img src="http://images.cnblogs.com/cnblogs_com/riccc/frameworks/hibernate-shards-test-hbdemo2.JPG" alt="" /&gt;&lt;img src="http://www.cnblogs.com/RicCC/aggbug/1708669.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/RicCC/archive/2010/04/09/hibernate-shards-1-hbtest.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
