﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Tinysun-随笔分类-数据库和SQL </title><link>http://www.blogjava.net/tinysun/category/37840.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 06 Aug 2010 19:59:17 GMT</lastBuildDate><pubDate>Fri, 06 Aug 2010 19:59:17 GMT</pubDate><ttl>60</ttl><item><title>用 Hadoop 进行分布式并行编程 转</title><link>http://www.blogjava.net/tinysun/archive/2010/08/06/328098.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Fri, 06 Aug 2010 01:42:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/08/06/328098.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/328098.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/08/06/328098.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/328098.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/328098.html</trackback:ping><description><![CDATA[Hadoop 是一个实现了 MapReduce 计算模型的开源分布式并行编程框架，借助于 Hadoop,
程序员可以轻松地编写分布式并行程序，将其运行于计算机集群上，完成海量数据的计算。本文将介绍 MapReduce
计算模型，分布式并行计算等基本概念，以及 Hadoop 的安装部署和基本运行方法。
<br />
Hadoop 简介
<br />
<br />
Hadoop
是一个开源的可运行于大规模集群上的分布式并行编程框架，由于分布式存储对于分布式编程来说是必不可少的，这个框架中还包含了一个分布式文件系统
HDFS( Hadoop Distributed File System )。也许到目前为止，Hadoop
还不是那么广为人知，其最新的版本号也仅仅是 0.16，距离 1.0 似乎都还有很长的一段距离，但提及 Hadoop 一脉相承的另外两个开源项目
Nutch 和 Lucene ( 三者的创始人都是 Doug Cutting ),那绝对是大名鼎鼎。Lucene 是一个用 Java
开发的开源高性能全文检索工具包，它不是一个完整的应用程序，而是一套简单易用的 API 。在全世界范围内，已有无数的软件系统，Web 网站基于
Lucene 实现了全文检索功能，后来 Doug Cutting 又开创了第一个开源的 Web 搜索引擎(<a href="http://www.nutch.org/" target="_blank">http://www.nutch.org</a>)
Nutch, 它在 Lucene 的基础上增加了网络爬虫和一些和 Web 相关的功能，一些解析各类文档格式的插件等，此外，Nutch
中还包含了一个分布式文件系统用于存储数据。从 Nutch 0.8.0 版本之后，Doug Cutting 把 Nutch
中的分布式文件系统以及实现 MapReduce 算法的代码独立出来形成了一个新的开源项 Hadoop。Nutch 也演化为基于 Lucene
全文检索以及 Hadoop 分布式计算平台的一个开源搜索引擎。
<br />
<br />
基于
Hadoop,你可以轻松地编写可处理海量数据的分布式并行程序，并将其运行于由成百上千个结点组成的大规模计算机集群上。从目前的情况来
看，Hadoop 注定会有一个辉煌的未来："云计算"是目前灸手可热的技术名词，全球各大 IT 公司都在投资和推广这种新一代的计算模式，而
Hadoop 又被其中几家主要的公司用作其"云计算"环境中的重要基础软件，如:雅虎正在借助 Hadoop 开源平台的力量对抗 Google,
除了资助 Hadoop 开发团队外，还在开发基于 Hadoop 的开源项目 Pig, 这是一个专注于海量数据集分析的分布式计算程序。Amazon
公司基于 Hadoop 推出了 Amazon S3 ( Amazon Simple Storage Service
)，提供可靠，快速，可扩展的网络存储服务，以及一个商用的云计算平台 Amazon EC2 ( Amazon Elastic Compute
Cloud )。在 IBM 公司的云计算项目--"蓝云计划"中，Hadoop 也是其中重要的基础软件。Google
正在跟IBM合作，共同推广基于 Hadoop 的云计算。
<br />
<br />
<br />
迎接编程方式的变革
<br />
<br />
在摩尔定律的作用下，以前程序员根本不用考虑计算机的性能会跟不上软件的发展，因为约每隔 18 个月，CPU
的主频就会增加一倍，性能也将提升一倍，软件根本不用做任何改变，就可以享受免费的性能提升。然而，由于晶体管电路已经逐渐接近其物理上的性能极限，摩尔
定律在 2005 年左右开始失效了，人类再也不能期待单个 CPU 的速度每隔 18 个月就翻一倍，为我们提供越来越快的计算性能。Intel,
AMD, IBM 等芯片厂商开始从多核这个角度来挖掘 CPU
的性能潜力，多核时代以及互联网时代的到来，将使软件编程方式发生重大变革，基于多核的多线程并发编程以及基于大规模计算机集群的分布式并行编程是将来软
件性能提升的主要途径。
<br />
<br />
许多人认为这种编程方式的重大变化将带来一次软件的并发危机，因为我们传统的软件方式基本上是单指令单数据流的顺序执行，这种顺序执行十分符合人
类的思考习惯，却与并发并行编程格格不入。基于集群的分布式并行编程能够让软件与数据同时运行在连成一个网络的许多台计算机上,这里的每一台计算机均可以
是一台普通的 PC 机。这样的分布式并行环境的最大优点是可以很容易的通过增加计算机来扩充新的计算结点，并由此获得不可思议的海量计算能力,
同时又具有相当强的容错能力，一批计算结点失效也不会影响计算的正常进行以及结果的正确性。Google 就是这么做的，他们使用了叫做
MapReduce 的并行编程模型进行分布式并行编程，运行在叫做 GFS ( Google File System
)的分布式文件系统上，为全球亿万用户提供搜索服务。
<br />
<br />
Hadoop 实现了 Google 的 MapReduce 编程模型，提供了简单易用的编程接口，也提供了它自己的分布式文件系统
HDFS,与 Google 不同的是，Hadoop
是开源的，任何人都可以使用这个框架来进行并行编程。如果说分布式并行编程的难度足以让普通程序员望而生畏的话，开源的 Hadoop
的出现极大的降低了它的门槛，读完本文，你会发现基于 Hadoop
编程非常简单，无须任何并行开发经验，你也可以轻松的开发出分布式的并行程序，并让其令人难以置信地同时运行在数百台机器上，然后在短时间内完成海量数据
的计算。你可能会觉得你不可能会拥有数百台机器来运行你的并行程序，而事实上，随着"云计算"的普及，任何人都可以轻松获得这样的海量计算能力。例如现在
Amazon 公司的云计算平台 Amazon EC2
已经提供了这种按需计算的租用服务，有兴趣的读者可以去了解一下，这篇系列文章的第三部分将有所介绍。
<br />
<br />
掌握一点分布式并行编程的知识对将来的程序员是必不可少的，Hadoop 是如此的简便好用，何不尝试一下呢？也许你已经急不可耐的想试一下基于
Hadoop 的编程是怎么回事了，但毕竟这种编程模型与传统的顺序程序大不相同，掌握一点基础知识才能更好地理解基于 Hadoop
的分布式并行程序是如何编写和运行的。因此本文会先介绍一下 MapReduce 的计算模型，Hadoop 中的分布式文件系统 HDFS,
Hadoop 是如何实现并行计算的，然后才介绍如何安装和部署 Hadoop 框架，以及如何运行 Hadoop 程序。
<br />
<br />
MapReduce 计算模型
<br />
<br />
MapReduce 是 Google 公司的核心计算模型，它将复杂的运行于大规模集群上的并行计算过程高度的抽象到了两个函数，Map 和
Reduce, 这是一个令人惊讶的简单却又威力巨大的模型。适合用 MapReduce 来处理的数据集(或任务)有一个基本要求:
待处理的数据集可以分解成许多小的数据集，而且每一个小数据集都可以完全并行地进行处理。
<br />
<br />
<br />
图 1. MapReduce 计算流程
<br />
<img src="http://www.ibm.com/developerworks/cn/opensource/os-cn-hadoop1/figure1.jpg"  alt="" />
<br />
图一说明了用 MapReduce 来处理大数据集的过程, 这个 MapReduce
的计算过程简而言之，就是将大数据集分解为成百上千的小数据集，每个(或若干个)数据集分别由集群中的一个结点(一般就是一台普通的计算机)进行处理并生
成中间结果，然后这些中间结果又由大量的结点进行合并, 形成最终结果。
<br />
<br />
计算模型的核心是 Map 和 Reduce 两个函数，这两个函数由用户负责实现，功能是按一定的映射规则将输入的 &lt;key, value&gt; 对转换成另一个或一批 &lt;key, value&gt; 对输出。
<br />
<br />
<br />
表一 Map 和 Reduce 函数
<br />
函数 输入 输出 说明
<br />
Map &lt;k1, v1&gt; List(&lt;k2,v2&gt;) 1. 将小数据集进一步解析成一批 &lt;key,value&gt; 对，输入 Map 函数中进行处理。
<br />
2. 每一个输入的 &lt;k1,v1&gt; 会输出一批 &lt;k2,v2&gt;。 &lt;k2,v2&gt; 是计算的中间结果。&nbsp;
<br />
Reduce &lt;k2,List(v2)&gt; &lt;k3,v3&gt; 输入的中间结果 &lt;k2,List(v2)&gt; 中的 List(v2) 表示是一批属于同一个 k2 的 value
<br />
<br />
以一个计算文本文件中每个单词出现的次数的程序为例，&lt;k1,v1&gt; 可以是 &lt;行在文件中的偏移位置,
文件中的一行&gt;，经 Map 函数映射之后，形成一批中间结果 &lt;单词，出现次数&gt;, 而 Reduce
函数则可以对中间结果进行处理，将相同单词的出现次数进行累加，得到每个单词的总的出现次数。
<br />
<br />
基于 MapReduce 计算模型编写分布式并行程序非常简单，程序员的主要编码工作就是实现 Map 和 Reduce
函数，其它的并行编程中的种种复杂问题，如分布式存储，工作调度，负载平衡，容错处理，网络通信等，均由 MapReduce 框架(比如 Hadoop
)负责处理，程序员完全不用操心。
<br />
<br />
四 集群上的并行计算
<br />
<br />
MapReduce 计算模型非常适合在大量计算机组成的大规模集群上并行运行。图一中的每一个 Map 任务和每一个 Reduce 任务均可以同时运行于一个单独的计算结点上，可想而知其运算效率是很高的，那么这样的并行计算是如何做到的呢？
<br />
<br />
数据分布存储
<br />
<br />
Hadoop 中的分布式文件系统 HDFS 由一个管理结点 ( NameNode )和N个数据结点 ( DataNode
)组成，每个结点均是一台普通的计算机。在使用上同我们熟悉的单机上的文件系统非常类似，一样可以建目录，创建，复制，删除文件，查看文件内容等。但其底
层实现上是把文件切割成 Block，然后这些 Block 分散地存储于不同的 DataNode 上，每个 Block 还可以复制数份存储于不同的
DataNode 上，达到容错容灾之目的。NameNode 则是整个 HDFS
的核心，它通过维护一些数据结构，记录了每一个文件被切割成了多少个 Block，这些 Block 可以从哪些 DataNode 中获得，各个
DataNode 的状态等重要信息。如果你想了解更多的关于 HDFS 的信息，可进一步阅读参考资料： [url]The Hadoop
Distributed File System:Architecture and Design [/url]
<br />
分布式并行计算
<br />
<br />
Hadoop 中有一个作为主控的 JobTracker，用于调度和管理其它的 TaskTracker, JobTracker
可以运行于集群中任一台计算机上。TaskTracker 负责执行任务，必须运行于 DataNode 上，即 DataNode
既是数据存储结点，也是计算结点。 JobTracker 将 Map 任务和 Reduce 任务分发给空闲的 TaskTracker,
让这些任务并行运行，并负责监控任务的运行情况。如果某一个 TaskTracker 出故障了，JobTracker
会将其负责的任务转交给另一个空闲的 TaskTracker 重新运行。
<br />
<br />
本地计算
<br />
<br />
数据存储在哪一台计算机上，就由这台计算机进行这部分数据的计算，这样可以减少数据在网络上的传输，降低对网络带宽的需求。在 Hadoop
这样的基于集群的分布式并行系统中，计算结点可以很方便地扩充，而因它所能够提供的计算能力近乎是无限的，但是由是数据需要在不同的计算机之间流动，故网
络带宽变成了瓶颈，是非常宝贵的，&#8220;本地计算&#8221;是最有效的一种节约网络带宽的手段，业界把这形容为&#8220;移动计算比移动数据更经济&#8221;。
<br />
<br />
图 2. 分布存储与并行计算
<br />
<img src="http://www.ibm.com/developerworks/cn/opensource/os-cn-hadoop1/figure2.jpg"  alt="" />
<br />
<br />
任务粒度
<br />
<br />
把原始大数据集切割成小数据集时，通常让小数据集小于或等于 HDFS 中一个 Block 的大小(缺省是
64M)，这样能够保证一个小数据集位于一台计算机上，便于本地计算。有 M 个小数据集待处理，就启动 M 个 Map 任务，注意这 M 个 Map
任务分布于 N 台计算机上并行运行，Reduce 任务的数量 R 则可由用户指定。
<br />
<br />
Partition
<br />
<br />
把 Map 任务输出的中间结果按 key 的范围划分成 R 份( R 是预先定义的 Reduce 任务的个数)，划分时通常使用 hash
函数如: hash(key) mod R，这样可以保证某一段范围内的 key，一定是由一个 Reduce 任务来处理，可以简化 Reduce
的过程。
<br />
<br />
Combine
<br />
<br />
在 partition 之前，还可以对中间结果先做 combine，即将中间结果中有相同 key的 &lt;key, value&gt;
对合并成一对。combine 的过程与 Reduce 的过程类似，很多情况下就可以直接使用 Reduce 函数，但 combine 是作为
Map 任务的一部分，在执行完 Map 函数后紧接着执行的。Combine 能够减少中间结果中 &lt;key, value&gt;
对的数目，从而减少网络流量。
<br />
<br />
Reduce 任务从 Map 任务结点取中间结果
<br />
<br />
Map 任务的中间结果在做完 Combine 和 Partition 之后，以文件形式存于本地磁盘。中间结果文件的位置会通知主控
JobTracker, JobTracker 再通知 Reduce 任务到哪一个 DataNode 上去取中间结果。注意所有的 Map
任务产生中间结果均按其 Key 用同一个 Hash 函数划分成了 R 份，R 个 Reduce 任务各自负责一段 Key 区间。每个
Reduce 需要向许多个 Map 任务结点取得落在其负责的 Key 区间内的中间结果，然后执行 Reduce 函数，形成一个最终的结果文件。
<br />
<br />
任务管道
<br />
<br />
有 R 个 Reduce 任务，就会有 R 个最终结果，很多情况下这 R 个最终结果并不需要合并成一个最终结果。因为这 R 个最终结果又可以做为另一个计算任务的输入，开始另一个并行计算任务。
<br />
<br />
五 Hadoop 初体验
<br />
<br />
Hadoop 支持 Linux 及 Windows 操作系统, 但其官方网站声明 Hadoop 的分布式操作在 Windows
上未做严格测试，建议只把 Windows 作为 Hadoop 的开发平台。在 Windows 环境上的安装步骤如下( Linux
平台类似，且更简单一些):
<br />
<br />
(1)在 Windows 下，需要先安装 Cgywin, 安装 Cgywin 时注意一定要选择安装 openssh (在 Net
category )。安装完成之后，把 Cgywin 的安装目录如 c:\cygwin\bin 加到系统环境变量 PATH 中，这是因为运行
Hadoop 要执行一些 linux 环境下的脚本和命令。
<br />
<br />
(2)安装 Java 1.5.x，并将 JAVA_HOME 环境变量设置为 Java 的安装根目录如 C:\Program Files\Java\jdk1.5.0_01。
<br />
<br />
(3)到 Hadoop 官方网站[url] http://hadoop.apache.org[/url]下载Hadoop Core, 最新的稳定版本是 0.16.0. 将下载后的安装包解压到一个目录，本文假定解压到 c:\hadoop-0.16.0。
<br />
<br />
4)修改 conf/hadoop-env.sh 文件，在其中设置 JAVA_HOME 环境变量： export
JAVA_HOME="C:\Program Files\Java\jdk1.5.0_01&#8221; (因为路径中 Program Files
中间有空格，一定要用双引号将路径引起来)
<br />
<br />
至此，一切就绪，可以运行 Hadoop 了。以下的运行过程，需要启动 cygwin, 进入模拟 Linux 环境。在下载的 Hadoop
Core 包中，带有几个示例程序并且已经打包成了 hadoop-0.16.0-examples.jar。其中有一个 WordCount
程序，功能是统计一批文本文件中各个单词出现的次数，我们先来看看怎么运行这个程序。Hadoop 共有三种运行模式:
单机(非分布式)模式，伪分布式运行模式，分布式运行模式，其中前两种运行模式体现不了 Hadoop
分布式计算的优势，并没有什么实际意义，但对程序的测试及调试很有帮助，我们先从这两种模式入手，了解基于 Hadoop
的分布式并行程序是如何编写和运行的。
<br />
<br />
单机(非分布式)模式
<br />
<br />
这种模式在一台单机上运行，没有分布式文件系统，而是直接读写本地操作系统的文件系统。
<br />
<br />
<br />
代码清单1
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <embed src="http://mintelong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%24%20cd%20%2Fcygdrive%2Fc%2Fhadoop-0.16.0%0A%24%20mkdir%20test-in%20%20%0A%24%20cd%20test-in%0A%23%E5%9C%A8%20test-in%20%E7%9B%AE%E5%BD%95%E4%B8%8B%E5%88%9B%E5%BB%BA%E4%B8%A4%E4%B8%AA%E6%96%87%E6%9C%AC%E6%96%87%E4%BB%B6%2C%20WordCount%20%E7%A8%8B%E5%BA%8F%E5%B0%86%E7%BB%9F%E8%AE%A1%E5%85%B6%E4%B8%AD%E5%90%84%E4%B8%AA%E5%8D%95%E8%AF%8D%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%24%20echo%20%22hello%20world%20bye%20world%22%20%3Efile1.txt%20%20%20%0A%24%20echo%20%22hello%20hadoop%20goodbye%20hadoop%22%20%3Efile2.txt%0A%24%20cd%20..%0A%24%20bin%2Fhadoop%20jar%20hadoop-0.16.0-examples.jar%20wordcount%20test-in%20test-out%0A%23%E6%89%A7%E8%A1%8C%E5%AE%8C%E6%AF%95%EF%BC%8C%E4%B8%8B%E9%9D%A2%E6%9F%A5%E7%9C%8B%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C%EF%BC%9A%0A%24%20cd%20test-out%0A%24%20cat%20part-00000%0Abye%20%20%20%20%201%0Agoodbye%20%201%0Ahadoop%20%202%0Ahello%20%20%20%202%0Aworld%20%20%202" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol class="dp-j" start="1">
    <li><span><span>$&nbsp;cd&nbsp;/cygdrive/c/hadoop-</span><span class="number">0.16</span><span>.</span><span class="number">0</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>$&nbsp;mkdir&nbsp;test-in&nbsp;&nbsp;&nbsp;&nbsp;</span></li>
    <li><span>$&nbsp;cd&nbsp;test-in&nbsp;&nbsp;</span></li>
    <li><span>#在&nbsp;test-in&nbsp;目录下创建两个文本文件,&nbsp;WordCount&nbsp;程序将统计其中各个单词出现次数&nbsp;&nbsp;</span></li>
    <li><span>$&nbsp;echo&nbsp;<span class="string">"hello&nbsp;world&nbsp;bye&nbsp;world"</span><span>&nbsp;&gt;file1.txt&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span></li>
    <li><span>$&nbsp;echo&nbsp;<span class="string">"hello&nbsp;hadoop&nbsp;goodbye&nbsp;hadoop"</span><span>&nbsp;&gt;file2.txt&nbsp;&nbsp;</span></span></li>
    <li><span>$&nbsp;cd&nbsp;..&nbsp;&nbsp;</span></li>
    <li><span>$&nbsp;bin/hadoop&nbsp;jar&nbsp;hadoop-<span class="number">0.16</span><span>.</span><span class="number">0</span><span>-examples.jar&nbsp;wordcount&nbsp;test-in&nbsp;test-out&nbsp;&nbsp;</span></span></li>
    <li><span>#执行完毕，下面查看执行结果：&nbsp;&nbsp;</span></li>
    <li><span>$&nbsp;cd&nbsp;test-out&nbsp;&nbsp;</span></li>
    <li><span>$&nbsp;cat&nbsp;part-<span class="number">00000</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>bye&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="number">1</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>goodbye&nbsp;&nbsp;<span class="number">1</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>hadoop&nbsp;&nbsp;<span class="number">2</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>hello&nbsp;&nbsp;&nbsp;&nbsp;<span class="number">2</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>world&nbsp;&nbsp;&nbsp;<span class="number">2</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">$ cd /cygdrive/c/hadoop-0.16.0
$ mkdir test-in
$ cd test-in
#在 test-in 目录下创建两个文本文件, WordCount 程序将统计其中各个单词出现次数
$ echo "hello world bye world" &gt;file1.txt
$ echo "hello hadoop goodbye hadoop" &gt;file2.txt
$ cd ..
$ bin/hadoop jar hadoop-0.16.0-examples.jar wordcount test-in test-out
#执行完毕，下面查看执行结果：
$ cd test-out
$ cat part-00000
bye     1
goodbye  1
hadoop  2
hello    2
world   2</pre>
<br />
<br />
注意事项：运行 bin/hadoop jar hadoop-0.16.0-examples.jar wordcount test-in
test-out 时，务必注意第一个参数是 jar, 不是 -jar, 当你用 -jar
时，不会告诉你是参数错了，报告出来的错误信息是：Exception in thread "main"
java.lang.NoClassDefFoundError: org/apache/hadoop/util/ProgramDriver,
笔者当时以为是 classpath 的设置问题，浪费了不少时间。通过分析 bin/hadoop 脚本可知，-jar 并不是 bin/hadoop
脚本定义的参数，此脚本会把 -jar 作为 Java 的参数，Java 的-jar 参数表示执行一个 Jar 文件(这个 Jar
文件必须是一个可执行的 Jar,即在 MANIFEST 中定义了主类), 此时外部定义的 classpath 是不起作用的，因而会抛出
java.lang.NoClassDefFoundError 异常。而 jar 是 bin/hadoop 脚本定义的参数，会调用 Hadoop
自己的一个工具类 RunJar，这个工具类也能够执行一个 Jar 文件，并且外部定义的 classpath 有效。
<br />
<br />
伪分布式运行模式
<br />
<br />
这种模式也是在一台单机上运行，但用不同的 Java 进程模仿分布式运行中的各类结点 ( NameNode, DataNode,
JobTracker, TaskTracker, Secondary NameNode )，请注意分布式运行中的这几个结点的区别：
<br />
<br />
从分布式存储的角度来说，集群中的结点由一个 NameNode 和若干个 DataNode 组成, 另有一个 Secondary
NameNode 作为 NameNode 的备份。从分布式应用的角度来说，集群中的结点由一个 JobTracker 和若干个
TaskTracker 组成，JobTracker 负责任务的调度，TaskTracker 负责并行执行任务。TaskTracker 必须运行在
DataNode 上，这样便于数据的本地计算。JobTracker 和 NameNode 则无须在同一台机器上。
<br />
<br />
(1) 按代码清单2修改 conf/hadoop-site.xml。注意 conf/hadoop-default.xml 中是
Hadoop 缺省的参数，你可以通过读此文件了解 Hadoop 中有哪些参数可供配置，但不要修改此文件。可通过修改
conf/hadoop-site.xml 改变缺省参数值，此文件中设置的参数值会覆盖 conf/hadoop-default.xml
的同名参数。
<br />
<br />
<br />
代码清单 2
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <embed src="http://mintelong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%3Cconfiguration%3E%0A%20%20%3Cproperty%3E%0A%20%20%20%20%3Cname%3Efs.default.name%3C%2Fname%3E%0A%20%20%20%20%3Cvalue%3Elocalhost%3A9000%3C%2Fvalue%3E%0A%20%20%3C%2Fproperty%3E%0A%20%20%3Cproperty%3E%0A%20%20%20%20%3Cname%3Emapred.job.tracker%3C%2Fname%3E%0A%20%20%20%20%3Cvalue%3Elocalhost%3A9001%3C%2Fvalue%3E%0A%20%20%3C%2Fproperty%3E%0A%20%20%3Cproperty%3E%0A%20%20%20%20%3Cname%3Edfs.replication%3C%2Fname%3E%0A%20%20%20%20%3Cvalue%3E1%3C%2Fvalue%3E%0A%20%20%3C%2Fproperty%3E%0A%3C%2Fconfiguration%3E" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol class="dp-j" start="1">
    <li><span><span>&lt;configuration&gt;&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&lt;property&gt;&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;fs.<span class="keyword">default</span><span>.name&lt;/name&gt;&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;localhost:<span class="number">9000</span><span>&lt;/value&gt;&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&lt;/property&gt;&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&lt;property&gt;&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;mapred.job.tracker&lt;/name&gt;&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;localhost:<span class="number">9001</span><span>&lt;/value&gt;&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&lt;/property&gt;&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&lt;property&gt;&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;dfs.replication&lt;/name&gt;&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;<span class="number">1</span><span>&lt;/value&gt;&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&lt;/property&gt;&nbsp;&nbsp;</span></li>
    <li><span>&lt;/configuration&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">&lt;configuration&gt;
&lt;property&gt;
&lt;name&gt;fs.default.name&lt;/name&gt;
&lt;value&gt;localhost:9000&lt;/value&gt;
&lt;/property&gt;
&lt;property&gt;
&lt;name&gt;mapred.job.tracker&lt;/name&gt;
&lt;value&gt;localhost:9001&lt;/value&gt;
&lt;/property&gt;
&lt;property&gt;
&lt;name&gt;dfs.replication&lt;/name&gt;
&lt;value&gt;1&lt;/value&gt;
&lt;/property&gt;
&lt;/configuration&gt;</pre>
<br />
<br />
参数 fs.default.name 指定 NameNode 的 IP 地址和端口号。缺省值是 file:///, 表示使用本地文件系统, 用于单机非分布式模式。此处我们指定使用运行于本机 localhost 上的 NameNode。
<br />
<br />
参数 mapred.job.tracker 指定 JobTracker 的 IP 地址和端口号。缺省值是 local, 表示在本地同一
Java 进程内执行 JobTracker 和 TaskTracker, 用于单机非分布式模式。此处我们指定使用运行于本机 localhost
上的 JobTracker ( 用一个单独的 Java 进程做 JobTracker )。
<br />
<br />
参数 dfs.replication 指定 HDFS 中每个 Block 被复制的次数，起数据冗余备份的作用。在典型的生产系统中，这个数常常设置为3。
<br />
<br />
(2)配置 SSH,如代码清单3所示:
<br />
<br />
<br />
代码清单 3
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <embed src="http://mintelong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%24%20ssh-keygen%20-t%20dsa%20-P%20''%20-f%20~%2F.ssh%2Fid_dsa%20%0A%24%20cat%20~%2F.ssh%2Fid_dsa.pub%20%3E%3E%20~%2F.ssh%2Fauthorized_keys" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol class="dp-j" start="1">
    <li><span><span>$&nbsp;ssh-keygen&nbsp;-t&nbsp;dsa&nbsp;-P&nbsp;</span><span class="string">''</span><span>&nbsp;-f&nbsp;~/.ssh/id_dsa&nbsp;&nbsp;&nbsp;</span></span></li>
    <li><span>$&nbsp;cat&nbsp;~/.ssh/id_dsa.pub&nbsp;&gt;&gt;&nbsp;~/.ssh/authorized_keys&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">$ ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
$ cat ~/.ssh/id_dsa.pub &gt;&gt; ~/.ssh/authorized_keys</pre>
<br />
<br />
配置完后，执行一下 ssh localhost, 确认你的机器可以用 SSH 连接，并且连接时不需要手工输入密码。
<br />
<br />
(3)格式化一个新的分布式文件系统, 如代码清单4所示:
<br />
<br />
<br />
代码清单 4
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <embed src="http://mintelong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%24%20cd%20%2Fcygdrive%2Fc%2Fhadoop-0.16.0%0A%24%20bin%2Fhadoop%20namenode%20%E2%80%93format%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol class="dp-j" start="1">
    <li><span><span>$&nbsp;cd&nbsp;/cygdrive/c/hadoop-</span><span class="number">0.16</span><span>.</span><span class="number">0</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>$&nbsp;bin/hadoop&nbsp;namenode&nbsp;&#8211;format&nbsp;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">$ cd /cygdrive/c/hadoop-0.16.0
$ bin/hadoop namenode &#8211;format </pre>
<br />
<br />
(4) 启动 hadoop 进程, 如代码清单5所示。控制台上的输出信息应该显示启动了 namenode, datanode,
secondary namenode, jobtracker, tasktracker。启动完成之后，通过 ps &#8211;ef
应该可以看到启动了5个新的 java 进程。
<br />
<br />
代码清单 5
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <embed src="http://mintelong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%24%20bin%2Fstart-all.sh%20%20%0A%24%20ps%20%E2%80%93ef" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol class="dp-j" start="1">
    <li><span><span>$&nbsp;bin/start-all.sh&nbsp;&nbsp;&nbsp;&nbsp;</span></span></li>
    <li><span>$&nbsp;ps&nbsp;&#8211;ef&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">$ bin/start-all.sh
$ ps &#8211;ef</pre>
<br />
<br />
(5) 运行 wordcount 应用, 如代码清单6所示:
<br />
<br />
代码清单 6
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <embed src="http://mintelong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%24%20bin%2Fhadoop%20dfs%20-put%20.%2Ftest-in%20input%20%20%0A%23%E5%B0%86%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%B8%8A%E7%9A%84%20.%2Ftest-in%20%E7%9B%AE%E5%BD%95%E6%8B%B7%E5%88%B0%20HDFS%20%E7%9A%84%E6%A0%B9%E7%9B%AE%E5%BD%95%E4%B8%8A%EF%BC%8C%E7%9B%AE%E5%BD%95%E5%90%8D%E6%94%B9%E4%B8%BA%20input%0A%23%E6%89%A7%E8%A1%8C%20bin%2Fhadoop%20dfs%20%E2%80%93help%20%E5%8F%AF%E4%BB%A5%E5%AD%A6%E4%B9%A0%E5%90%84%E7%A7%8D%20HDFS%20%E5%91%BD%E4%BB%A4%E7%9A%84%E4%BD%BF%E7%94%A8%E3%80%82%0A%24%20bin%2Fhadoop%20jar%20hadoop-0.16.0-examples.jar%20wordcount%20input%20output%0A%23%E6%9F%A5%E7%9C%8B%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C%3A%0A%23%E5%B0%86%E6%96%87%E4%BB%B6%E4%BB%8E%20HDFS%20%E6%8B%B7%E5%88%B0%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%86%8D%E6%9F%A5%E7%9C%8B%EF%BC%9A%0A%24%20bin%2Fhadoop%20dfs%20-get%20output%20output%20%0A%24%20cat%20output%2F*%0A%23%E4%B9%9F%E5%8F%AF%E4%BB%A5%E7%9B%B4%E6%8E%A5%E6%9F%A5%E7%9C%8B%0A%24%20bin%2Fhadoop%20dfs%20-cat%20output%2F*%0A%24%20bin%2Fstop-all.sh%20%23%E5%81%9C%E6%AD%A2%20hadoop%20%E8%BF%9B%E7%A8%8B" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol class="dp-j" start="1">
    <li><span><span>$&nbsp;bin/hadoop&nbsp;dfs&nbsp;-put&nbsp;./test-in&nbsp;input&nbsp;&nbsp;&nbsp;&nbsp;</span></span></li>
    <li><span>#将本地文件系统上的&nbsp;./test-in&nbsp;目录拷到&nbsp;HDFS&nbsp;的根目录上，目录名改为&nbsp;input&nbsp;&nbsp;</span></li>
    <li><span>#执行&nbsp;bin/hadoop&nbsp;dfs&nbsp;&#8211;help&nbsp;可以学习各种&nbsp;HDFS&nbsp;命令的使用。&nbsp;&nbsp;</span></li>
    <li><span>$&nbsp;bin/hadoop&nbsp;jar&nbsp;hadoop-<span class="number">0.16</span><span>.</span><span class="number">0</span><span>-examples.jar&nbsp;wordcount&nbsp;input&nbsp;output&nbsp;&nbsp;</span></span></li>
    <li><span>#查看执行结果:&nbsp;&nbsp;</span></li>
    <li><span>#将文件从&nbsp;HDFS&nbsp;拷到本地文件系统中再查看：&nbsp;&nbsp;</span></li>
    <li><span>$&nbsp;bin/hadoop&nbsp;dfs&nbsp;-get&nbsp;output&nbsp;output&nbsp;&nbsp;&nbsp;</span></li>
    <li><span>$&nbsp;cat&nbsp;output/*&nbsp;&nbsp;</span></li>
    <li><span>#也可以直接查看&nbsp;&nbsp;</span></li>
    <li><span>$&nbsp;bin/hadoop&nbsp;dfs&nbsp;-cat&nbsp;output/*&nbsp;&nbsp;</span></li>
    <li><span>$&nbsp;bin/stop-all.sh&nbsp;#停止&nbsp;hadoop&nbsp;进程&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">$ bin/hadoop dfs -put ./test-in input
#将本地文件系统上的 ./test-in 目录拷到 HDFS 的根目录上，目录名改为 input
#执行 bin/hadoop dfs &#8211;help 可以学习各种 HDFS 命令的使用。
$ bin/hadoop jar hadoop-0.16.0-examples.jar wordcount input output
#查看执行结果:
#将文件从 HDFS 拷到本地文件系统中再查看：
$ bin/hadoop dfs -get output output
$ cat output/*
#也可以直接查看
$ bin/hadoop dfs -cat output/*
$ bin/stop-all.sh #停止 hadoop 进程</pre>
<br />
<br />
故障诊断
<br />
<br />
(1) 执行 $ bin/start-all.sh 启动 Hadoop 进程后，会启动5个 java 进程, 同时会在 /tmp
目录下创建五个 pid 文件记录这些进程 ID 号。通过这五个文件，可以得知 namenode, datanode, secondary
namenode, jobtracker, tasktracker 分别对应于哪一个 Java 进程。当你觉得 Hadoop
工作不正常时，可以首先查看这5个 java 进程是否在正常运行。
<br />
<br />
(2) 使用 web 接口。访问 http://localhost:50030 可以查看 JobTracker 的运行状态。访问
http://localhost:50060 可以查看 TaskTracker 的运行状态。访问 http://localhost:50070
可以查看 NameNode 以及整个分布式文件系统的状态，浏览分布式文件系统中的文件以及 log 等。
<br />
<br />
(3) 查看 ${HADOOP_HOME}/logs 目录下的 log 文件，namenode, datanode, secondary
namenode, jobtracker, tasktracker 各有一个对应的 log 文件，每一次运行的计算任务也有对应用 log
文件。分析这些 log 文件有助于找到故障原因。
<br />
<br />
<br />
结束语
<br />
<br />
现在，你已经了解了 MapReduce 计算模型，分布式文件系统 HDFS，分布式并行计算等的基本原理, 并且有了一个可以运行的 Hadoop 环境，运行了一个基于 Hadoop 的并行程序。
<img src ="http://www.blogjava.net/tinysun/aggbug/328098.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-08-06 09:42 <a href="http://www.blogjava.net/tinysun/archive/2010/08/06/328098.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>了解SQL Server执行计划 </title><link>http://www.blogjava.net/tinysun/archive/2010/04/15/318438.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 15 Apr 2010 09:12:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/04/15/318438.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/318438.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/04/15/318438.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/318438.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/318438.html</trackback:ping><description><![CDATA[<p>如果在执行计划中看到如下所示的任何一项，就应该将它们视作警告信号并调查它们以找出潜在的性能问题。从性能方面来说，下面所示的每一项都是不理想的。</p>
<p>&nbsp;Index or table scans(索引或者表扫描)：可能意味着需要更好的或者额外的索引。</p>
<p>Bookmark Lookups(书签查找)：考虑修改当前的聚集索引，使用复盖索引，限制SELECT语句中的字段数量。</p>
<p>Filter(过滤)：在WHERE从句中移除用到的任何函数，不要在SQL语句中包含视图，可能需要额外的索引。</p>
<p>Sort(排序)：数据是否真的需要排序？可否使用索引来避免排序？在客户端排序是否会更加有效率？</p>
<p>&nbsp;<br />
</p>
<p>查看SQL Server图形执行计划时，可以查找的非常有用的一个东西就是查询优化器如何为给定的查询使用索引来从表中获取数据。通过查看是否有用到索引，以及索引如何被使用，都有助于判断当前的索引是否使得查询执行得尽可能的快。</p>
<p>将鼠标移到图形执行计划上的表名(以及它的图标)上面，就会弹出一个窗口，从它上面可以看到一些信息。这些信息让你知道是否有用到索引来从表中获取数据，以及它是如何使用的。这些信息包括：</p>
<p>&#183;&nbsp;&nbsp;&nbsp;&nbsp; Table Scan(表扫描)：如果看到这个信息，就说明数据表上没有聚集索引，或者查询优化器没有使用索引来查找。意即资料表的每一行都被检查到。如果资料表相对较小的话，表扫描可以非常快速，有时甚至快过使用索引。</p>
<p>因此，当看到有执行表扫描时，第一件要做的事就是看看数据表有多少数据行。如果不是太多的话，那么表扫描可能提供了最好的总体效能。但如果数据表大的话，表扫描就极可能需要长时间来完成，查询效能就大受影响。在这种情况下，就需要仔细研究，为数据表增加一个适当的索引用于这个查询。</p>
<p>假设你发现某查询使用了表扫描，有一个合适的非聚集索引，但它没有用到。这意味着什么呢？为什么这个索引没有用到呢？如果需要获得的数据量相对数据表大小来说非常大，或者数据选择性不高(意味着同一个字段中重复的值很多)，表扫描经常会比索引扫描快。例如，如果一个数据表有10000个数据行，查询返回1000行，如果这个表没有聚集索引的话，那么表扫描将比使用一个非聚集索引更快。或者如果数据表有10000个数据行，且同一个字段(WHERE条件句有用到这个字段)上有1000笔重复的数据，表扫描也会比使用非聚集索引更快。</p>
<p>查看图形执行计划上的数据表上的弹出式窗口时，请注意&#8221;预估的资料行数(Estimated Row Count)&#8221;。这个数字是查询优化器作出的多少个数据行会被返回的最佳推测。如果执行了表扫描且&#8221;预估的数据行数&#8221;数值很高的话，就意味着返回的记录数很多，查询优化器认为执行表扫描比使用可用的非聚集索引更快。</p>
<p>&#183;&nbsp;&nbsp;&nbsp;&nbsp; Index Seek(索引查找)：索引查找意味着查询优化器使用了数据表上的非聚集索引来查找数据。性能通常会很快，尤其是当只有少数的数据行被返回时。</p>
<p>&#183;&nbsp;&nbsp;&nbsp;&nbsp; Clustered Index Seek(聚集索引查找)：这指查询优化器使用了数据表上的聚集索引来查找数据，性能很快。实际上，这是SQL Server能做的最快的索引查找类型。</p>
<p>&#183;&nbsp;&nbsp;&nbsp;&nbsp; Clustered Index Scan(聚集索引扫描)：聚集索引扫描与表扫描相似，不同的是聚集索引扫描是在一个建有聚集索引的数据表上执行的。和一般的表扫描一样，聚集索引扫描可能表明存在效能问题。一般来说，有两种原因会引此聚集索引扫描的执行。第一个原因，相对于数据表上的整体数据行数目，可能需要获取太多的数据行。查看&#8221;预估的数据行数量(Estimated Row Count)&#8221;可以对此加以验证。第二个原因，可能是由于WHERE条件句中用到的字段选择性不高。在任何情况下，与标准的表扫描不同，聚集索引扫描并不会总是去查找数据表中的所有数据，所以聚集索引扫描一般都会比标准的表扫描要快。通常来说，要将聚集索引扫描改成聚集索引查找，你唯一能做的是重写查询语句，让语句限制性更多，从而返回更少的数据行。</p>
<p>&nbsp;<br />
</p>
<p>绝大多数情况下，查询优化器会对连接进行分析，按最有效率的顺序，使用最有效率的连接类型来对数据表进行连接。但并不总是如此。在图形执行计划中你可以看到代表查询所使用到的各种不同连接类型的图标。此外，每个连接图标都有两个箭头指向它。指向连接图标的上面的箭头代表该连接的外部表，下面的箭头则代表这个连接的内部表。箭头的另一头则指向被连接的数据表名。</p>
<p>&nbsp;</p>
<p><br />
有时在多表连接的查询中，箭头的另一头指向的并不是一个数据表，而是另一个连接。如果将鼠标移到指向外部连接与内部连接的箭头上，就可以看到一个弹出式窗口，告诉你有多少数据行被发送至这个连接来进行处理。外部表应该总是比内部表含有更少的数据行。如果不是，则说明查询优化器所选择的连接顺序可能不正确(下面是关于这个话题的更多信息)。</p>
<p>&nbsp;</p>
<p><br />
首先，让我们来看看连接类型。SQL Server可以使用三种不同的技术来连接资料表：嵌套循环(nested loop)，散列(hash)，以及合并(merge)。一般来说，嵌套循环是最快的连接类型，但如果不可能使用嵌套循环的话，则会用到散列或者合并作为合适的连接类型。两者都比嵌套循环连接慢。</p>
<p>&nbsp;</p>
<p><br />
当连接大表时，则合并连接可能是最佳选项，而非嵌套循环连接。唯一的明确这一点的方式是对两者都进行测试以查看哪一个最有效率。</p>
<p>&nbsp;</p>
<p><br />
如果你怀疑某个查询速度慢的原因可能是因为它所使用的连接类型不理想，那么你可以使用连接提示来复盖查询优化器的选择。在使用连接提示之前，你需要花费一些时间去了解一下每种连接类型以及它们的工作方式。这是一个复杂的话题，超出了本文的讨论范围。</p>
<p>&nbsp;</p>
<p><br />
查询优化器选择最有效率的连接类型来连接数据表。例如，嵌套循环连接的外部表应该是连接的两个表中较小的那个表。散列连接也是一样，它的外部表应该是较小的那个表。如果你觉得查询优化器选择的连接顺序是错误的，可以使用连接提示来复盖它。</p>
<p>&nbsp;</p>
<p>你常常会在图形执行计划上看到标识成&#8221;书签查找(Bookmark Lookup)&#8221;的图标。书签查找相当常见。书签查找的本质是告诉你查询处理器必须从数据表或者聚集索引中来查找它所需要的数据行，而不是从非聚集索引中直接读取。</p>
<p>&nbsp;</p>
<p><br />
打比方说，如果一个查询语句的SELECT,JOIN以及WHERE子句中的所有字段，都不存在于那个用来定位符合查询条件的数据行的非聚集索引中，那么查询优化器就不得不做额外的工作在数据表或聚集索引中查找那些满足这个查询语句的字段。</p>
<p>&nbsp;</p>
<p><br />
另一种引起书签查找的原因是使用了SELECT *。由于在绝大多情况下它会返回比你实际所需更多的数据，所以应该永不使用SELECT *.</p>
<p>&nbsp;</p>
<p><br />
从性能方面来说，书签查找是不理想的。因为它会请求额外的I/O开销在字段中查找以返回所需的数据行。</p>
<p>&nbsp;</p>
<p><br />
如果认为书签查找防碍了查询的性能，那么有四种选择可以用来避免它：可以建立WHERE子句会用到的聚集索引，利用索引交集的优势，建立覆盖的非聚集索引，或者(如果是SQL Server 2000/2005企业版的话)可以建立索引视图。如果这些都不可能，或者使用它们中的任何一个都会耗用比书签查找更多的资源，那么书签查找就是最佳的选择了。[</p>
<p><br />
本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/xiao_hn/archive/2009/06/11/4259628.aspx</p>
<img src ="http://www.blogjava.net/tinysun/aggbug/318438.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-04-15 17:12 <a href="http://www.blogjava.net/tinysun/archive/2010/04/15/318438.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQLserver锁和事务隔离级别的比较与使用</title><link>http://www.blogjava.net/tinysun/archive/2010/02/04/311962.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 04 Feb 2010 07:01:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/02/04/311962.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/311962.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/02/04/311962.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/311962.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/311962.html</trackback:ping><description><![CDATA[<p style="margin: 0cm 0cm 0pt"><strong><span>对象</span></strong></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>①<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span><strong style="background-color: #99ff99; color: black">锁</strong>：每条</span><span>SQL</span><span>语句</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>②<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>隔离：事务</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span><strong style="background-color: #99ff99; color: black">锁</strong></span></strong></p>
<p style="margin: 0cm 0cm 0pt"><span>①</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span>并发问题</span></strong></p>
<p style="margin: 0cm 0cm 0pt"><span>丢失更新</span></p>
<p style="margin: 0cm 0cm 0pt"><span>未确认的读取（脏读）</span></p>
<p style="margin: 0cm 0cm 0pt"><span>不一致的分析（非重复读）：多次读取相同的数据（行）不一致（其他用户更改</span><span>update</span><span>）</span></p>
<p style="margin: 0cm 0cm 0pt"><span>幻像读：多次读取有不存在和新增的数据（其他用户插入</span><span>insert</span><span>或删除</span><span>delete</span><span>）</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span><strong style="background-color: #ffff66; color: black">隔离级别</strong></span></strong></p>
<table style="width: 440.25pt" border="1" cellpadding="0" width="587">
    <tbody>
        <tr style="height: 34.2pt">
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 29.48%; padding-right: 1.5pt; height: 34.2pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="29%">
            <p style="text-align: center; margin: 0cm 0cm 0pt" align="center"><strong><span style="font-size: 9pt"><strong style="background-color: #ffff66; color: black">隔离级别</strong></span></strong></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 5.24%; padding-right: 1.5pt; height: 34.2pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="5%">
            <p style="text-align: center; margin: 0cm 0cm 0pt" align="center"><strong><span style="font-size: 9pt">脏读</span></strong></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 7.88%; padding-right: 1.5pt; height: 34.2pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="7%">
            <p style="text-align: center; margin: 0cm 0cm 0pt" align="center"><strong><span style="font-size: 9pt">不可重复读取</span></strong></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 5.8%; padding-right: 1.5pt; height: 34.2pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="5%">
            <p style="text-align: center; margin: 0cm 0cm 0pt" align="center"><strong><span style="font-size: 9pt">幻像</span></strong></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 49.58%; padding-right: 1.5pt; height: 34.2pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="49%">
            <p style="margin: 0cm 0cm 0pt"><strong><span style="font-size: 9pt">说明</span></strong></p>
            </td>
        </tr>
        <tr>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 29.48%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="29%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">未提交读<span>(read uncommitted)</span></span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 5.24%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="5%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">是</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 7.88%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="7%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">是</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 5.8%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="5%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">是</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 49.58%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="49%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">如果其他事务更新，不管是否提交，立即执行</span></p>
            </td>
        </tr>
        <tr>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 29.48%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="29%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">提交读<span>(read committed默认)</span></span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 5.24%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="5%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">否</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 7.88%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="7%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">是</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 5.8%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="5%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">是</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 49.58%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="49%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">读取提交过的数据。如果其他事务更新没提交，则等待</span></p>
            </td>
        </tr>
        <tr>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 29.48%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="29%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">可重复读<span>(repeatable read)</span></span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 5.24%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="5%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">否</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 7.88%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="7%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">否</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 5.8%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="5%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">是</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 49.58%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="49%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">查询期间，不允许其他事务<span>update</span></span></p>
            </td>
        </tr>
        <tr>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 29.48%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="29%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">可串行读<span>(serializable)</span></span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 5.24%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="5%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">否</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 7.88%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="7%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">否</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 5.8%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="5%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">否</span></p>
            </td>
            <td style="border-bottom: #c1ccd9; border-left: #c1ccd9; padding-bottom: 1.5pt; background-color: transparent; padding-left: 1.5pt; width: 49.58%; padding-right: 1.5pt; border-top: #c1ccd9; border-right: #c1ccd9; padding-top: 1.5pt" valign="top" width="49%">
            <p style="text-align: left; margin: 0cm 0cm 0pt" align="left"><span style="font-size: 9pt">查询期间，不允许其他事务<span>insert或delete</span></span></p>
            </td>
        </tr>
    </tbody>
</table>
<p style="margin: 0cm 0cm 0pt"><strong><span>提交读</span></strong></p>
<p style="margin: 0cm 0cm 0pt"><span>假设存在表</span><span>A</span><span>，如下所示</span></p>
<table style="border-bottom: medium none; border-left: medium none; border-collapse: collapse; border-top: medium none; border-right: medium none" border="1" cellspacing="0" cellpadding="0">
    <tbody>
        <tr>
            <td style="border-bottom: windowtext 1pt solid; border-left: windowtext 1pt solid; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 142pt; padding-right: 5.4pt; border-top: windowtext 1pt solid; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="189">
            <p style="margin: 0cm 0cm 0pt"><strong><span>A1</span></strong></p>
            </td>
            <td style="border-bottom: windowtext 1pt solid; border-left: #c1ccd9; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 142.05pt; padding-right: 5.4pt; border-top: windowtext 1pt solid; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="189">
            <p style="margin: 0cm 0cm 0pt"><strong><span>A2</span></strong></p>
            </td>
            <td style="border-bottom: windowtext 1pt solid; border-left: #c1ccd9; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 142.05pt; padding-right: 5.4pt; border-top: windowtext 1pt solid; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="189">
            <p style="margin: 0cm 0cm 0pt"><strong><span>A3</span></strong></p>
            </td>
        </tr>
        <tr>
            <td style="border-bottom: windowtext 1pt solid; border-left: windowtext 1pt solid; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 142pt; padding-right: 5.4pt; border-top: #c1ccd9; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="189">
            <p style="margin: 0cm 0cm 0pt"><span>11</span></p>
            </td>
            <td style="border-bottom: windowtext 1pt solid; border-left: #c1ccd9; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 142.05pt; padding-right: 5.4pt; border-top: #c1ccd9; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="189">
            <p style="margin: 0cm 0cm 0pt"><span>21</span></p>
            </td>
            <td style="border-bottom: windowtext 1pt solid; border-left: #c1ccd9; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 142.05pt; padding-right: 5.4pt; border-top: #c1ccd9; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="189">
            <p style="margin: 0cm 0cm 0pt"><span>31</span></p>
            </td>
        </tr>
        <tr>
            <td style="border-bottom: windowtext 1pt solid; border-left: windowtext 1pt solid; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 142pt; padding-right: 5.4pt; border-top: #c1ccd9; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="189">
            <p style="margin: 0cm 0cm 0pt"><span>12</span></p>
            </td>
            <td style="border-bottom: windowtext 1pt solid; border-left: #c1ccd9; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 142.05pt; padding-right: 5.4pt; border-top: #c1ccd9; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="189">
            <p style="margin: 0cm 0cm 0pt"><span>22</span></p>
            </td>
            <td style="border-bottom: windowtext 1pt solid; border-left: #c1ccd9; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 142.05pt; padding-right: 5.4pt; border-top: #c1ccd9; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="189">
            <p style="margin: 0cm 0cm 0pt"><span>32</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p style="margin: 0cm 0cm 0pt"><span>打开查询分析器并打开两个连接，分别输入如下两个事务：</span></p>
<p style="margin: 0cm 0cm 0pt"></p>
<p style="margin: 0cm 0cm 0pt"><span>--</span><span>事务Ⅰ</span></p>
<p style="margin: 0cm 0cm 0pt"><span>SET TRANSACTION ISOLATION LEVEL READ Committed</span></p>
<p style="margin: 0cm 0cm 0pt"><span>begin tran</span></p>
<p style="margin: 0cm 0cm 0pt"><span>update A set A2 = 20 where A1 = 11</span></p>
<p style="margin: 0cm 0cm 0pt"><span>waitfor delay '00:</span><ST1:TIME minute="10" hour="0"><span>00:10</span></ST1:TIME><span>'</span></p>
<p style="margin: 0cm 0cm 0pt"><span>rollback tran</span></p>
<p style="margin: 0cm 0cm 0pt"><span>--</span><span>事务Ⅱ</span></p>
<p style="margin: 0cm 0cm 0pt"><span>SET TRANSACTION ISOLATION LEVEL READ Committed</span></p>
<p style="margin: 0cm 0cm 0pt"><span>select * from A where A1 = 11</span></p>
<p style="margin: 0cm 0cm 0pt"></p>
<p style="margin: 0cm 0cm 0pt"><span>如果先运行事务Ⅰ，然后紧接着运行事务Ⅱ，则事务Ⅱ要等待</span><span>10</span><span>秒钟（</span><span style="color: blue; font-size: 9pt">一个连接在修改数据块时别的连接也不能查询这个数据块，直到解<strong style="background-color: #99ff99; color: black">锁</strong>。反之亦然：读的时候不能写和修改</span><span>）。</span></p>
<p style="margin: 0cm 0cm 0pt"><span>如果把事务Ⅱ改为如下</span></p>
<p style="margin: 0cm 0cm 0pt"><span>SET TRANSACTION ISOLATION LEVEL READ UNCommitted</span></p>
<p style="margin: 0cm 0cm 0pt"><span>select * from A where A1 = 11</span></p>
<p style="margin: 0cm 0cm 0pt"><span>那么事务Ⅱ不需等待，立即执行（</span><span style="color: blue; font-size: 9pt">可以看出</span><span style="color: blue; font-size: 9pt">READ UNCommitted</span><span style="color: blue; font-size: 9pt">事务</span><span style="color: blue; font-size: 9pt">select</span><span style="color: blue; font-size: 9pt">不对数据发出共享<strong style="background-color: #99ff99; color: black">锁</strong></span><span>）</span></p>
<span>
<p style="margin: 0cm 0cm 0pt"><strong><span><strong style="background-color: #99ff99; color: black">锁</strong>：</span><span>(</span></strong><strong><span>这里主要讲解</span> </strong><strong><span>共享<strong style="background-color: #99ff99; color: black">锁</strong></span> </strong><strong><span>和</span> </strong><strong><span>排他<strong style="background-color: #99ff99; color: black">锁</strong></span> </strong><strong><span>两种经常用到的<strong style="background-color: #99ff99; color: black">锁</strong></span><span>)</span></strong></p>
<p style="text-indent: 21pt; margin: 0cm 0cm 0pt"><span>共享<strong style="background-color: #99ff99; color: black">锁</strong>主要是为了共享读（</span><span>select</span><span>），如果存在事务（一个或多个）拥有对表中数据（关于<strong style="background-color: #99ff99; color: black">锁</strong>数据的多少，视<strong style="background-color: #99ff99; color: black">锁</strong>的粒度而定）的共享<strong style="background-color: #99ff99; color: black">锁</strong>，不允许对<strong style="background-color: #99ff99; color: black">锁</strong>定的数据进行更新</span><span>(update)</span><span>（从<strong style="background-color: #99ff99; color: black">锁</strong>的角度讲，即不允许事务获取排他<strong style="background-color: #99ff99; color: black">锁</strong>，要等到所有的共享<strong style="background-color: #99ff99; color: black">锁</strong>都释放掉）。反之，如果事务对数据已经具有排他<strong style="background-color: #99ff99; color: black">锁</strong>（只能有一个），其他的事务就不能对<strong style="background-color: #99ff99; color: black">锁</strong>定的数据获取共享<strong style="background-color: #99ff99; color: black">锁</strong>和排他<strong style="background-color: #99ff99; color: black">锁</strong>（即排他<strong style="background-color: #99ff99; color: black">锁</strong>与共享<strong style="background-color: #99ff99; color: black">锁</strong>不能兼容，更多信息请查看<span style="color: blue"><strong style="background-color: #99ff99; color: black">锁</strong>兼容性</span>），在此特别强调一下</span> <span><strong style="background-color: #99ff99; color: black">锁</strong>定的数据</span> <span>，因为有的资料上讲解到</span><span style="color: red; font-size: 9pt">&#8220;一个连接写的时候，另一个连接可以写&#8221;</span><span>，实际上写的这种情况是各个连接的读写的数据不是相同的行，也就是说各个连接<strong style="background-color: #99ff99; color: black">锁</strong>定的数据不同。</span></p>
<p style="text-indent: 21pt; margin: 0cm 0cm 0pt"><span>根据以上分析，我们总结为六个字为&#8220;<span style="color: blue">共享读，排他写</span>&#8221;。</span></p>
<p style="text-indent: 21pt; margin: 0cm 0cm 0pt"><span>了解了<strong style="background-color: #99ff99; color: black">锁</strong>的情况之后，又涉及到一个问题。事务究竟要保持<strong style="background-color: #99ff99; color: black">锁</strong>多久呢</span><span>?</span></p>
<p style="text-indent: 21pt; margin: 0cm 0cm 0pt"><span>一般来说，共享<strong style="background-color: #99ff99; color: black">锁</strong>的<strong style="background-color: #99ff99; color: black">锁</strong>定时间与事务的<strong style="background-color: #ffff66; color: black">隔离级别</strong>有关，如果<strong style="background-color: #ffff66; color: black">隔离级别</strong>为</span><span>Read Committed</span><span>的默认级别，只在读取</span><span>(select)</span><span>的期间保持<strong style="background-color: #99ff99; color: black">锁</strong>定，即在查询出数据以后就释放了<strong style="background-color: #99ff99; color: black">锁</strong>；如果<strong style="background-color: #ffff66; color: black">隔离级别</strong>为更高的</span><span>Repeatable read</span><span>或</span><span>Serializable</span><span>，直到事务结束才释放<strong style="background-color: #99ff99; color: black">锁</strong>。另说明，如果</span><span>select</span><span>语句中指定了</span><span>HoldLock</span><span>提示，则也要等到事务结束才释放<strong style="background-color: #99ff99; color: black">锁</strong>。</span></p>
<p style="text-indent: 21pt; margin: 0cm 0cm 0pt"><span>排他<strong style="background-color: #99ff99; color: black">锁</strong>直到事务结束才释放。</span></p>
<p style="text-indent: 21pt; margin: 0cm 0cm 0pt"><span>做出了以上分析，现在我们可能会存在这样的疑问，到底在执行</span><span>SQL</span><span>语句的时候发出什么样的<strong style="background-color: #99ff99; color: black">锁</strong>呢，这就由事务的<strong style="background-color: #ffff66; color: black">隔离级别</strong>决定了。一般情况，读语句</span><span>(select)</span><span>发出共享<strong style="background-color: #99ff99; color: black">锁</strong>，写语句</span><span>(update,insert,delete)</span><span>发出排他<strong style="background-color: #99ff99; color: black">锁</strong>。但是，如果这样不能满足我们的要求怎么办呢，有没有更多选择呢，别急，</span><span>SQLserver</span><span>为我们提供了<strong style="background-color: #99ff99; color: black">锁</strong>定提示的概念。</span></p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span><strong style="background-color: #99ff99; color: black">锁</strong>定提示对</span><span>SQL</span><span>语句进行特别指定，这个指定将覆盖事务的<strong style="background-color: #ffff66; color: black">隔离级别</strong>。下面对各个<strong style="background-color: #99ff99; color: black">锁</strong>定提示分别予以介绍（更多资料请查看</span><span>SQLserver</span><span>的联机帮助），笔者做出了以下分类。</span></p>
<p style="margin: 0cm 0cm 0pt"><span>类型</span><span>1</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>①<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>READUNCOMMITTED</span><span>：不发出<strong style="background-color: #99ff99; color: black">锁</strong></span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>②<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>READCOMMITTED</span><span>：发出共享<strong style="background-color: #99ff99; color: black">锁</strong>，保持到读取结束</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>③<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>REPEATABLEREAD</span><span>：发出共享<strong style="background-color: #99ff99; color: black">锁</strong>，保持到事务结束</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>④<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>SERIALIZABLE</span><span>：发出共享<strong style="background-color: #99ff99; color: black">锁</strong>，保持到事务结束</span></p>
<p style="margin: 0cm 0cm 0pt"><span>类型</span><span>2</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>①<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>NOLOCK</span><span>：不发出<strong style="background-color: #99ff99; color: black">锁</strong>。等同于</span><span>READUNCOMMITTED</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>②<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>HOLDLOCK</span><span>：发出共享<strong style="background-color: #99ff99; color: black">锁</strong>，保持到事务结束。等同于</span><span>SERIALIZABLE</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>③<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>XLOCK</span><span>：发出排他<strong style="background-color: #99ff99; color: black">锁</strong>，保持到事务结束。</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>④<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>UPDLOCK</span><span>：发出更新<strong style="background-color: #99ff99; color: black">锁</strong>，保持到事务事务结束。（<span style="color: red">更新<strong style="background-color: #99ff99; color: black">锁</strong>：不阻塞别的事物，允许别的事物读数据（即更新<strong style="background-color: #99ff99; color: black">锁</strong>可与共享<strong style="background-color: #99ff99; color: black">锁</strong>兼容），但他确保自上次读取数据后数据没有被更新</span>）</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>⑤<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>READPAST</span><span>：发出共享<strong style="background-color: #99ff99; color: black">锁</strong>，但跳过<strong style="background-color: #99ff99; color: black">锁</strong>定行，它不会被阻塞。<span style="color: red">适用条件：提交读的<strong style="background-color: #ffff66; color: black">隔离级别</strong>，行级<strong style="background-color: #99ff99; color: black">锁</strong>，</span></span><span style="color: red">select</span><span style="color: red">语句中。</span></p>
<p style="margin: 0cm 0cm 0pt"><span>类型</span><span>3</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>①<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>ROWLOCK</span><span>：行级<strong style="background-color: #99ff99; color: black">锁</strong></span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>②<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>PAGLOCK</span><span>：页级<strong style="background-color: #99ff99; color: black">锁</strong></span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>③<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>TABLOCK</span><span>：表<strong style="background-color: #99ff99; color: black">锁</strong></span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"><span>④<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>TABLOCKX</span><span>：表排他<strong style="background-color: #99ff99; color: black">锁</strong></span></p>
<span>
<p style="margin: 0cm 0cm 0pt 18pt"><span>讲解完<strong style="background-color: #99ff99; color: black">锁</strong>后，下面结合一个具体实例，具体看一下<strong style="background-color: #99ff99; color: black">锁</strong>的使用。</span></p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span>在很多系统中，经常会遇到这种情况，要保持一个编号的唯一，如会计软件中的凭证的编号。一种编号的处理是这样的，把表中的最大编号保存到表中，然后在这个编号上累加，形成新的编号。这个过程对并发处理要求非常高，下面我们就来模拟这个过程，看如何保持编号的唯一性。</span></p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span>新建一张表</span><span>code</span><span>来保存凭证的最大编号。字段如下：编号</span><span>:bh(numeric(18,0)),</span><span>凭证表名</span><span>pinzheng(varchar(50))</span></p>
<p style="margin: 0cm 0cm 0pt"><span>假设表中有这样的一条记录：</span></p>
<p style="text-indent: -18pt; margin: 0cm 0cm 0pt 18pt"></p>
<table style="border-bottom: medium none; border-left: medium none; border-collapse: collapse; border-top: medium none; border-right: medium none" border="1" cellspacing="0" cellpadding="0">
    <tbody>
        <tr>
            <td style="border-bottom: windowtext 1pt solid; border-left: windowtext 1pt solid; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 213.05pt; padding-right: 5.4pt; border-top: windowtext 1pt solid; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="284">
            <p style="margin: 0cm 0cm 0pt"><span>Bh</span></p>
            </td>
            <td style="border-bottom: windowtext 1pt solid; border-left: #c1ccd9; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 213.05pt; padding-right: 5.4pt; border-top: windowtext 1pt solid; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="284">
            <p style="margin: 0cm 0cm 0pt"><span>Pinzheng</span></p>
            </td>
        </tr>
        <tr>
            <td style="border-bottom: windowtext 1pt solid; border-left: windowtext 1pt solid; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 213.05pt; padding-right: 5.4pt; border-top: #c1ccd9; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="284">
            <p style="margin: 0cm 0cm 0pt"><span>18000</span></p>
            </td>
            <td style="border-bottom: windowtext 1pt solid; border-left: #c1ccd9; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 213.05pt; padding-right: 5.4pt; border-top: #c1ccd9; border-right: windowtext 1pt solid; padding-top: 0cm" valign="top" width="284">
            <p style="margin: 0cm 0cm 0pt"><span>会计凭证</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p style="margin: 0cm 0cm 0pt"><span>新建一个存储过程来生成新的凭证编号，如下：</span></p>
<p style="margin: 0cm 0cm 0pt"><span>CREATE PROCEDURE up_getbh<span>&nbsp;&nbsp;</span>AS</span></p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>Begin Tran</p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>Declare @numnewbh numeric(18,0)</p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>select<span>&nbsp;&nbsp;</span>@numnewbh = bh FROM code<span>&nbsp;&nbsp;</span>WITH (UPDLOCK,ROWLOCK) where pinzheng = '<span>会计凭证</span><span>' </span></p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>set @numnewbh = @numnewbh + 1</p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>update code set<span>&nbsp;&nbsp;</span>bh = @numnewbh where pinzheng = '<span>会计凭证</span><span>'</span></p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>print @numnewbh</p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>Commit tran</p>
<p style="margin: 0cm 0cm 0pt"><span>GO</span></p>
<p style="margin: 0cm 0cm 0pt"></p>
<p style="margin: 0cm 0cm 0pt"><span>然后，打开查询分析器，并多开几个连接（笔者开了</span><span>8</span><span>个连接，模拟有</span><span>8</span><span>个人同时并发，读者可以开更多的连接进行试验），把类似以下这样的语句复制到每个连接窗口中，</span></p>
<p style="margin: 0cm 0cm 0pt"><span>declare @i numeric(18,0)</span></p>
<p style="margin: 0cm 0cm 0pt"><span>set @i = 1</span></p>
<p style="margin: 0cm 0cm 0pt"><span>while @i = 1</span></p>
<p style="margin: 0cm 0cm 0pt"><span>Begin</span></p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>if getdate() &gt; '2004-07-22 14:23'<span>&nbsp;&nbsp;</span>--<span>设定一个时间，到此时间同时执行</span><span>upgetbh</span><span>存储过程</span></p>
<p style="margin: 0cm 0cm 0pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>set @i = 0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></p>
<p style="margin: 0cm 0cm 0pt"><span>end </span></p>
<p style="margin: 0cm 0cm 0pt"><span>exec up_getbh</span></p>
<p style="margin: 0cm 0cm 0pt"></p>
<p style="margin: 0cm 0cm 0pt"><span>然后，接连运行各个连接，到</span><span>2004-7-22 14</span><span>：</span><span>23 </span><span>这一刻，各个连接同时运行</span><span>up_getbh</span><span>。从运行结果可以看出连接顺序出现</span><span>18001</span><span>开始个数字，并没有重号或丢号的现象。</span></p>
<p style="margin: 0cm 0cm 0pt"></p>
<p style="margin: 0cm 0cm 0pt"><span>分析：由于</span><span>up_getbh</span><span>中的</span><span>select</span><span>语句使用了更新<strong style="background-color: #99ff99; color: black">锁</strong>，因更新<strong style="background-color: #99ff99; color: black">锁</strong>之间不能兼容，所以各个连接要等到所有其他的连接释放掉<strong style="background-color: #99ff99; color: black">锁</strong>才能执行，而更新<strong style="background-color: #99ff99; color: black">锁</strong>的释放要等到事务结束，这样就不会发生号出错的现象了。</span></p>
</span></span>
<img src ="http://www.blogjava.net/tinysun/aggbug/311962.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-02-04 15:01 <a href="http://www.blogjava.net/tinysun/archive/2010/02/04/311962.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Sql Server 锁机制 </title><link>http://www.blogjava.net/tinysun/archive/2010/02/04/311956.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 04 Feb 2010 06:38:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/02/04/311956.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/311956.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/02/04/311956.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/311956.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/311956.html</trackback:ping><description><![CDATA[<div style="position: relative" _eventid="4" _madepositioned="true">转自：<a href="http://blog.csdn.net/missmecn/archive/2008/10/06/3019798.aspx">http://blog.csdn.net/missmecn/archive/2008/10/06/3019798.aspx</a></div>
<div style="position: relative" _eventid="4" _madepositioned="true">&nbsp;</div>
<div style="position: relative" _eventid="4" _madepositioned="true">相关文章: <span class="close"><a title="关闭" onclick="$('related_topics').hide();return false;" href="http://www.javaeye.com/topic/186542#"><font color="#006699">&nbsp;</font></a></span>
<ul>
    <li><a href="http://www.javaeye.com/topic/236271" target="_blank"><u><font color="#006699">my<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">sql</font></span></span>数据库锁</font></u></a>
    <li><a href="http://www.javaeye.com/topic/170393" target="_blank"><u><font color="#006699">ORACLE里几种锁模式</font></u></a> </li>
</ul>
<br />
推荐圈子: <a href="http://pipboy.group.javaeye.com/" target="_blank"><u><font color="#006699">Pipboy</font></u></a> <br />
<a href="http://www.javaeye.com/wiki/topic/186542" target="_blank"><u><font color="#006699">更多相关推荐</font></u></a> </div>
<script type="text/javascript">
            new Draggable("related_topics");
          </script>对<span class="hilite3"><span class="hilite3"><font style="background-color: #aaffaa">锁机制</font></span></span>的研究要具备两个条件： <br />
1．数据量大 <br />
2．多个用户同时并发 <br />
如果缺少这两个条件，数据库不容易产生死锁问题。研究起来可能会事倍功半。如果这两个条件都有，但你还是按数据库缺省设置来处理数据，则会带来很多的问题，比如： <br />
1）丢失更新 <br />
A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果 <br />
2）脏读 <br />
A用户修改了数据时,B用户也在读该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致 <br />
3）不可重复读 <br />
B用户读出该数据并修改,同时，A用户也在读取数据，此时A用户再读取数据时发现前后两次的值不一致 <br />
<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">SERVER</font></span></span> 作为多用户数据库系统，以事务为单位，使用锁来实现并发控制。<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span><span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">SERVER</font></span></span>使用&#8220;锁&#8221;确保事务完整性和数据一致性。 <br />
<br />
<strong>一、锁的概念</strong> <br />
锁（LOCKING）是最常用的并发控制机构。是防止其他事务访问指定的资源控制、实现并发控制的一种主要手段。锁是事务对某个数据库中的资源（如表和记录）存取前，先向系统提出请求，封锁该资源，事务获得锁后，即取得对数据的控制权，在事务释放它的锁之前，其他事务不能更新此数据。当事务撤消后，释放被锁定的资源。 <br />
当一个用户锁住数据库中的某个对象时，其他用户就不能再访问该对象 <br />
<br />
<strong>二、锁的粒度</strong> <br />
<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">Server</font></span></span> 2000 具有多粒度锁定，允许一个事务锁定不同类型的的资源。为了使锁定的成本减至最少，<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">Server</font></span></span> 自动将资源锁定在适合任务的级别。锁定在较小的粒度（例如行）可以增加并发但需要较大的开销，因为如果锁定了许多行，则需要控制更多的锁。锁定在较大的粒度（例如表）就并发而言是相当昂贵的，因为锁定整个表限制了其它事务对表中任意部分进行访问，但要求的开销较低，因为需要维护的锁较少。<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">Server</font></span></span> 可以锁定行、页、扩展盘区、表、库等资源。 <br />
<ul>
    <li>资源 级别 描述
    <li>RID 行锁 表中的单个行
    <li>Key 行级锁 索引中的行
    <li>Page 页级锁 一个数据页或者索引页
    <li>Extent 页级锁 一组数据页或者索引页
    <li>Table 表级锁 整个表
    <li>Database 数据库级锁 整个数据库 </li>
</ul>
<br />
选择多大的粒度，根据对数据的操作而定。如果是更新表中所有的行，则用表级锁;如果是更新表中的某一行，则用行级锁。 <br />
行级锁是一种最优锁，因为行级锁不可能出现数据既被占用又没有使用的浪费现象。但是，如果用户事务中频繁对某个表中的多条记录操作，将导致对该表的许多记录行都加上了行级锁，数据库系统中锁的数目会急剧增加，这样就加重了系统负荷，影响系统性能。因此，在<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">Server</font></span></span>中，还支持锁升级(lock escalation)。 <br />
所谓锁升级是指调整锁的粒度，将多个低粒度的锁替换成少数的更高粒度的锁，以此来降低系统负荷。在<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">Server</font></span></span>中当一个事务中的锁较多，达到锁升级门限时，系统自动将行级锁和页面锁升级为表级锁。 <br />
特别值得注意的是，在<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">Server</font></span></span>中，锁的升级门限以及锁升级是由系统自动来确定的，不需要用户设置。 <br />
<br />
<strong>三、锁的模式</strong> <br />
锁模式以及描述表 <br />
<ul><br />
    锁模式 描述
    <li>共享（S） 用于不更改或不更新数据（只读操作），如SELECT语句
    <li>更新（U） 用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。
    <li>排它（X） 用于数据修改操作，例如 INSERT、UPDATE或DELETE。确保不会同时对同一资源进行多重更新
    <li>意向 当 Microsoft <span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">Server</font></span></span> 数据库引擎获取低级别的锁时，它还将在包含更低级别对象的对象上放置意向锁.例如： 当锁定行或索引键范围时，数据库引擎将在包含行或键的页上放置意向锁。当锁定页时，数据库引擎将在包含页的更高级别的对象上放置意向锁。 <br />
    意向锁的类型为：意向共享（IS）、意向排它（IX）以及意向排它共享（SIX）
    <li>架构 在执行依赖于表架构的操作时使用。架构锁的类型为：架构修改（Sch-M）和架构稳定（Sch-S）
    <li>大容量更新（BU） 向表中大容量复制数据并指定了TABLOCK提示时使用 </li>
</ul>
<br />
<br />
<br />
<strong>四 <span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">Server</font></span></span> 中锁的设置</strong> <br />
1 处理死锁和设置死锁优先级 <br />
死锁就是多个用户申请不同封锁,由于申请者均拥有一部分封锁权而又等待其他用户拥有的部分封锁而引起的无休止的等待 <br />
可以使用SET DEADLOCK_PRIORITY控制在发生死锁情况时会话的反应方式。 <br />
Syntax: <br />
SET DEADLOCK_PRIORITY { LOW | NORMAL} <br />
其中LOW说明该进程会话的优先级较低，在出现死锁时，可以首先中断该进程的事务。 <br />
2 处理超时和设置锁超时持续时间。 <br />
@@LOCK_TIMEOUT 返回当前会话的当前锁超时设置，单位为毫秒 <br />
SET LOCK_TIMEOUT 设置允许应用程序设置语句等待阻塞资源的最长时间。当语句等待的时间大于 LOCK_TIMEOUT 设置时，系统将自动取消阻塞的语句，并给应用程序返回"已超过了锁请求超时时段"的 1222 号错误信息 <br />
示例 <br />
1）将锁超时期限设置为 1,800 毫秒。 <br />
SET LOCK_TIMEOUT 1800 <br />
2) 配置索引的锁定粒度 <br />
可以使用 sp_indexoption 系统存储过程来设置用于索引的锁定粒度 <br />
3）设置事务隔离级别 <br />
SET&nbsp;&nbsp; TRANSACTION&nbsp;&nbsp; ISOLATION&nbsp;&nbsp; LEVEL <br />
<br />
<strong>五 查看锁的信息</strong> <br />
1 执行 EXEC SP_LOCK 报告有关锁的信息 <br />
2 查询分析器中按Ctrl+2可以看到锁的信息 <br />
<br />
<strong>六、奇怪的<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">sql</font></span></span>语句</strong> <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/186542#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><font color="#000000">begin&nbsp;tran &nbsp;&nbsp;</font>
    <li><font color="#000000">update&nbsp;titles&nbsp;set&nbsp;title_idid＝title_id&nbsp;&nbsp;where&nbsp;</font><span class="number"><font color="#c00000">1</font></span><font color="#000000">＝</font><span class="number"><font color="#c00000">2</font></span><font color="#000000">&nbsp;&nbsp;</font></span>
    <li><span class="keyword"><strong><font color="#7f0055">if</font></strong></span><font color="#000000">&nbsp;(selectavg(price)fromtitles)&gt;$</font><span class="number"><font color="#c00000">15</font></span><font color="#000000">&nbsp;&nbsp;</font></span>
    <li><font color="#000000">begin &nbsp;&nbsp;</font>
    <li><font color="#000000">update&nbsp;titles&nbsp;set&nbsp;price＝price*</font><span class="number"><font color="#c00000">1.10</font></span><font color="#000000">&nbsp;&nbsp;</font></span>
    <li><font color="#000000">where&nbsp;price&lt;(select&nbsp;avg(price)from&nbsp;titles) &nbsp;&nbsp;</font>
    <li><font color="#000000">end &nbsp;&nbsp;</font>
    <li><font color="#000000">commit&nbsp;tran&nbsp;&nbsp;</font> </li>
</ol>
</div>
<pre style="display: none" class="java" name="code_lighted">begin tran
update titles set title_idid＝title_id  where 1＝2
if (selectavg(price)fromtitles)&gt;$15
begin
update titles set price＝price*1.10
where price&lt;(select avg(price)from titles)
end
commit tran</pre>
<br />
<br />
update titles set title_idid＝title_id&nbsp; where 1＝2，这个条件是永远也不会成立的，如此写的含义是什么呢？ <br />
这里的where子句看起来很奇怪，尽管计算出的结果总是false。当优化器处理此查询时，因为它找不到任何有效的SARG，它的查询规划就会强制使用一个独占锁定来进行表扫描。此事务执行时，where子句立即得到一个false值，于是不会执行实际上的扫描，但此进程仍得到了一个独占的表锁定。 <br />
因为此进程现在已有一个独占的表锁，所以可以保证没有其他事务会修改任何数据行，能进行重复读，且避免了由于holdlock所引起的潜在性死锁。 <br />
但是，在使用表锁定来尽可能地减少死锁的同时，也增加了对表锁定的争用。因此，在实现这种方法之前，你需要权衡一下：避免死锁是否比允许并发地对表进行访问更重要。 <br />
所以，在这个事务中，没有其他进程修改表中任何行的price。 <br />
<br />
<strong>七 如何避免死锁</strong> <br />
1 使用事务时，尽量缩短事务的逻辑处理过程，及早提交或回滚事务； <br />
2 设置死锁超时参数为合理范围，如：3分钟-10分种；超过时间，自动放弃本次操作，避免进程悬挂； <br />
3 所有的SP都要有错误处理（通过@error） <br />
4 一般不要修改<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">SERVER</font></span></span>事务的默认级别。不推荐强行加锁 <br />
5 优化程序，检查并避免死锁现象出现； <br />
1）合理安排表访问顺序 <br />
2）在事务中尽量避免用户干预，尽量使一个事务处理的任务少些。 <br />
3）采用脏读技术。脏读由于不对被访问的表加锁，而避免了锁冲突。在客户机/服务器应用环境中，有些事务往往不允许读脏数据，但在特定的条件下，我们可以用脏读。 <br />
4）数据访问时域离散法。数据访问时域离散法是指在客户机/服务器结构中，采取各种控制手段控制对数据库或数据库中的对象访问时间段。主要通过以下方式实现: 合理安排后台事务的执行时间，采用工作流对后台事务进行统一管理。工作流在管理任务时，一方面限制同一类任务的线程数（往往限制为1个），防止资源过多占用; 另一方面合理安排不同任务执行时序、时间，尽量避免多个后台任务同时执行，另外，避免在前台交易高峰时间运行后台任务 <br />
5）数据存储空间离散法。数据存储空间离散法是指采取各种手段，将逻辑上在一个表中的数据分散到若干离散的空间上去，以便改善对表的访问性能。主要通过以下方法实现: 第一，将大表按行或列分解为若干小表; 第二，按不同的用户群分解。 <br />
6）使用尽可能低的隔离性级别。隔离性级别是指为保证数据库数据的完整性和一致性而使多用户事务隔离的程度，<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span>92定义了4种隔离性级别：未提交读、提交读、可重复读和可串行。如果选择过高的隔离性级别，如可串行，虽然系统可以因实现更好隔离性而更大程度上保证数据的完整性和一致性，但各事务间冲突而死锁的机会大大增加，大大影响了系统性能。 <br />
7）使用Bound Connections。Bound connections 允许两个或多个事务连接共享事务和锁，而且任何一个事务连接要申请锁如同另外一个事务要申请锁一样，因此可以允许这些事务共享数据而不会有加锁的冲突。 <br />
8）考虑使用乐观锁定或使事务首先获得一个独占锁定。&nbsp; <br />
<br />
<strong>八如何对行、 表、数据库加锁</strong> <br />
1 如何锁一个表的某一行 <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/186542#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><font color="#000000">SET&nbsp;TRANSACTION&nbsp;ISOLATION&nbsp;LEVEL&nbsp;READ&nbsp;UNCOMMITTED &nbsp;&nbsp;</font>
    <li><font color="#000000">SELECT&nbsp;*&nbsp;FROM&nbsp;table1&nbsp;ROWLOCK&nbsp;WHERE&nbsp;A&nbsp;=&nbsp;</font><span class="string"><font color="#0000ff">'a1'</font></span><font color="#000000">&nbsp;&nbsp;</font></span> </li>
</ol>
</div>
<pre style="display: none" class="java" name="code_lighted">SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT * FROM table1 ROWLOCK WHERE A = 'a1'</pre>
<br />
2 锁定数据库的一个表 <br />
select col1 from 表 (tablockx) where 1=1 ; <br />
加锁后其它人不可操作，直到加锁用户解锁，用commit或rollback解锁 <br />
3.实例 <br />
建表 <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/186542#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><font color="#000000">create&nbsp;table&nbsp;table1(A&nbsp;varchar(</font><span class="number"><font color="#c00000">50</font></span><font color="#000000">)&nbsp;&nbsp;not&nbsp;&nbsp;</font><span class="keyword"><strong><font color="#7f0055">null</font></strong></span><font color="#000000">,&nbsp;B&nbsp;varchar(</font><span class="number"><font color="#c00000">50</font></span><font color="#000000">)&nbsp;,C&nbsp;varchar(</font><span class="number"><font color="#c00000">50</font></span><font color="#000000">)); &nbsp;&nbsp;</font>
    <li><font color="#000000">create&nbsp;table&nbsp;table2(D&nbsp;varchar(</font><span class="number"><font color="#c00000">50</font></span><font color="#000000">),E&nbsp;varchar(</font><span class="number"><font color="#c00000">50</font></span><font color="#000000">)) &nbsp;&nbsp;</font></span>
    <li><font color="#000000">insert&nbsp;table1&nbsp;(A,B,C)&nbsp;values(&#8216;a1&#8217;,&#8217;b1&#8217;,&#8217;c1&#8217;); &nbsp;&nbsp;</font>
    <li><font color="#000000">insert&nbsp;table1&nbsp;(A,B,C)&nbsp;values(&#8216;a2&#8217;,&#8217;b2&#8217;,&#8217;c2&#8217;); &nbsp;&nbsp;</font>
    <li><font color="#000000">insert&nbsp;table1&nbsp;(A,B,C)&nbsp;values(&#8216;a3&#8217;,&#8217;b3&#8217;,&#8217;c3&#8217;); &nbsp;&nbsp;</font>
    <li><font color="#000000">insert&nbsp;table2&nbsp;(D,E)&nbsp;values(&#8216;d1&#8217;,&#8217;e1&#8217;); &nbsp;&nbsp;</font>
    <li><font color="#000000">insert&nbsp;table2&nbsp;(D,E)&nbsp;values(&#8216;d2&#8217;,&#8217;e2&#8217;);&nbsp;&nbsp;</font> </li>
</ol>
</div>
<pre style="display: none" class="java" name="code_lighted">create table table1(A varchar(50)  not  null, B varchar(50) ,C varchar(50));
create table table2(D varchar(50),E varchar(50))
insert table1 (A,B,C) values(&#8216;a1&#8217;,&#8217;b1&#8217;,&#8217;c1&#8217;);
insert table1 (A,B,C) values(&#8216;a2&#8217;,&#8217;b2&#8217;,&#8217;c2&#8217;);
insert table1 (A,B,C) values(&#8216;a3&#8217;,&#8217;b3&#8217;,&#8217;c3&#8217;);
insert table2 (D,E) values(&#8216;d1&#8217;,&#8217;e1&#8217;);
insert table2 (D,E) values(&#8216;d2&#8217;,&#8217;e2&#8217;);</pre>
<br />
1）排它锁 <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/186542#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><font color="#000000">--&nbsp;A事务先更新table1表，在更新时，对其他事务进行排他 &nbsp;&nbsp;</font>
    <li><font color="#000000">begin&nbsp;tran &nbsp;&nbsp;</font>
    <li><font color="#000000">update&nbsp;table1&nbsp;set&nbsp;A=</font><span class="string"><font color="#0000ff">'aa'</font></span><font color="#000000">&nbsp;where&nbsp;B=</font><span class="string"><font color="#0000ff">'b2'</font></span><font color="#000000">； &nbsp;&nbsp;</font></span>
    <li><font color="#000000">waitfor&nbsp;delay&nbsp;</font><span class="string"><font color="#0000ff">'00:00:30'</font></span><font color="#000000">；&nbsp;--等待</font><span class="number"><font color="#c00000">30</font></span><font color="#000000">秒 &nbsp;&nbsp;</font></span>
    <li><font color="#000000">commit&nbsp;tran &nbsp;&nbsp;</font>
    <li><font color="#000000">--&nbsp;A事务先更新table2表 &nbsp;&nbsp;</font>
    <li><font color="#000000">begin&nbsp;tran &nbsp;&nbsp;</font>
    <li><font color="#000000">select&nbsp;*&nbsp;from&nbsp;table1&nbsp;where&nbsp;B=</font><span class="string"><font color="#0000ff">'b2'</font></span><font color="#000000">; &nbsp;&nbsp;</font></span>
    <li><font color="#000000">commit&nbsp;tran&nbsp;&nbsp;</font> </li>
</ol>
</div>
<pre style="display: none" class="java" name="code_lighted">-- A事务先更新table1表，在更新时，对其他事务进行排他
begin tran
update table1 set A='aa' where B='b2'；
waitfor delay '00:00:30'； --等待30秒
commit tran
-- A事务先更新table2表
begin tran
select * from table1 where B='b2';
commit tran</pre>
若同时执行上述两个事务，则select查询必须等待update执行完毕才能执行即要等待30秒 <br />
2）共享锁 <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/186542#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><font color="#000000">--&nbsp;A事务先查询table1表，在查询时，加共享锁，防止其他事务对该表进行修改操作 &nbsp;&nbsp;</font>
    <li><font color="#000000">begin&nbsp;tran &nbsp;&nbsp;</font>
    <li><font color="#000000">select&nbsp;*&nbsp;from&nbsp;table1&nbsp;holdlock&nbsp;where&nbsp;B=</font><span class="string"><font color="#0000ff">'b2'</font></span><font color="#000000">&nbsp;; &nbsp;&nbsp;</font></span>
    <li><font color="#000000">&nbsp;-holdlock人为加锁 &nbsp;&nbsp;</font>
    <li><font color="#000000">waitfor&nbsp;delay&nbsp;</font><span class="string"><font color="#0000ff">'00:00:30'</font></span><font color="#000000">;--等待</font><span class="number"><font color="#c00000">30</font></span><font color="#000000">秒 &nbsp;&nbsp;</font></span>
    <li><font color="#000000">commit&nbsp;tran &nbsp;&nbsp;</font>
    <li><font color="#000000">--&nbsp;A事务先查询table1表，后更改table1表 &nbsp;&nbsp;</font>
    <li><font color="#000000">begin&nbsp;tran &nbsp;&nbsp;</font>
    <li><font color="#000000">select&nbsp;A,C&nbsp;from&nbsp;table1&nbsp;where&nbsp;B=</font><span class="string"><font color="#0000ff">'b2'</font></span><font color="#000000">; &nbsp;&nbsp;</font></span>
    <li><font color="#000000">update&nbsp;table1&nbsp;set&nbsp;A=</font><span class="string"><font color="#0000ff">'aa'</font></span><font color="#000000">&nbsp;where&nbsp;B=</font><span class="string"><font color="#0000ff">'b2'</font></span><font color="#000000">; &nbsp;&nbsp;</font></span>
    <li><font color="#000000">commit&nbsp;tran&nbsp;&nbsp;</font> </li>
</ol>
</div>
<pre style="display: none" class="java" name="code_lighted">-- A事务先查询table1表，在查询时，加共享锁，防止其他事务对该表进行修改操作
begin tran
select * from table1 holdlock where B='b2' ;
-holdlock人为加锁
waitfor delay '00:00:30';--等待30秒
commit tran
-- A事务先查询table1表，后更改table1表
begin tran
select A,C from table1 where B='b2';
update table1 set A='aa' where B='b2';
commit tran</pre>
若并发执行上述两个事务，则B事务中的select查询可以执行,而update必须等待第一个事务释放共享锁转为排它锁后才能执行即要等待30秒 <br />
3）死锁 <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.javaeye.com/topic/186542#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" _counted="undefined" /></a></div>
</div>
<ol class="dp-j">
    <li><font color="#000000">--&nbsp;A事务先更新table1表，然后延时</font><span class="number"><font color="#c00000">30</font></span><font color="#000000">秒，再更新table2表； &nbsp;&nbsp;</font>
    <li><font color="#000000">begin&nbsp;tran &nbsp;&nbsp;</font>
    <li><font color="#000000">update&nbsp;table1&nbsp;set&nbsp;A=</font><span class="string"><font color="#0000ff">'aa'</font></span><font color="#000000">&nbsp;where&nbsp;B=</font><span class="string"><font color="#0000ff">'b2'</font></span><font color="#000000">; &nbsp;&nbsp;</font></span>
    <li><font color="#000000">--这将在&nbsp;Table1&nbsp;中生成排他行锁，直到事务完成后才会释放该锁。 &nbsp;&nbsp;</font>
    <li><font color="#000000">waitfor&nbsp;delay&nbsp;</font><span class="string"><font color="#0000ff">'00:00:30'</font></span><font color="#000000">; &nbsp;&nbsp;</font></span>
    <li><font color="#000000">--进入延时 &nbsp;&nbsp;</font>
    <li><font color="#000000">update&nbsp;table2&nbsp;set&nbsp;D=</font><span class="string"><font color="#0000ff">'d5'</font></span><font color="#000000">&nbsp;where&nbsp;E=</font><span class="string"><font color="#0000ff">'e1'</font></span><font color="#000000">&nbsp;; &nbsp;&nbsp;</font></span>
    <li><font color="#000000">commit&nbsp;tran &nbsp;&nbsp;</font>
    <li><font color="#000000">--&nbsp;B事务先更新table2表，然后延时</font><span class="number"><font color="#c00000">10</font></span><font color="#000000">秒，再更新table1表； &nbsp;&nbsp;</font></span>
    <li><font color="#000000">begin&nbsp;tran &nbsp;&nbsp;</font>
    <li><font color="#000000">update&nbsp;table2&nbsp;set&nbsp;D=</font><span class="string"><font color="#0000ff">'d5'</font></span><font color="#000000">&nbsp;where&nbsp;E=</font><span class="string"><font color="#0000ff">'e1'</font></span><font color="#000000">; &nbsp;&nbsp;</font></span>
    <li><font color="#000000">--这将在&nbsp;Table2&nbsp;中生成排他行锁，直到事务完成后才会释放该锁 &nbsp;&nbsp;</font>
    <li><font color="#000000">waitfor&nbsp;delay&nbsp;</font><span class="string"><font color="#0000ff">'00:00:10'</font></span><font color="#000000">&nbsp;&nbsp;</font></span>
    <li><font color="#000000">--进入延时 &nbsp;&nbsp;</font>
    <li><font color="#000000">update&nbsp;table1&nbsp;set&nbsp;A=</font><span class="string"><font color="#0000ff">'aa'</font></span><font color="#000000">&nbsp;where&nbsp;B=</font><span class="string"><font color="#0000ff">'b2'</font></span><font color="#000000">&nbsp;; &nbsp;&nbsp;</font></span>
    <li><font color="#000000">commit&nbsp;tran&nbsp;&nbsp;</font> </li>
</ol>
</div>
<pre style="display: none" class="java" name="code_lighted">-- A事务先更新table1表，然后延时30秒，再更新table2表；
begin tran
update table1 set A='aa' where B='b2';
--这将在 Table1 中生成排他行锁，直到事务完成后才会释放该锁。
waitfor delay '00:00:30';
--进入延时
update table2 set D='d5' where E='e1' ;
commit tran
-- B事务先更新table2表，然后延时10秒，再更新table1表；
begin tran
update table2 set D='d5' where E='e1';
--这将在 Table2 中生成排他行锁，直到事务完成后才会释放该锁
waitfor delay '00:00:10'
--进入延时
update table1 set A='aa' where B='b2' ;
commit tran</pre>
若并发执行上述两个事务，A,B两事务都要等待对方释放排他锁，这样便形成了死锁。 <br />
<br />
<strong>九、<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">sql</font></span></span><span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">server</font></span></span>提供的表级锁</strong> <br />
<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">sql</font></span></span><span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">server</font></span></span>所指定的表级锁定提示有如下几种 <br />
1． HOLDLOCK: 在该表上保持共享锁，直到整个事务结束，而不是在语句执行完立即释放所添加的锁。&nbsp; <br />
2． NOLOCK：不添加共享锁和排它锁，当这个选项生效后，可能读到未提交读的数据或&#8220;脏数据&#8221;，这个选项仅仅应用于SELECT语句。&nbsp;&nbsp; <br />
3． PAGLOCK：指定添加页锁（否则通常可能添加表锁） <br />
4． READCOMMITTED用与运行在提交读隔离级别的事务相同的锁语义执行扫描。默认情况下，<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">Server</font></span></span> 2000 在此隔离级别上操作。 <br />
5． READPAST: 跳过已经加锁的数据行，这个选项将使事务读取数据时跳过那些已经被其他事务锁定的数据行，而不是阻塞直到其他事务释放锁，READPAST仅仅应用于READ COMMITTED隔离性级别下事务操作中的SELECT语句操作 <br />
6． READUNCOMMITTED：等同于NOLOCK。&nbsp;&nbsp;&nbsp; <br />
7． REPEATABLEREAD：设置事务为可重复读隔离性级别。&nbsp; <br />
8． ROWLOCK：使用行级锁，而不使用粒度更粗的页级锁和表级锁。 <br />
9． SERIALIZABLE：用与运行在可串行读隔离级别的事务相同的锁语义执行扫描。等同于 HOLDLOCK。 <br />
&nbsp; 10． TABLOCK：指定使用表级锁，而不是使用行级或页面级的锁，<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">SQL</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">Server</font></span></span>在该语句执行完后释放这个锁，而如果同时指定了HOLDLOCK，该锁一直保持到这个事务结束。 <br />
11． TABLOCKX：指定在表上使用排它锁，这个锁可以阻止其他事务读或更新这个表的数据，直到这个语句或整个事务结束。 <br />
12． UPDLOCK ：指定在读表中数据时设置更新锁（update lock）而不是设置共享锁，该锁一直保持到这个语句或整个事务结束，使用UPDLOCK的作用是允许用户先读取数据（而且不阻塞其他用户读数据），并且保证在后来再更新数据时，这一段时间内这些数据没有被其他用户修改 <br />
SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表，但不能更新删除 <br />
SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除 <br />
<br />
<strong>十、应用程序锁</strong> <br />
<br />
应用程序锁就是客户端代码生成的锁，而不是<span class="hilite1"><span class="hilite1"><font style="background-color: #ffff00">sql</font></span></span> <span class="hilite2"><span class="hilite2"><font style="background-color: #55ff55">server</font></span></span>本身生成的锁处理应用程序锁的两个系统存储过程 <br />
sp_getapplock： 锁定应用程序资源 <br />
sp_releaseapplock： 为应用程序资源解锁 
<img src ="http://www.blogjava.net/tinysun/aggbug/311956.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-02-04 14:38 <a href="http://www.blogjava.net/tinysun/archive/2010/02/04/311956.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>sqlserver乐观锁与悲观锁实例</title><link>http://www.blogjava.net/tinysun/archive/2010/02/04/311952.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 04 Feb 2010 06:22:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/02/04/311952.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/311952.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/02/04/311952.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/311952.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/311952.html</trackback:ping><description><![CDATA[<p>在实际的多用户并发访问的生产环境里边，我们经常要尽可能的保持数据的一致性。而其中最典型的例子就是我们从表里边读取数据，检查验证后对数据进行修改，然后写回到数据库中。在读取和写入的过程中，假如在多用户并发的环境里边，其他用户已经把你要修改的数据进行了修改是非常有可能发生的情况，这样就造成了数据的不一致性。解决这样的办法，SQL SERVER提出了乐观锁定和悲观锁定的概念，下边我以一个实例来说明如何使用乐观锁定和悲观锁定来解决这样的问题。 </p>
<p>/* 建立测试表:Card,代表一个真实的卡库,供用户注册.用户要从里边选出一个未使用的卡，也就是F_Flag=0的卡，给用户注册：更新F_Name,F_Time,F_Flag字段. 假如出现两个用户同时更新一张卡的情况，是不能容忍的，也就是我们所说的数据不一致行。*/</p>
<p>create table Card(F_CardNO varchar(20),F_Name varchar(20),F_Flag bit,F_Time datetime)<br />
Go<br />
insert Card(F_CardNo,F_Flag) select '1111-1111',0<br />
insert Card(F_CardNo,F_Flag) select '1111-1112',0<br />
insert Card(F_CardNo,F_Flag) select '1111-1113',0<br />
insert Card(F_CardNo,F_Flag) select '1111-1114',0<br />
insert Card(F_CardNo,F_Flag) select '1111-1115',0<br />
insert Card(F_CardNo,F_Flag) select '1111-1116',0<br />
insert Card(F_CardNo,F_Flag) select '1111-1117',0<br />
insert Card(F_CardNo,F_Flag) select '1111-1118',0<br />
insert Card(F_CardNo,F_Flag) select '1111-1119',0<br />
insert Card(F_CardNo,F_Flag) select '1111-1110',0<br />
Go</p>
<p>-- 下边是我们经常使用的更新方案如下:</p>
<p>declare @CardNo varchar(20)<br />
Begin Tran</p>
<p>-- 选择一张未使用的卡<br />
select top 1 @CardNo=F_CardNo<br />
from Card where F_Flag=0</p>
<p>-- 延迟50秒，模拟并发访问.<br />
waitfor delay '000:00:50'</p>
<p>-- 把刚才选择出来的卡进行注册.</p>
<p>update Card<br />
set F_Name=user,<br />
F_Time=getdate(),<br />
F_Flag=1<br />
where F_CardNo=@CardNo</p>
<p>commit</p>
<p>问题:假如我们在同一窗口执行同一段代码，但是去掉了waitfor delay子句.两边执行完毕后 我们发现尽管执行了两次注册，但是只注册了一张卡，也就是两个人注册了同一张卡. </p>
<p>悲观锁定解决方案</p>
<p>-- 我们只要对上边的代码做微小的改变就可以实现悲观的锁定.</p>
<p>declare @CardNo varchar(20)<br />
Begin Tran</p>
<p>-- 选择一张未使用的卡<br />
select top 1 @CardNo=F_CardNo<br />
from Card with (UPDLOCK) where F_Flag=0</p>
<p>-- 延迟50秒，模拟并发访问.<br />
waitfor delay '000:00:50'</p>
<p>-- 把刚才选择出来的卡进行注册.</p>
<p>update Card<br />
set F_Name=user,<br />
F_Time=getdate(),<br />
F_Flag=1<br />
where F_CardNo=@CardNo</p>
<p>commit</p>
<p>注重其中的区别了吗?with(updlock),是的,我们在查询的时候使用了with (UPDLOCK)选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对次记录进行更新.注重更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.假如我们在另外一个窗口执行此代码,同样不加waifor delay子句.两边执行完毕后,我们发现成功的注册了两张卡.可能我们已经发现了悲观锁定的缺点:当一个用户进行更新的事务的时候,其他更新用户必须排队等待,即使那个用户更新的不是同一条记录. </p>
<p>乐观锁定解决方案</p>
<p>-- 首先我们在Card表里边加上一列F_TimeStamp 列,该列是varbinary(8)类型.但是在更新的时候这个值会自动增长.</p>
<p>alter table Card add F_TimeStamp timestamp not null</p>
<p>-- 悲观锁定<br />
declare @CardNo varchar(20)<br />
declare @timestamp varbinary(8)<br />
declare @rowcount int</p>
<p>Begin Tran</p>
<p>-- 取得卡号和原始的时间戳值<br />
select top 1 @CardNo=F_CardNo,<br />
@timestamp=F_TimeStamp<br />
from Card<br />
where F_Flag=0</p>
<p>-- 延迟50秒，模拟并发访问.<br />
waitfor delay '000:00:50'</p>
<p>-- 注册卡,但是要比较时间戳是否发生了变化.假如没有发生变化.更新成功.假如发生变化,更新失败.</p>
<p>update Card<br />
set F_Name=user,<br />
F_Time=getdate(),<br />
F_Flag=1<br />
where F_CardNo=@CardNo and F_TimeStamp=@timestamp<br />
set @rowcount=@@rowcount<br />
if @rowcount=1<br />
begin<br />
print '更新成功!'<br />
commit<br />
end<br />
else if @rowcount=0<br />
begin<br />
if exists(select 1 from Card where F_CardNo=@CardNo)<br />
begin<br />
print '此卡已经被另外一个用户注册！'<br />
rollback tran<br />
end<br />
else<br />
begin<br />
print '并不存在此卡!'<br />
rollback tran<br />
end<br />
end</p>
<p>在另外一个窗口里边执行没有waitfor的代码,注册成功后,返回原来的窗口,我们就会发现到时间后它显示的提示是此卡以被另外一个用户注册的提示.很明显,这样我们也可以避免两个用户同时注册一张卡的现象的出现.同时,使用这种方法的另外一个好处是没有使用更新锁,这样增加的系统的并发处理能力.</p>
<p>上边我具体介绍了乐观锁定和悲观锁定的使用方法,在实际生产环境里边,假如并发量不大,我们完全可以使用悲观锁定的方法,因为这种方法使用起来非常方便和简单.但是假如系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.</p>
<p><br />
本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/gudenren/archive/2009/07/31/4397291.aspx</p>
<img src ="http://www.blogjava.net/tinysun/aggbug/311952.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-02-04 14:22 <a href="http://www.blogjava.net/tinysun/archive/2010/02/04/311952.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>锁_数据库原理</title><link>http://www.blogjava.net/tinysun/archive/2010/02/04/311949.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 04 Feb 2010 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/02/04/311949.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/311949.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/02/04/311949.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/311949.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/311949.html</trackback:ping><description><![CDATA[<li><span><span>数据库--锁 &nbsp;&nbsp;</span></span></li>
<li><span class="number">1</span><span>#定义 &nbsp;&nbsp;</span></span></li>
<li><span>数据库和操作系统一样，是一个多用户使用的共享资源。当多个用户并发地存取数据时，在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据，破坏数据库的一致性。 &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;加锁是实现数据库并发控制的一个非常重要的技术。在实际应用中经常会遇到的与锁相关的异常情况，当两个事务需要一组有冲突的锁，而不能将事务继续下去的话，就会出现死锁，严重影响应用的正常执行。 &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;在数据库中有两种基本的锁类型：排它锁（Exclusive&nbsp;Locks，即X锁）和共享锁（Share&nbsp;Locks，即S锁）。 &nbsp;&nbsp;</span></li>
<li><span>@当数据对象被加上排它锁时，其他的事务不能对它读取和修改。 &nbsp;&nbsp;</span></li>
<li><span>@加了共享锁的数据对象可以被其他事务读取，但不能修改。 &nbsp;&nbsp;</span></li>
<li><span>数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。 &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span class="number">2</span><span>#悲观锁，正如其名，它指的是对数据被外界（包括本系统当前的其他事务，以及来自外部系统的事务处理）修改持保守态度，因此，在整个数据处理过程中，将数据处于锁定状态。悲观锁的实现，往往依靠数据库提供的锁机制（也只有数据库层提供的锁机制才能真正保证数据访问的排他性，否则，即使在本系统中实现了加锁机制，也无法保证外部系统不会修改数据）。 &nbsp;&nbsp;</span></span></li>
<li><span>一个典型的倚赖数据库的悲观锁调用： &nbsp;&nbsp;</span></li>
<li><span>select&nbsp;*&nbsp;from&nbsp;account&nbsp;where&nbsp;name=&#8221;Erica&#8221;&nbsp;</span><span class="keyword">for</span><span>&nbsp;update &nbsp;&nbsp;</span></span></li>
<li><span>这条&nbsp;sql&nbsp;语句锁定了&nbsp;account&nbsp;表中所有符合检索条件（name=&#8220;Erica&#8221;）的记录。本次事务提交之前（事务提交时会释放事务过程中的锁），外界无法修改这些记录。 &nbsp;&nbsp;</span></li>
<li><span>悲观锁大多数情况下依靠数据库的锁机制实现，以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销，特别是对长事务而言，这样的开销往往无法承受。如一个金融系统，当某个操作员读取用户的数据，并在读出的用户数据的基础上进行修改时（如更改用户帐户余额），如果采用悲观锁机制，也就意味着整个操作过程中（从操作员读出数据、开始修改直至提交修改结果的全过程，甚至还包括操作员中途去煮咖啡的时间），数据库记录始终处于加锁状态，可以想见，如果面对几&nbsp;百上千个并发，这样的情况将导致怎样的后果。 &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span class="number">3</span><span>#乐观锁机制在一定程度上解决了这个问题。乐观锁，大多是基于数据版本（Version）记录机制实现。何谓数据版本？即为数据增加一个版本标识，在基于数据库表的版本解决方案中，一般是通过为数据库表增加一个&nbsp;&#8220;version&#8221;&nbsp;字段来实现读取出数据时，将此版本号一同读出，之后更新时，对此版本号加一。此时，将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对，如果提交的数据版本号大于数据库表当前版本号，则予以更新，否则认为是过期数据。 &nbsp;&nbsp;</span></span></li>
<li><span>对于上面修改用户帐户信息的例子而言，假设数据库中帐户信息表中有一个&nbsp;version&nbsp;字段，当前值为&nbsp;</span><span class="number">1</span><span>&nbsp;；而当前帐户余额字段（&nbsp;balance&nbsp;）为&nbsp;$</span><span class="number">100</span><span>&nbsp;。 &nbsp;&nbsp;</span></span></li>
<li><span class="number">1</span><span>&nbsp;操作员&nbsp;A&nbsp;此时将其读出（&nbsp;version=</span><span class="number">1</span><span>&nbsp;），并从其帐户余额中扣除&nbsp;$</span><span class="number">50</span><span>（&nbsp;$</span><span class="number">100</span><span>-$</span><span class="number">50</span><span>&nbsp;）。 &nbsp;&nbsp;</span></span></li>
<li><span class="number">2</span><span>&nbsp;在操作员&nbsp;A&nbsp;操作的过程中，操作员&nbsp;B&nbsp;也读入此用户信息（&nbsp;version=</span><span class="number">1</span><span>&nbsp;），并从其帐户余额中扣除&nbsp;$</span><span class="number">20</span><span>&nbsp;（&nbsp;$</span><span class="number">100</span><span>-$</span><span class="number">20</span><span>&nbsp;）。 &nbsp;&nbsp;</span></span></li>
<li><span class="number">3</span><span>&nbsp;操作员&nbsp;A&nbsp;完成了修改工作，将数据版本号加一（&nbsp;version=</span><span class="number">2</span><span>&nbsp;），连同帐户扣除后余额（&nbsp;balance=$</span><span class="number">50</span><span>&nbsp;），提交至数据库更新，此时由于提交数据版本大于数据库记录当前版本，数据被更新，数据库记录&nbsp;version&nbsp;更新为&nbsp;</span><span class="number">2</span><span>&nbsp;。 &nbsp;&nbsp;</span></span></li>
<li><span class="number">4</span><span>&nbsp;操作员&nbsp;B&nbsp;完成了操作，也将版本号加一（&nbsp;version=</span><span class="number">2</span><span>&nbsp;）试图向数据库提交数据（&nbsp;balance=$</span><span class="number">80</span><span>&nbsp;），但此时比对数据库记录版本时发现，操作员&nbsp;B&nbsp;提交的数据版本号为&nbsp;</span><span class="number">2</span><span>&nbsp;，数据库记录当前版本也为&nbsp;</span><span class="number">2</span><span>&nbsp;，不满足&nbsp;&#8220;&nbsp;提交版本必须大于记录当前版本才能执行更新&nbsp;&#8220;&nbsp;的乐观锁策略，因此，操作员&nbsp;B&nbsp;的提交被驳回。这样，就避免了操作员&nbsp;B&nbsp;用基于version=</span><span class="number">1</span><span>的旧数据修改的结果覆盖操作员&nbsp;A&nbsp;的操作结果的可能。 &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span class="number">4</span><span>#死锁 &nbsp;&nbsp;</span></span></li>
<li><span>死锁的第一种情况 &nbsp;&nbsp;</span></li>
<li><span>一个用户A&nbsp;访问表A(锁住了表A),然后又访问表B；另一个用户B&nbsp;访问表B(锁住了表B)，然后企图访问表A；这时用户A由于用户B已经锁住表B，它必须等待用户B释放表B才能继续，同样用户B要等用户A释放表A才能继续，这就死锁就产生了。 &nbsp;&nbsp;</span></li>
<li><span>解决方法： &nbsp;&nbsp;</span></li>
<li><span>这种死锁比较常见，是由于程序的BUG产生的，除了调整的程序的逻辑没有其它的办法。仔细分析程序的逻辑，对于数据库的多表操作时，尽量按照相同的顺序进行处理，尽量避免同时锁定两个资源，如操作A和B两张表时，总是按先A后B的顺序处理，&nbsp;必须同时锁定两个资源时，要保证在任何时刻都应该按照相同的顺序来锁定资源。 &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>死锁的第二种情况 &nbsp;&nbsp;</span></li>
<li><span>用户A查询一条纪录，然后修改该条纪录；这时用户B修改该条纪录，这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁，而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁，而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁，于是出现了死锁。这种死锁比较隐蔽，但在稍大点的项目中经常发生。如在某项目中，页面上的按钮点击后，没有使按钮立刻失效，使得用户会多次快速点击同一按钮，这样同一段代码对数据库同一条记录进行多次操作，很容易就出现这种死锁的情况。 &nbsp;&nbsp;</span></li>
<li><span>解决方法： &nbsp;&nbsp;</span></li>
<li><span class="number">1</span><span>、对于按钮等控件，点击后使其立刻失效，不让用户重复点击，避免对同时对同一条记录操作。 &nbsp;&nbsp;</span></span></li>
<li><span class="number">2</span><span>、使用乐观锁进行控制。 &nbsp;&nbsp;</span></span></li>
<li><span class="number">3</span><span>、使用悲观锁进行控制。 &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>死锁的第三种情况 &nbsp;&nbsp;</span></li>
<li><span>如果在事务中执行了一条不满足条件的update语句，则执行全表扫描，把行级锁上升为表级锁，多个这样的事务执行后，就很容易产生死锁和阻塞。类似的情况还有当表中的数据量非常庞大而索引建的过少或不合适的时候，使得经常发生全表扫描，最终应用系统会越来越慢，最终发生阻塞或死锁。 &nbsp;&nbsp;</span></li>
<li><span>解决方法： &nbsp;&nbsp;</span></li>
<li><span>SQL语句中不要使用太复杂的关联多表的查询；使用&#8220;执行计划&#8221;对SQL语句进行分析，对于有全表扫描的SQL语句，建立相应的索引进行优化。&nbsp;&nbsp;&nbsp;</span></li>
<img src ="http://www.blogjava.net/tinysun/aggbug/311949.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-02-04 14:16 <a href="http://www.blogjava.net/tinysun/archive/2010/02/04/311949.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HASH JOIN ,MERGE JOIN ,NESTED LOOP</title><link>http://www.blogjava.net/tinysun/archive/2010/02/01/311508.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Mon, 01 Feb 2010 07:04:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/02/01/311508.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/311508.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/02/01/311508.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/311508.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/311508.html</trackback:ping><description><![CDATA[<table style="border-collapse: collapse; word-wrap: break-word" border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td align="center">
            <table style="border-collapse: collapse; word-wrap: break-word" border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td width="100%">
                        <div style="margin: 15px" id="art" width="100%">
                        <div><font size="3">对于Oracle的一些基本知识要加强学习，我从网上抄了三个人对这个题目的见解下来，供已参考。</font></div>
                        <div>&nbsp;</div>
                        <div>
                        <div id="postmessage_272916" class="t_msgfont">NESTED LOOP:<br />
                        <br />
                        对于被连接的数据子集较小的情况，嵌套循环连接是个较好的选择。在嵌套循环中，内<br />
                        表被外表驱动，外表返回的每一行都要在内表中检索找到与它匹配的行，因此整个查询返回<br />
                        的结果集不能太大（大于1 万不适合），要把返回子集较小表的作为外表（CBO 默认外表是<br />
                        驱动表），而且在内表的连接字段上一定要有索引。当然也可以用ORDERED 提示来改变CBO<br />
                        默认的驱动表，使用USE_NL(table_name1 table_name2)可是强制CBO 执行嵌套循环连接。<br />
                        <br />
                        HASH JOIN :<br />
                        <br />
                        散列连接是CBO 做大数据集连接时的常用方式，优化器使用两个表中较小的表（或数据<br />
                        源）利用连接键在内存中建立散列表，然后扫描较大的表并探测散列表，找出与散列表匹配<br />
                        的行。<br />
                        这种方式适用于较小的表完全可以放于内存中的情况，这样总成本就是访问两个表的成<br />
                        本之和。但是在表很大的情况下并不能完全放入内存，这时优化器会将它分割成若干不同的<br />
                        分区，不能放入内存的部分就把该分区写入磁盘的临时段，此时要有较大的临时段从而尽量<br />
                        提高I/O 的性能。<br />
                        也可以用USE_HASH(table_name1 table_name2)提示来强制使用散列连接。如果使用散<br />
                        列连接HASH_AREA_SIZE 初始化参数必须足够的大，如果是9i，Oracle建议使用SQL工作区<br />
                        自动管理，设置WORKAREA_SIZE_POLICY 为AUTO，然后调整PGA_AGGREGATE_TARGET 即可。<br />
                        <br />
                        排序合并连接<br />
                        <br />
                        通常情况下散列连接的效果都比排序合并连接要好，然而如果行源已经被排过序，在执<br />
                        行排序合并连接时不需要再排序了，这时排序合并连接的性能会优于散列连接。可以使用<br />
                        USE_MERGE(table_name1 table_name2)来强制使用排序合并连接</div>
                        <div class="t_msgfont">&nbsp;</div>
                        <div class="t_msgfont">
                        <hr id="null" color="#ff0000" />
                        </div>
                        <div class="t_msgfont">
                        <hr id="null" color="#ff0000" />
                        </div>
                        <div class="t_msgfont">&nbsp;</div>
                        <div class="t_msgfont"><font face="Verdana">Nested loop join:<br />
                        <br />
                        步骤：确定一个驱动表(outer table)，另一个表为inner table，驱动表中的每一行与inner表中的相应记录JOIN。类似一个嵌套的循环。适用于驱动表的记录集比较小（&lt;10000）而且inner表需要有有效的访问方法（Index）。需要注意的是：JOIN的顺序很重要，驱动表的记录集一定要小，返回结果集的响应时间是最快的。<br />
                        <br />
                        cost = outer access cost + (inner access cost * outer cardinality)<br />
                        <br />
                        <br />
                        | 2 | NESTED LOOPS | | 3 | 141 | 7 (15)|<br />
                        | 3 | TABLE ACCESS FULL | EMPLOYEES | 3 | 60 | 4 (25)|<br />
                        | 4 | TABLE ACCESS BY INDEX ROWID| JOBS | 19 | 513 | 2 (50)|<br />
                        | 5 | INDEX UNIQUE SCAN | JOB_ID_PK | 1 | | |<br />
                        <br />
                        <br />
                        EMPLOYEES为outer table, JOBS为inner table.<br />
                        <br />
                        Hash join<br />
                        <br />
                        步骤：将两个表中较小的一个在内存中构造一个HASH表（对JOIN KEY），扫描另一个表，同样对JOIN KEY进行HASH后探测是否可以JOIN。适用于记录集比较大的情况。需要注意的是：如果HASH表太大，无法一次构造在内存中，则分成若干个partition，写入磁盘的temporary segment，则会多一个写的代价，会降低效率。<br />
                        <br />
                        cost = (outer access cost * # of hash partitions) + inner access cost<br />
                        --------------------------------------------------------------------------<br />
                        | Id | Operation | Name | Rows | Bytes | Cost (%CPU)|<br />
                        --------------------------------------------------------------------------<br />
                        | 0 | SELECT STATEMENT | | 665 | 13300 | 8 (25)|<br />
                        | 1 | HASH JOIN | | 665 | 13300 | 8 (25)|<br />
                        | 2 | TABLE ACCESS FULL | ORDERS | 105 | 840 | 4 (25)|<br />
                        | 3 | TABLE ACCESS FULL | ORDER_ITEMS | 665 | 7980 | 4 (25)|<br />
                        --------------------------------------------------------------------------<br />
                        <br />
                        <br />
                        ORDERS为HASH TABLE，ORDER_ITEMS扫描<br />
                        <br />
                        Sort merge join<br />
                        <br />
                        步骤：将两个表排序，然后将两个表合并。通常情况下，只有在以下情况发生时，才会使用此种JOIN方式：<br />
                        <br />
                        1.RBO模式<br />
                        <br />
                        2.不等价关联(&gt;,&lt;,&gt;=,&lt;=,&lt;&gt;)<br />
                        <br />
                        3.HASH_JOIN_ENABLED=false<br />
                        <br />
                        4.数据源已排序<br />
                        <br />
                        cost = (outer access cost * # of hash partitions) + inner access cost</font></div>
                        <div class="t_msgfont"><font face="Verdana"></font>&nbsp;</div>
                        <div class="t_msgfont">
                        <hr id="null" color="#ff0000" />
                        </div>
                        <div class="t_msgfont">
                        <hr id="null" color="#ff0000" />
                        </div>
                        <div class="t_msgfont"><font face="Verdana"></font>&nbsp;</div>
                        <div class="t_msgfont">转载biti的一段话: &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; 举例，表连接返回一条记录 &nbsp; <br />
                        &nbsp; 存在两个表，一个 &nbsp; 10条记录 &nbsp; ，一个1000万条记录 &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; 若2表都存在连接字段索引，若以小表为驱动表，则 &nbsp; <br />
                        &nbsp; 代价： &nbsp; &nbsp; <br />
                        &nbsp; 10* &nbsp; (通过索引在大表查询一条记录的代价) &nbsp; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; 若以大表为驱动表： &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; 1000万 &nbsp; * &nbsp; (通过索引在小表中查询一条记录的代价) &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; 通过索引获取一条记录，10rows的表，代价通常在 &nbsp; 3 &nbsp; blocks &nbsp; &nbsp; <br />
                        &nbsp; 索引2块，表一块 &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; 而如果是1000万的表，索引可能达到4块表一块 &nbsp; <br />
                        &nbsp; 这样一来参考上面的计算，你说哪个更好？很显然！ &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; 小表查询参考 &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; SQL&gt; &nbsp; create &nbsp; table &nbsp; test &nbsp; as &nbsp; select &nbsp; * &nbsp; from &nbsp; all_objects &nbsp; where &nbsp; rownum &nbsp; &lt; &nbsp; 11; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; Table &nbsp; created. &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; SQL&gt; &nbsp; create &nbsp; index &nbsp; test_index &nbsp; on &nbsp; test(object_id); &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; Index &nbsp; created. &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; SQL&gt; &nbsp; select &nbsp; object_id &nbsp; from &nbsp; test; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; OBJECT_ID &nbsp; <br />
                        &nbsp; ---------- &nbsp; <br />
                        &nbsp; 18159 &nbsp; <br />
                        &nbsp; 7781 &nbsp; <br />
                        &nbsp; 4841 &nbsp; <br />
                        &nbsp; 19891 &nbsp; <br />
                        &nbsp; 22549 &nbsp; <br />
                        &nbsp; 17099 &nbsp; <br />
                        &nbsp; 17712 &nbsp; <br />
                        &nbsp; 4287 &nbsp; <br />
                        &nbsp; 10107 &nbsp; <br />
                        &nbsp; 19135 &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; 10 &nbsp; rows &nbsp; selected. &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; Execution &nbsp; Plan &nbsp; <br />
                        &nbsp; ---------------------------------------------------------- &nbsp; <br />
                        &nbsp; 0 &nbsp; SELECT &nbsp; STATEMENT &nbsp; Optimizer=CHOOSE &nbsp; <br />
                        &nbsp; 1 &nbsp; 0 &nbsp; TABLE &nbsp; ACCESS &nbsp; (FULL) &nbsp; OF &nbsp; 'TEST' &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; Statistics &nbsp; <br />
                        &nbsp; ---------------------------------------------------------- &nbsp; <br />
                        &nbsp; 0 &nbsp; recursive &nbsp; calls &nbsp; <br />
                        &nbsp; 12 &nbsp; db &nbsp; block &nbsp; gets &nbsp; <br />
                        &nbsp; 6 &nbsp; consistent &nbsp; gets &nbsp; <br />
                        &nbsp; 0 &nbsp; physical &nbsp; reads &nbsp; <br />
                        &nbsp; 0 &nbsp; redo &nbsp; size &nbsp; <br />
                        &nbsp; 736 &nbsp; bytes &nbsp; sent &nbsp; via &nbsp; SQL*Net &nbsp; to &nbsp; client &nbsp; <br />
                        &nbsp; 425 &nbsp; bytes &nbsp; received &nbsp; via &nbsp; SQL*Net &nbsp; from &nbsp; client &nbsp; <br />
                        &nbsp; 2 &nbsp; SQL*Net &nbsp; roundtrips &nbsp; to/from &nbsp; client &nbsp; <br />
                        &nbsp; 0 &nbsp; sorts &nbsp; (memory) &nbsp; <br />
                        &nbsp; 0 &nbsp; sorts &nbsp; (disk) &nbsp; <br />
                        &nbsp; 10 &nbsp; rows &nbsp; processed &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; SQL&gt; &nbsp; select &nbsp; * &nbsp; from &nbsp; test &nbsp; where &nbsp; object_id &nbsp; = &nbsp; 4287; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; OWNER &nbsp; OBJECT_NAME &nbsp; <br />
                        &nbsp; ------------------------------ &nbsp; ------------------------------ &nbsp; <br />
                        &nbsp; SUBOBJECT_NAME &nbsp; OBJECT_ID &nbsp; DATA_OBJECT_ID &nbsp; OBJECT_TYPE &nbsp; <br />
                        &nbsp; ------------------------------ &nbsp; ---------- &nbsp; -------------- &nbsp; ------------------ &nbsp; <br />
                        &nbsp; CREATED &nbsp; LAST_DDL_ &nbsp; TIMESTAMP &nbsp; STATUS &nbsp; T &nbsp; G &nbsp; S &nbsp; <br />
                        &nbsp; --------- &nbsp; --------- &nbsp; ------------------- &nbsp; ------- &nbsp; - &nbsp; - &nbsp; - &nbsp; <br />
                        &nbsp; SYS &nbsp; /1033c8a_SqlTypeWithMethods &nbsp; <br />
                        &nbsp; 4287 &nbsp; JAVA &nbsp; CLASS &nbsp; <br />
                        &nbsp; 14-NOV-00 &nbsp; 03-JUL-03 &nbsp; 2003-07-03:11:18:19 &nbsp; INVALID &nbsp; N &nbsp; N &nbsp; N &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; Execution &nbsp; Plan &nbsp; <br />
                        &nbsp; ---------------------------------------------------------- &nbsp; <br />
                        &nbsp; 0 &nbsp; SELECT &nbsp; STATEMENT &nbsp; Optimizer=CHOOSE &nbsp; <br />
                        &nbsp; 1 &nbsp; 0 &nbsp; TABLE &nbsp; ACCESS &nbsp; (BY &nbsp; INDEX &nbsp; ROWID) &nbsp; OF &nbsp; 'TEST' &nbsp; <br />
                        &nbsp; 2 &nbsp; 1 &nbsp; INDEX &nbsp; (RANGE &nbsp; SCAN) &nbsp; OF &nbsp; 'TEST_INDEX' &nbsp; (NON-UNIQUE) &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; &nbsp; <br />
                        &nbsp; Statistics &nbsp; <br />
                        &nbsp; ---------------------------------------------------------- &nbsp; <br />
                        &nbsp; 0 &nbsp; recursive &nbsp; calls &nbsp; <br />
                        &nbsp; 0 &nbsp; db &nbsp; block &nbsp; gets &nbsp; <br />
                        &nbsp; 3 &nbsp; consistent &nbsp; gets &nbsp; <br />
                        &nbsp; 0 &nbsp; physical &nbsp; reads &nbsp; <br />
                        &nbsp; 0 &nbsp; redo &nbsp; size &nbsp; <br />
                        &nbsp; 1157 &nbsp; bytes &nbsp; sent &nbsp; via &nbsp; SQL*Net &nbsp; to &nbsp; client &nbsp; <br />
                        &nbsp; 425 &nbsp; bytes &nbsp; received &nbsp; via &nbsp; SQL*Net &nbsp; from &nbsp; client &nbsp; <br />
                        &nbsp; 2 &nbsp; SQL*Net &nbsp; roundtrips &nbsp; to/from &nbsp; client &nbsp; <br />
                        &nbsp; 0 &nbsp; sorts &nbsp; (memory) &nbsp; <br />
                        &nbsp; 0 &nbsp; sorts &nbsp; (disk) &nbsp; <br />
                        &nbsp; 1 &nbsp; rows &nbsp; processed&nbsp;&nbsp; <br />
                        </div>
                        </div>
                        </div>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p style="line-height: 150%; margin: 5px"></p>
            </td>
        </tr>
        <tr>
            <td height="25">&nbsp;<font color="#000099"><strong>TAG</strong></font> <a style="color: #ff0000" href="http://blog.chinaunix.net/tag.php?q=%B1%ED%C1%AA%BD%D3" target="_blank">表联接</a> <a style="color: #ff0000" href="http://blog.chinaunix.net/tag.php?q=oracle" target="_blank">oracle</a> <a style="color: #ff0000" href="http://blog.chinaunix.net/tag.php?q=join" target="_blank">join</a> </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/tinysun/aggbug/311508.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-02-01 15:04 <a href="http://www.blogjava.net/tinysun/archive/2010/02/01/311508.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是持久化和对象关系映射ORM技术 </title><link>http://www.blogjava.net/tinysun/archive/2009/04/23/267162.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 23 Apr 2009 07:34:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2009/04/23/267162.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/267162.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2009/04/23/267162.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/267162.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/267162.html</trackback:ping><description><![CDATA[<div>何谓&#8220;持久化&#8221;<br />
持久（Persistence），即把数据（如内存中的对象）保存到可永久保存的存储设备中（如磁盘）。持久化的主要应用是将内存中的数据存储在关系型的数据库中，当然也可以存储在磁盘文件中、XML数据文件中等等。
<p>何谓&#8220;持久层&#8221;<br />
持久层（Persistence Layer），即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面，将数据使用者和数据实体相关联。</p>
<p>何谓&#8220;对象数据映射（ORM）&#8221;<br />
ORM-Object/Relational Mapper，即&#8220;对象-关系型数据映射组件&#8221;。对于O/R，即 Object（对象）和 Relational（关系型数据），表示必须同时使用面向对象和关系型数据进行开发。</p>
<p>备注：建模领域中的 ORM 为 Object/Role Modeling（对象角色建模）。另外这里是&#8220;O/R Mapper&#8221;而非&#8220;O/R Mapping&#8221;。相对来讲，O/R Mapping 描述的是一种设计思想或者实现机制，而 O/R Mapper指以O/R原理设计的持久化框架（Framework），包括 O/R机制还有 SQL自生成，事务处理，Cache管理等。</p>
<p><br />
除了 ORM 技术，还有以下几种持久化技术</p>
<p>主动域对象模式<br />
它是在实现中封装了关系数据模型和数据访问细节的一种形式。在 J2EE 架构中，EJB 组件分为会话 EJB 和实体 EJB。会话 EJB 通常实现业务逻辑，而实体 EJB 表示业务实体。实体 EJB 又分为两种：由 EJB 本身管理持久化，即 BMP（Bean-Managed Persistence）；有 EJB 容器管理持久化，即 CMP（Container-Managed Persistence）。BM P就是主动域对象模式的一个例子，BMP 表示由实体 EJB 自身管理数据访问细节。<br />
主动域对象本身位于业务逻辑层，因此采用主动域对象模式时，整个应用仍然是三层应用结构，并没有从业务逻辑层分离出独立的持久化层。</p>
<p>JDO 模式<br />
Java Data Objects（JDO）是 SUN 公司制定的描述对象持久化语义的标准API。严格的说，JDO 并不是对象-关系映射接口，因为它支持把对象持久化到任意一种存储系统中，包括 关系数据库、面向对象的数据库、基于 XML 的数据库，以及其他专有存储系统。由于关系数据库是目前最流行的存储系统，许多 JDO 的实现都包含了对象-关系映射服务。</p>
<p>CMP 模式<br />
在 J2EE 架构中，CMP（Container-Managed Persistence）表示由 EJB 容器来管理实体 EJB 的持久化，EJB 容器封装了对象-关系的映射及数据访问细节。CMP 和 ORM 的相似之处在于，两者都提供对象-关系映射服务，都把对象持久化的任务从业务逻辑中分离出来。区别在于 CMP 负责持久化实体 EJB 组件，而 ORM 负责持久化 POJO，它是普通的基于 Java Bean 形式的实体域对象。</p>
<p>一般把基于 Java Bean 形式的实体域对象称为 POJO（Plain Old Java Object），意为又普通又古老的 Java 对象的意思。随着各种 ORM 映射工具的日趋成熟和流行，POJO有重现光彩，它和基于 CMP 的实体 EJB 相比，即简单又具有很高的可移植性，因此联合使用 ORM 映射工具和 POJO，已经成为一种越来越受欢迎的且用来取代 CMP 的持久化方案。POJO 的缺点就是无法做远程调用，不支持分布式计算。</p>
<p><br />
为什么要做持久化和ORM设计</p>
<p>在目前的企业应用系统设计中，MVC，即 Model（模型）- View（视图）- Control（控制）为主要的系统架构模式。MVC 中的 Model 包含了复杂的业务逻辑和数据逻辑，以及数据存取机制（如 JDBC的连接、SQL生成和Statement创建、还有ResultSet结果集的读取等）等。将这些复杂的业务逻辑和数据逻辑分离，以将系统的紧耦合关系转化为松耦合关系（即解耦合），是降低系统耦合度迫切要做的，也是持久化要做的工作。MVC 模式实现了架构上将表现层（即View）和数据处理层（即Model）分离的解耦合，而持久化的设计则实现了数据处理层内部的业务逻辑和数据逻辑分离的解耦合。而 ORM 作为持久化设计中的最重要也最复杂的技术，也是目前业界热点技术。</p>
<p>简单来说，按通常的系统设计，使用 JDBC 操作数据库，业务处理逻辑和数据存取逻辑是混杂在一起的。<br />
一般基本都是如下几个步骤：<br />
1、建立数据库连接，获得 Connection 对象。<br />
2、根据用户的输入组装查询 SQL 语句。<br />
3、根据 SQL 语句建立 Statement 对象 或者 PreparedStatement 对象。<br />
4、用 Connection 对象执行 SQL语句，获得结果集 ResultSet 对象。<br />
5、然后一条一条读取结果集 ResultSet 对象中的数据。<br />
6、根据读取到的数据，按特定的业务逻辑进行计算。<br />
7、根据计算得到的结果再组装更新 SQL 语句。<br />
8、再使用 Connection 对象执行更新 SQL 语句，以更新数据库中的数据。<br />
7、最后依次关闭各个 Statement 对象和 Connection 对象。</p>
<p>由上可看出代码逻辑非常复杂，这还不包括某条语句执行失败的处理逻辑。其中的业务处理逻辑和数据存取逻辑完全混杂在一块。而一个完整的系统要包含成千上万个这样重复的而又混杂的处理过程，假如要对其中某些业务逻辑或者一些相关联的业务流程做修改，要改动的代码量将不可想象。另一方面，假如要换数据库产品或者运行环境也可能是个不可能完成的任务。而用户的运行环境和要求却千差万别，我们不可能为每一个用户每一种运行环境设计一套一样的系统。<br />
所以就要将一样的处理代码即业务逻辑和可能不一样的处理即数据存取逻辑分离开来，另一方面，关系型数据库中的数据基本都是以一行行的数据进行存取的，而程序运行却是一个个对象进行处理，而目前大部分数据库驱动技术（如ADO.NET、JDBC、ODBC等等）均是以行集的结果集一条条进行处理的。所以为解决这一困难，就出现 ORM 这一个对象和数据之间映射技术。</p>
<p>举例来说，比如要完成一个购物打折促销的程序，用 ORM 思想将如下实现（引自《深入浅出Hibernate》）：<br />
业务逻辑如下：<br />
public Double calcAmount(String customerid, double amount) <br />
{<br />
&nbsp;&nbsp;&nbsp; // 根据客户ID获得客户记录<br />
&nbsp;&nbsp;&nbsp; Customer customer = CustomerManager.getCustomer(custmerid); <br />
&nbsp;&nbsp;&nbsp; // 根据客户等级获得打折规则<br />
&nbsp;&nbsp;&nbsp; Promotion promotion = PromotionManager.getPromotion(customer.getLevel()); <br />
&nbsp;&nbsp;&nbsp; // 累积客户总消费额，并保存累计结果<br />
&nbsp;&nbsp;&nbsp; customer.setSumAmount(customer.getSumAmount().add(amount); <br />
&nbsp;&nbsp;&nbsp; CustomerManager.save(customer); <br />
&nbsp;&nbsp;&nbsp; // 返回打折后的金额<br />
&nbsp;&nbsp;&nbsp; return amount.multiply(protomtion.getRatio()); <br />
}<br />
这样代码就非常清晰了，而且与数据存取逻辑完全分离。设计业务逻辑代码的时候完全不需要考虑数据库JDBC的那些千篇一律的操作，而将它交给 CustomerManager 和 PromotionManager 两个类去完成。这就是一个简单的 ORM 设计，实际的 ORM 实现框架比这个要复杂的多。</p>
<p><br />
目前有哪些流行的 ORM 产品<br />
目前众多厂商和开源社区都提供了持久层框架的实现，常见的有<br />
Apache OJB （<a href="http://db.apache.org/ojb/"><u><font color="#0000ff">http://db.apache.org/ojb/</font></u></a>）<br />
Cayenne （<a href="http://objectstyle.org/cayenne/"><u><font color="#0000ff">http://objectstyle.org/cayenne/</font></u></a>）<br />
Jaxor （<a href="http://jaxor.sourceforge.net/"><u><font color="#0000ff">http://jaxor.sourceforge.net</font></u></a>）<br />
Hibernate （<a href="http://www.hibernate.org/"><u><font color="#800080">http://www.hibernate.org</font></u></a>）<br />
iBatis （<a href="http://www.ibatis.com/"><u><font color="#0000ff">http://www.ibatis.com</font></u></a>）<br />
jRelationalFramework （<a href="http://ijf.sourceforge.net/"><u><font color="#0000ff">http://ijf.sourceforge.net</font></u></a>）<br />
mirage （<a href="http://itor.cq2.org/en/oss/mirage/toon"><u><font color="#0000ff">http://itor.cq2.org/en/oss/mirage/toon</font></u></a>）<br />
SMYLE （<a href="http://www.drjava.de/smyle"><u><font color="#0000ff">http://www.drjava.de/smyle</font></u></a>）<br />
TopLink （<a href="http://otn.oracle.com/products/ias/toplink/index.html"><u><font color="#0000ff">http://otn.oracle.com/products/ias/toplink/index.html</font></u></a>）<br />
其中 TopLink 是 Oracle 的商业产品，其他均为开源项目。</p>
<p>其中 Hibernate 的轻量级 ORM 模型逐步确立了在 Java ORM 架构中领导地位，甚至取代复杂而又繁琐的 EJB 模型而成为事实上的 Java ORM 工业标准。而且其中的许多设计均被 J2EE 标准组织吸纳而成为最新 EJB 3.0 规范的标准，这也是开源项目影响工业领域标准的有力见证。</p>
<br />
http://www.blogjava.net/fyz210/archive/2007/05/22/119098.html<br />
</div>
<img src ="http://www.blogjava.net/tinysun/aggbug/267162.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2009-04-23 15:34 <a href="http://www.blogjava.net/tinysun/archive/2009/04/23/267162.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ORM技术概念与实例</title><link>http://www.blogjava.net/tinysun/archive/2009/04/23/267150.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 23 Apr 2009 07:02:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2009/04/23/267150.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/267150.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2009/04/23/267150.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/267150.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/267150.html</trackback:ping><description><![CDATA[对象关系映射（<span style="font-size: 10pt; font-family: 新宋体">Object Relational Mapping</span><span style="font-size: 10pt; font-family: 新宋体">，简称</span><span style="font-size: 10pt; font-family: 新宋体">ORM</span><span style="font-size: 10pt; font-family: 新宋体">）是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说，</span><span style="font-size: 10pt; font-family: 新宋体">ORM</span><span style="font-size: 10pt; font-family: 新宋体">是通过使用描述对象和数据库之间映射的元数据，将程序中的对象自动持久化到关系数据库中。</span><span style="font-size: 10pt; font-family: 新宋体">本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示者额外的执行开销；然而，如果ORM作为一种中间件实现，则会有很多机会做优化，而这些在手写的持久层并不存在。 </span><span style="font-size: 10pt; font-family: 新宋体">更重要的是用于控制转换的元数据需要提供和管理；但是同样，这些花费要比维护手写的方案要少</span><span style="font-size: 10pt; font-family: 新宋体">.</span>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; font-family: 新宋体">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 10pt; font-family: 新宋体">对象</span><span style="font-size: 10pt; font-family: 新宋体">-</span><span style="font-size: 10pt; font-family: 新宋体">关系映射（</span><span style="font-size: 10pt; font-family: 新宋体">Object/Relation Mapping</span><span style="font-size: 10pt; font-family: 新宋体">，简称</span><span style="font-size: 10pt; font-family: 新宋体">ORM</span><span style="font-size: 10pt; font-family: 新宋体">），是随着面向对象的软件开发方法发展而产生的</span><span style="font-size: 10pt; font-family: 新宋体">,</span><span style="font-size: 10pt; font-family: 新宋体">面向对象的开发方法是当今企业级应用开发环境中的主流开发方法，关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。</span><span style="font-size: 10pt; font-family: 新宋体">对象和关系数据是业务实体的两种表现形式，业务实体在内存中表现为对象，在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系，而在数据库中，关系数据无法直接表达多对多关联和继承关系。因此，对象-关系映射(ORM)系统一般以中间件的形式存在，主要实现程序对象到关系数据库数据的映射。</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; font-family: 新宋体">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 10pt; font-family: 新宋体">面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的，而关系数据库则是从数学理论发展而来的，两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; font-family: 新宋体">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 10pt; font-family: 新宋体">让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。几乎所有的程序里面，都存在对象和关系数据库。在业务逻辑层和用户界面层中，我们是面向对象的。当对象信息发生变化的时候，我们需要把对象的信息保存在关系数据库中。</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; font-family: 新宋体">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 10pt; font-family: 新宋体">当你开发一个应用程序的时候(不使用O/R Mapping),你可能会写不少数据访问层的代码，用来从数据库保存，删除，读取对象信息，等等。你在DAL中写了很多的方法来读取对象数据，改变状态对象等等任务。而这些代码写起来总是重复的。</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; font-family: 新宋体">&nbsp;</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; font-family: 新宋体">　　如果打开你最近的程序，看看DAL代码，你肯定会看到很多近似的通用的模式。我们以保存对象的方法为例，你传入一个对象，为SqlCommand对象添加SqlParameter，把所有属性和对象对应，设置SqlCommand的CommandText属性为存储过程，然后运行SqlCommand。对于每个对象都要重复的写这些代码。</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; font-family: 新宋体">　　除此之外，还有更好的办法吗？有，引入一个O/R Mapping。实质上，一个O/R Mapping会为你生成DAL。与其自己写DAL代码，不如用O/R Mapping。你用O/R Mapping保存，删除，读取对象，O/R Mapping负责生成SQL，你只需要关心对象就好。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 24pt"><span style="font-size: 10pt; font-family: 新宋体">一般的ORM包括以下四部分：</span></p>
<p style="margin: 0cm 0cm 0pt 60pt; text-indent: -24pt; tab-stops: list 60.0pt"><span style="font-size: 10pt; font-family: Wingdings">u<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 10pt; font-family: 新宋体">一个对持久类对象进行</span><span style="font-size: 10pt; font-family: 新宋体">CRUD</span><span style="font-size: 10pt; font-family: 新宋体">操作的</span><span style="font-size: 10pt; font-family: 新宋体">API</span><span style="font-size: 10pt; font-family: 新宋体">；</span></p>
<p style="margin: 0cm 0cm 0pt 60pt; text-indent: -24pt; tab-stops: list 60.0pt"><span style="font-size: 10pt; font-family: Wingdings">u<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 10pt; font-family: 新宋体">一个语言或</span><span style="font-size: 10pt; font-family: 新宋体">API</span><span style="font-size: 10pt; font-family: 新宋体">用来规定与类和类属性相关的查询；</span></p>
<p style="margin: 0cm 0cm 0pt 60pt; text-indent: -24pt; tab-stops: list 60.0pt"><span style="font-size: 10pt; font-family: Wingdings">u<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 10pt; font-family: 新宋体">一个规定</span><span style="font-size: 10pt; font-family: 新宋体">mapping metadata</span><span style="font-size: 10pt; font-family: 新宋体">的工具；</span></p>
<p style="margin: 0cm 0cm 0pt 60pt; text-indent: -24pt; tab-stops: list 60.0pt"><span style="font-size: 10pt; font-family: Wingdings">u<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 10pt; font-family: 新宋体">一种技术可以让</span><span style="font-size: 10pt; font-family: 新宋体">ORM</span><span style="font-size: 10pt; font-family: 新宋体">的实现同事务对象一起进行</span><span style="font-size: 10pt; font-family: 新宋体">dirty checking, lazy association fetching</span><span style="font-size: 10pt; font-family: 新宋体">以及其他的优化操作。</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; font-family: 新宋体">事例</span></p>
<p style="margin: 0cm 0cm 0pt 24pt"><span style="font-size: 10pt; font-family: 新宋体">极致业务基础平台采用</span><span style="font-size: 10pt; font-family: 新宋体">ORM</span><span style="font-size: 10pt; font-family: 新宋体">技术实现数据的持久化</span><span style="font-size: 10pt; font-family: 新宋体">,</span><span style="font-size: 10pt; font-family: 新宋体">并提供了完整的实体定义工具</span><span style="font-size: 10pt; font-family: 新宋体">,</span><span style="font-size: 10pt; font-family: 新宋体">元数据管理机制</span><span style="font-size: 10pt; font-family: 新宋体">,</span><span style="font-size: 10pt; font-family: 新宋体">自动生成关系数据库表结构</span><span style="font-size: 10pt; font-family: 新宋体">.</span></p>
<p style="margin: 0cm 0cm 0pt 24pt"><span style="font-size: 10pt; font-family: 新宋体">平台下载地址</span><span style="font-size: 10pt; font-family: 新宋体">:</span></p>
<p style="margin: 0cm 0cm 0pt 24pt"><span style="font-family: 新宋体"><font size="3"><font color="#000000"><a href="http://www.jeez.com.cn/jbf"><font face="宋体"><a href="http://www.jeez.com.cn/jbf"><u>http://www.jeez.com.cn/</u></font><u>jbf</u></a></a></font></font></span></p>
<img src ="http://www.blogjava.net/tinysun/aggbug/267150.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2009-04-23 15:02 <a href="http://www.blogjava.net/tinysun/archive/2009/04/23/267150.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基础知识：带你了解最常见的13个数据库术语</title><link>http://www.blogjava.net/tinysun/archive/2009/02/22/256042.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Sun, 22 Feb 2009 07:56:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2009/02/22/256042.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/256042.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2009/02/22/256042.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/256042.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/256042.html</trackback:ping><description><![CDATA[<div class="newscon">
<div id="csdn_tag_adstyle">
<dl>
<dd class="tit"><a href="http://tagegg.csdn.net/a.aspx?action=click&amp;unionuser=16&amp;adid=&amp;tag=数据库&amp;adurl="></a></dd>
<dd></dd></dl>
<dl>
<dd class="tit"><a href="http://tagegg.csdn.net/a.aspx?action=click&amp;unionuser=16&amp;adid=&amp;tag=数据库&amp;adurl="></a></dd>
<dd></dd></dl>
<dl>
<dd class="tit"><a href="http://tagegg.csdn.net/a.aspx?action=click&amp;unionuser=16&amp;adid=&amp;tag=数据库&amp;adurl="></a></dd>
<dd></dd></dl>
<dl>
<dd class="tit"><a href="http://tagegg.csdn.net/a.aspx?action=click&amp;unionuser=16&amp;adid=&amp;tag=数据库&amp;adurl="></a></dd>
<dd></dd></dl></div>
1．关系模型：用二维表格结构表示实体集，外键表示实体间联系的数据模型称为关系模型。关系模型是由若干个关系模式组成的集合。 <br />
<br />
2．关系模式：关系模式实际上就是记录类型。它包括：模式名，属性名，值域名以及模式的主键。关系模式仅是对数据特性的描述。 <br />
<br />
3．关系实例：就是一个关系，即一张二维表格。 <br />
<br />
4．属性：在关系模型中，字段称为属性。 <br />
<br />
5．域：在关系中，每一个属性都有一个取值范围，称为属性的值域。 <br />
<br />
6．元组：在关系中，记录称为元组。 <br />
<br />
7．候选码：在关系中能唯一标识元组的属性集称为关系模式的候选码。 <br />
<br />
8．主码：用户选作元组标识的一个候选码为主码。 <br />
<br />
9．外码：某个关系的主码相应的属性在另一关系中出现，此时该主码在就是另一关系的外码，如有两个关系S和SC,其中S#是关系S的主码，相应的属性S#在关系SC中也出现，此时S#就是关系SC的外码。 <br />
<br />
10．实体完整性规则：这条规则要求关系中元组在组成主码的属性上不能有空值。如果出现空值，那么主码值就起不了唯一标识元组的作用。 <br />
<br />
11．参照完整性规则：这条规则要求&#8220;不引用不存在的实体&#8221;。其形式定义如下：如果属性集K是关系模式R1的主码，K也是关系模式R2的外码，那么R2的关系中，K的取值只允许有两种可能，或者为空值，或者等于R1关系中某个主码值。 <br />
<br />
这条规则在使用时有三点应注意： <br />
<br />
(1)外码和相应的主码可以不同名，只要定义在相同值域上即可。 <br />
<br />
(2)R1和R2也可以是同一个关系模式，表示了属性之间的联系。 <br />
<br />
(3)外码值是否允许空应视具体问题而定。 <br />
<br />
12．过程性语言：在编程时必须给出获得结果的操作步骤，即&#8220;干什么&#8221;和&#8220;怎么干&#8221;。如Pascal和C语言等。 <br />
<br />
13．非过程性语言：编程时只须指出需要什么信息，不必组出具体的操作步骤的语言，各种关系查询语言均属于非过程性语言。</div>
<img src ="http://www.blogjava.net/tinysun/aggbug/256042.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2009-02-22 15:56 <a href="http://www.blogjava.net/tinysun/archive/2009/02/22/256042.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>