Thursday, January 03, 2008

关于负载均衡器

web应用服务器集群系统,是由一群同时运行同一个web应用的服务器组成的集群系统,在外界看来,就像是一个服务器一样。为了均衡集群服务器的负载,达 到优化系统性能的目的,集群服务器将众多的访问请求,分散到系统中的不同节点进行处理。从而实现了更高的有效性和稳定性,而这也正是基于Web的企业应用 所必须具备的特性。
  
  高可靠性可以看作为系统的一种冗余设定。对于一个特定的请求,如果所申请的服务器不能进行处理的话,那么其他的服务器能不能对之进行有效的处 理呢?对于一个高效的系统,如果一个Web服务器失败的话,其他的服务器可以马上取代它的位置,对所申请的请求进行处理,而且这一过程对用户来说,要尽可 能的透明,使用户察觉不到!
  
  稳定性决定了应用程序能否支持不断增长的用户请求数量,它是应用程序自身的一种能力。稳定性是影响系统性能的众多因素的一种有效的测量手段,包括机群系统所能支持的同时访问系统的最大用户数目以及处理一个请求所需要的时间。
  
  在现有众多的均衡服务器负载的方法中,广泛研究并使用的是以下两个方法:
  
  DNS负载平衡的方法RR-DNS(Round-Robin Domain Name System)
  负载均衡器
  以下,我们将就这两种方法进行讨论。
  
  DNS轮流排程 RR-DNS(Round-Robin Domain Name System)
  
  域名服务器(Domain Name Server)中的数据文件将主机名字映射到其IP地址。当你在浏览器中键入一个URL时(例如:www.loadbalancedsite.com), 浏览器则将请求发送到DNS,要求其返回相应站点的IP地址,这被称为DNS查询。当浏览器获得该站点的IP地址后,便通过该IP地址连接到所要访问的站 点,将页面展现在用户面前。
  
  域名服务器(DNS)通常包含一个单一的IP地址与该IP地址所映射的站点的名称的列表。在我们上面所假象的例子中,www.loadbalancedsite.com 这个站点的映射IP地址为203.24.23.3。
  
  为了利用DNS均衡服务器的负载,对于同一个站点来讲,在DNS服务器中同时拥有几个不同的IP地址。这几个IP地址代表集群中不同的机器, 并在逻辑上映射到同一个站点名。通过我们的例子可以更好的理解这一点,www.loadbalancedsite.com将通过下面的三个IP地址发布到 一个集群中的三台机器上:
  
  203.34.23.3
  
  203.34.23.4
  
  203.34.23.5
  
  在本例中,DNS服务器中包含下面的映射表:
  
  www.loadbalancedsite.com 203.34.23.3
  
  www.loadbalancedsite.com 203.34.23.4
  
  www.loadbalancedsite.com 203.34.23.5
  
  当第一个请求到达DNS服务器时,返回的是第一台机器的IP地址203.34.23.3;当第二个请求到达时,返回的是第二台机器的IP地址203.34.23.4,以此类推。当第四个请求到达时,第一台机器的IP地址将被再次返回,循环调用。
  
  利用上述的DNS Round Robin技术,对于某一个站点的所有请求将被平均的分配到及群中的机器上。因此,在这种技术中,集群中的所有的节点对于网络来说都是可见的。
  
  DNS 轮流排程的优势
  
   DNS Round Robin的最大的优点就是易于实现和代价低廉:
  
  代价低,易于建立。 为了支持轮流排程,系统管理员只需要在DNS服务器上作一些改动,而且在许多比较新的版本的DNS服务器上已经增加了这种功能。对于Web应用来说,不需要对代码作任何的修改;事实上,Web应用本身并不会意识到负载均衡配置,即使在它面前。
  简单. 不需要网络专家来对之进行设定,或在出现问题时对之进行维护。
  DNS 轮流排程的缺点
  
   这种基于软件的负载均衡方法主要存在两处不足,一是不实时支持服务期间的关联,一是不具有高可靠性。
[ refer to http://www.javaworld.com/cgi-bin/mailto/x_java.cgi]
  • No server affinity(姻亲关系:表示上一次请求送到了某个服务器,那么下个请求还是希望送到原来的那个服务器,否则有可能导致会话信息丢失) support. When a user receives an IP address, it is cached on the browser. Once the cache expires, the user makes another request for the IP address associated with a logical name. That second request can return the IP address of any other machine in the cluster, resulting in a lost session.
  • No HA support. Imagine a cluster of n servers. If one of those servers goes down, every nth request to the DNS server will go to the dead server.
  • Changes to the cluster take time to propagate to the rest of the Internet. Many corporations' and ISPs' DNS servers cache DNS lookups from their clients. Even if your DNS list of servers in the cluster could change dynamically, it would take time for the cached entries on other DNS servers to expire. For example, after a downed server is removed from your cluster's DNS list, AOL clients could still attempt to hit the downed server if AOL's DNS servers cached entries to the downed server. As a result, AOL users would not be able connect to your site even if other machines in the cluster were available.
  • No guarantee of equal client distribution across all servers in the cluster. If you don't configure cooperating DNS servers to support DNS load balancing, they could take only the first IP address returned from the initial lookup and use that for their client requests. Imagine a partner corporation with thousands of employees all pinned to a single server in your cluster!
  
   • 不支持服务器间的一致性。服务器一致性是负载均衡系统所应具备的一种能力,通过它,系统可以根据会话信息是属于服务器端的,还是底层数据库级别的,继而将 用户的请求导向相应的服务器。而DNS轮流排程则不具备这种智能化的特性。它是通过cookie、隐藏域、重写URL三种方法中的一种来进行相似的判断 的。当用户通过上述基于文本标志的方法与服务器建立连接之后,其所有的后续访问均是连接到同一个服务器上。问题是,服务器的IP是被浏览器暂时存放在缓存 中,一旦记录过期,则需要重新建立连接,那么同一个用户的请求很可能被不同的服务器进行处理,则先前的所有会话信息便会丢失。
  
  不支持高可靠性。设想一个具有N个节点的集群。如果其中的一个节点毁坏,那么所有的访问该节点的请求将不会有所回应,这是任何人都不愿意看到 的。比较先进的路由器可以通过每隔一定的时间间隔,对节点检查,如果有毁坏的节点,则将之从列表中去除的方法,解决这个问题。但是,由于在 Internet上,ISPs将众多的DNS存放在缓存中,以节省访问时间,因此,DNS的更新就会变得非常缓慢,以至于有的用户可能会访问一些已经不存 在的站点,或者一些新的站点得不到访问。所以,尽管DNS轮流排程在一定程度上解决了负载均衡问题,但这种状况的改变并不是十分乐观和有效的。
  除了上面介绍的轮流排程方法外,还有三种DNS负载均衡处理分配方法,将这四种方法列出如下:
  
  Ø Round robin (RRS): 将工作平均的分配到服务器 (用于实际服务主机性能一致)
  
  Ø Least-connections (LCS): 向较少连接的服务器分配较多的工作(IPVS 表存储了所有的活动的连接。用于实际服务主机性能一致。)
  
  Ø Weighted round robin (WRRS): 向较大容量的服务器分配较多的工作。可以根据负载信息动态的向上或向下调整。 (用于实际服务主机性能不一致时)
  
  Ø Weighted least-connections (WLC): 考虑它们的容量向较少连接的服务器分配较多的工作。容量通过用户指定的砝码来说明,可以根据装载信息动态的向上或向下调整。(用于实际服务主机性能不一致时)
  
  
  
  负载均衡器
  
  负载均衡器通过虚拟IP地址方法,解决了轮流排程所面临的许多问题。使用了负载均衡器集群系统,在外部看来,像是具有一个IP地址的单一服务 器一样,当然,这个IP地址是虚拟的,它映射了集群中的每一台机器的地址。所以,在某种程度上,负载均衡器是将整个集群的IP地址报漏给外部网络。
  
  当请求到达负载均衡器时,它会重写该请求的头文件,并将之指定到集群中的机器上。如果某台机器被从集群中移除了,请求不会别发往已经不存在的 服务器上,因为所有的机器表面上都具有同一个IP地址,即使集群中的某个节点被移除了,该地址也不会发生变化。而且,internet上缓存的DNS条目 也不再是问题了。当返回一个应答时,客户端看到的只是从负载均衡器上所返回的结果。也就是说,客户端操作的对象是负载均衡器,对于其更后端的操作,对客户 端来讲,是完全透明的。
  
  负载均衡器的优点
  
   • 服务器一致性. 负载均衡器读取客户端发出的每一个请求中所包含的cookies或url解释。基于所读出的这些信息,负载均衡器就可以重写报头并将请求发往集群中合适的 节点上,该节点维护着相应客户端请求的会话信息。在HTTP通信中,负载均衡器可以提供服务器一致性,但并不是通过一个安全的途径(例如:HTTPS)来 提供这种服务。当消息被加密后(SSL),负载均衡器就不能读出隐藏在其中的会话信息。
  
   • 通过故障恢复机制获得高可靠性. 故障恢复发生在当集群中某个节点不能处理请求,需将请求重新导向到其他节点时。主要有两种故障恢复:
  
  • 请求级故障恢复。当集群中的一个节点不能处理请求时(通常是由于down机),请求被发送到其他节点。当然,在导向到其他节点的同时,保存在原节点上的会话信息将会丢失。
  
  • 透明会话故障恢复。当一个引用失败后,负载均衡器会将之发送到集群中其他的节点上,以完成操作,这一点对用户来说是透明的。由于透明会话故障恢复需要节点 具备相应的操作信息,因此为了实现该功能,集群中的所有节点必须具有公共存储区域或通用数据库,存储会话信息数据,以提供每个节点在进行单独进程会话故障 恢复时所需要的操作信息。
  
   • 统计计量。既然所有的Web应用请求都必须经过负载均衡系统,那么系统就可以确定活动会话的数量,在任何实例访问中的活动会话的数目,应答的次数,高峰负 载次数,以及在高峰期和低谷期的会话的数目,还有其他更多的。所有的这些统计信息都可以被很好的用来调整整个系统的性能。
  
  负载均衡器的缺点
  
   硬件路由的缺点在于费用、复杂性以及单点失败的。由于所有的请求均是通过一个单一的硬件负载均衡器来传递,因此,负载均衡器上的任何故障都将导致整个站点的崩溃。
  
  HTTPS请求的负载均衡
  
   正如上面所提到的,很难在那些来自HTTPS的请求上进行负载均衡和会话信息维护处理。因为,这些请求中的信息已经被加密了。负载均衡器没有能力处理这类请求。不过,这里有两种方法可以解决这一问题:
  
  代理网络服务器
  硬件SSL解码器
   代理服务器位于服务器集群之前,首先由它接受所有的请求并对之进行解密,然后将这些处理后的请求根据头信息重新发往相应的节点上,这种方式不需要硬件上的支持,但会增加代理服务器的额外的负担。
  
   硬件SSL解码器,则是在请求到达负载均衡器之前,先经由它进行解密处理。这种方式比代理服务器的处理速度要快捷一些。但代价也高,而且实现比较复杂。

Reverse Proxy(就是对请求方显示单独的接口,对web server也是一样,但一般会携带原始请求的ip)

A reverse proxy or surrogate is a proxy server that is installed within the neighborhood of one or more servers. Typically, reverse proxies are used in front of Web servers. All connections coming from the Internet addressed to one of the Web servers are routed through the proxy server, which may either deal with the request itself or pass the request wholly or partially to the main web servers.

A reverse proxy dispatches in-bound network traffic to a set of servers, presenting a single interface to the caller. For example, a reverse proxy could be used for load balancing a cluster of web servers. In contrast, a forward proxy acts as a proxy for out-bound traffic. For example, an ISP may use a proxy to forward HTTP traffic from its clients to external web servers on the internet; it may also cache the results to improve performance.

Wednesday, January 02, 2008

(转)J2EE集群的神话

http://www.theserverside.com/tt/articles/article.tss?l=J2EEClustering

失效转移可以完全避免错误——否定

Jboss的文档中,整个章节都在警告你“你真的需要HTTP会话的复制吗?”。是的,有时没有失效转移的高可用性的解决方案也是可接受并且是廉价的。失效转移并不是你想象的那么强壮。

那么失效转移到底给你带来了什么?你可能想失效转移可以避免错误。你看,没有会话的失效转移,当一个服务器实例失效后,会话数据将丢失而导致错误。通过失效转移,会话可以从备份中恢复,而请求可以被其他服务器实例所处理,用户根本意识不到失效。这是事实,但这是有条件的!

回想一样我们定义的“失效转移”,我们定义了一个条件是失效转移是在“两个方法调用之间”发生的。这是说如果你有两个连续的对远程对象的方法调用,失效转移只会在第一调用成功后并且第二调用的请求发出前才能发生。

这样,当远程服务器在处理请求的过程中失效了会发生什么呢?答案是:多数情况处理将会停止而客户端将会看到错误信息。除非这个方法是等幂的(Idempotent),只有这个方法是等幂的,一些负载均衡器更智能,它会重试这些方法并将它失效转移到其他实例上。

为什么“等幂”重要呢,因为客户端不知道当失效发生的时候请求被执行到什么地方。是才刚刚初始化还是差不多就要完成了?客户端没法判断!如果方法不是等幂的,在相同方法上的两次调用可能会两次修改系统的状态,而使得系统出现不一致的情形。

你可能想所有在事务中的方法都是等幂的,毕竟,如果错误发生,事务将被回滚,事务状态的改变都将被复位。但事实上事务的边界可能不包括所有的远程方法调用过程。如果事务已经在服务器上提交了而返回给客户端时网络崩溃怎么办呢?客户端不知道服务器的事务是否是成功了。

在一些应用程序中,将所有的方法都做成等幂的是不可能的。这样,你只能通过失效转移减少错误,而不是避免它们。拿在线商店为例,假设每台服务器可以同时处理100个在线用户的请求,当一台服务器失效了,没有失效转移的解决方案将丢失100个用户的会话数据并激怒这些用户。而有失效转移的解决方案中,当失效发生的时候有20个用户正在处理请求,这样20个用户将被失效激怒。而其他80个用户正处于思考时间或在两个方法调用之间,这些用户可以透明地获得失效转移。这样,你就需做以下的考虑:

l 激怒20个用户和激怒100个用户造成影响的区别。

l 采用失效转移和不采用失效转移产品成本的区别

独立应用可以透明的迁移到集群结构中——否定

尽管一些供应商宣称他们的J2EE产品有这样的灵活性。不要相信他们!事实你要在开始系统设计时就要准备集群,而这将影响开发和测试的所有阶段。

Http Session

在集群环境中,如我前面提到的,使用HTTP Session有很多限制,这取决于你的应用程序服务器采用了那种会话失效转移的机制。第一个重要的限制就是所有保存的HTTP Session中的对象必须是可序列化的,这将限制设计和应用程序结构。一些设计模式和MVC框架会用HTTP Session保存一些不序列化的对象(如ServletContextEJB本地接口和WEB服 务引用),这样的设计不能在集群中工作。第二,对象的序列的反序列化对性能的影响很大,特别是数据库保存的方式。在这样的环境中,应该避免在会话中保存大 的或是众多的对象。如果你采用了内存复制的方式,如前所述你必须小心在会话中属性的交叉引用。其他在集群环境中的主要区别是在会话不管任何属性修改,你必 须调用“setAttribute()”方法。这个方法调用在独立的系统中是可选的。这个方法的目的是区别已修改的属性和那些没用到属性,这样系统可以只为失效转移备份必要的数据,从而提高性能。

缓存

我经历过的大多数J2EE项目都用了缓存来提高性能,同时流行的应用程序服务器也都提供了不同程度的缓存用来加快应用程序的速度。但这些缓存都是为那些典型的独立环境设计的,只能在单JVM实例中工作。我们需要缓存是因为一些对象很“重”,创建它需花费大量的时间和资源。因此我们维护了对象池用于重用这些对象,而不需要在后面创建。我们只有当维护缓存比创建对象更廉价时才能获得性能的提高。在集群环境,每个JVM实例都要维护一份缓存的拷贝,这些拷贝必须同步以维持所有服务器实例状态的一致性。有时这种类型的同步会比没有缓存带来更糟的性能。

Static变量

当我们设计J2EE应用程序时,在架构上经常会使用一些设计模式。这些如“Singleton”的设计模式会用到静态变量来在多对象之间共享状态。这种方式在单服务中工作得很好,但在集群环境将失效。集群中的每个实例都会在它的JVM实 例中维护一份静态变量的拷贝,这样就破坏了模式的机制。一个使用静态变量的例子就是用它来保持在线用户数。用静态变量来保存在线用户数是一个很简单的办 法,当用户进入或离开时就增加和减少它。这种方式在单服务器中绝对是好的,但在集群环境将失效。在集群中更好的方式是将所有状态保存到数据库。

外部资源

尽管J2EE规范不支持,但为各种目的仍然会用外部I/O的操作。例如,一些应用会使用文件系统来保存用户上传的文件,或是创建一个动态配置的XML文件。在集群环境是没有办法来在其他实例之间来复制这些文件的。为了在集群中工作,办法是用数据库作为外部文件的存放点,另外也可以使用SAN(存储区域网,Storage Area Network)作为存放点。

特殊服务

一 些特殊的服务只在独立的环境中才有意义,定时服务就一个很好例子,这种服务可以在一个固定的间隔时间有规律的触发。定时服务常用于执行一些自动化管理任 务。如日志文件滚动,系统数据备份,数据库一致性检查和冗余数据清理等。一些基于事件的服务也很难被迁移到集群环境中。初始化服务就是个好例子,它只在整 个系统启动时才发生。邮件通知服务也一样,它在一些警告条件下触发。

这些服务是被事件而不是被请求触发的,而且只被执行一次。这些服务使得负载均衡和失效转移在集群中没多少意义。

一些产品准备了这些服务,如Jboss使用了“集群单例设施”来协调所有实例,保证执行这些服务一次且仅有一次。基于你所选择的平台,一些特殊的服务可能会是把你的应用迁移到集群结构中的障碍。

分布式结构比并置结构更灵活——不一定

J2EE技术,尤其是EJB,天生就是用来做分布式计算。解耦业务功能,重用远程组件,这些使得多层应用非常流行。但是我们不能将所有的东西都分布。一些J2EE架构师认为Web层与EJB层并置得越近越好。这些计论后面会继续。先让我解释一下。

20 分布式结构

如图20所示,这是一个分布式结构。当请求来了,负载均衡器将请求分发到不同服务器中的不同WEB容器,如果请求包含了EJB调用,WEB容器将重发EJB调用到不同的EJB容器。这样,请求将被负载均衡和失效转移两次。

一些人看分布式结构,他们会指出:

l 第二次负载均衡没有必要,因为它不会使任务分配更平坦。每个服务器实例都有它们自己的WEB容器和EJB容器。把EJB容器用来处理来自其他实例WEB容器的请求比只在服务器内部调用并没有什么优势。

l 第二次失效转移没有必要,因为它不能是高可用性。多数供应商实现J2EE服务器都会在同一服务器中运行的WEB容器和EJB容器放在一个JVM实例中。如果EJB容器失效了,多数情况下在同一个JVM中的WEB容器也将同时失效。

l 性能将下降。想像一下对你的应用的一次调用包含一组对EJB的调用,如果你负载均衡了这些EJB,这将跨越每个服务器实例,导致许多不必要的服务器到服务器的交互。还有,如果这个方法在事务范围内,那么事务边界将包含许多服务器实例,这将严重影响性能。

实际上在运行期,多数的供应商(包括Sun JESWebLogicJboss)都会优化EJB调用机制,使请求首先选择同一个服务器中的EJB容器。这样,如图21所示,我们只在第一层(WEB层)做负载均衡,然后调用相同服务器上的服务。这种结构我们称之为并置结构。技术上说,并置结构是分布式结构的一种特例。

21 并置结构

一个有趣的问题是,既然多数的部署在运行期都演进成了并置结构,为什么不用本地接口代替远程接口,这将大提高性能。你当然可以,但是记住,当你使用本地接口后,WEB组件和EJB耦合得很紧,而方法调用也是直接的而不通过RMI/IIOP。负载均衡和失效转移分发器没有机会介入本地接口调用。“WEB+EJB”整体处理负载均衡和失效转移。

但不幸的是,在集群中使用本地接口在多数J2EE服务器中有局限性。使用本地接口的EJB是本地对象,是不可序列化的,这一个限制就使本地引用不能保存在HTTP Session中。一些产品,如Sun JES,会将本地接口区别看待,使它们可以序列化。这样就可以用在HTTP Session中。

另一个有趣的问题是,既然并置结构这么流行并且有好的性能,为什么还要分布式结构呢?这在多数情况下是有道理的,但有时分布式结构是不可替代的。

l EJB不仅被WEB容器使用,富客户端也会使用它。

l EJB组件和WEB组件需在不同的安全级别上,并需要物理分离。这样防火墙将被设置用于保护运行EJB的重要机器。

l WEB层和EJB层极端不对称使得分布式结构是更好的选择。比如,一些EJB组件非常复杂并且很消耗资源,它们只能运行在昂贵的大型服务器上,另一方面,WEB组件(HTMLJSPServlet)简单得只需廉价的PC服务器就能满足要求。在这种情况下,专门的WEB服务器可以用来接受客户端连接请求,很快处理静态数据(HTML和图像)和简单的WEB组件(JSPServlet)。大型服务器只被用来做复杂计算。这将更好的利用投资。

结论

集群与独立环境不同,J2EE供应商采用不同的方法来实现集群。如果你的项目为做到高伸缩性而使用集群,你应该在你的项目开始的时候就做准备。选择符合你的需求的正确的J2EE产品。选择正确的第三方软件和框架并确保它们能支持集群。最后设计正确的架构使得能从集群中受益而不是受害。


AppendixA: Classical cluster topology

The image “http://www.javaworld.com/jw-02-2001/images/jw-0223-extremescale7.gif” cannot be displayed, because it contains errors.


=================================================

ADDITIONAL INFORMATION

=================================================

垂直伸缩(scale vertically),应用部署在单个节点上,随着系统用户数的增多,可以增加系统的硬件资源,比如增加cpy,增加内存,增加带宽等。

水平伸缩(scale horizontally),应用可以部署在多个节点上,随着系统用户的增多,可以增加更多的处理节点,比如从一台节点增加到三台节点。 水平伸缩方式需要在每个节点上部署相同的应用,但是这种伸缩方式更加便宜,同时也提高了系统的可用性。

参考资料:http://www.theserverside.com/tt/articles/article.tss?l=ScalingYourJavaEEApplications

Thursday, November 01, 2007

Database Normalization And Design Techniques

One of the most important factors in dynamic web page development is database definition. If your tables are not set up properly, it can cause you a lot of headaches down the road when you have to perform miraculous SQL calls in your PHP code in order to extract the data you want. By understanding data relationships and the normalization of data, you will be better prepared to begin developing your application in PHP. ----
我们可以快速的开发一套系统,但是系统是否具有高可用性,是否支持大量用户的并发访问。前几天就有新闻,说是奥运门票的第二阶段销售开始,结果当天销售门票的网站就崩溃了,因为一下子有太多的用户去访问这个网站。 我想这种情况是可以预期的,谁都会知道抢购奥运门票的情形该有多火爆,所以应该是网站没有做好准备。 这里我主要关注系统的性能问题,即支持大并发量的用户访问,而所有决定系统性能的因素当中,数据库设计应该是最重要的,这是数据架构级别的问题,是决定性的,当然还有其他的因素,比如分布式系统的远程调用, 数据的缓存等等,这些也很重要。
我在网上找了一些资料,整理一下。
Basically, the Rules of Normalization are enforced by eliminating redundancy and inconsistent dependency in your table designs.

  1 引言
   数据库优化的目标无非是避免磁盘I/O瓶颈、减少CPU利用率和减少资源竞争。为了便于读者阅读和理解,笔者参阅了Sybase、Informix和 Oracle等大型数据库系统参考资料,基于多年的工程实践经验,从基本表设计、扩展设计和数据库表对象放置等角度进行讨论,着重讨论了如何避免磁盘 I/O瓶颈和减少资源竞争,相信读者会一目了然。

  2 基于第三范式的基本表设计

  在基于表驱动的信息管理系 统(MIS)中,基本表的设计规范是第三范式(3NF)。第三范式的基本特征是非主键属性只依赖于主键属性。基于第三范式的数据库表设计具有很多优点:一 是消除了冗余数据,节省了磁盘存储空间;二是有良好的数据完整性限制,即基于主外键的参照完整限制和基于主键的实体完整性限制,这使得数据容易维护,也容 易移植和更新;三是数据的可逆性好,在做连接(Join)查询或者合并表时不遗漏、也不重复;四是因消除了冗余数据(冗余列), 在查询(Select)时每个数据页存的数据行就多,这样就有效地减少了逻辑I/O,每个Cash存的页面就多,也减少物理I/O;五是对大多数事务 (Transaction)而言,运行性能好;六是物理设计(Physical Design)的机动性较大,能满足日益增长的用户需求。

  在基本表设计中,表的主键、外键、索引设计占有非常重要的地位,但系统设计人员往往只注重于满足用户要求,而没有从系统优化的高度来认识和重视它们。实际上,它们与系统的运行性能密切相关。现在从系统数据库优化角度讨论这些基本概念及其重要意义:

   (1)主键(Primary Key):主键被用于复杂的SQL语句时,频繁地在数据访问中被用到。一个表只有一个主键。主键应该有固定值(不能为Null或缺省值,要有相对稳定 性),不含代码信息,易访问。把常用(众所周知)的列作为主键才有意义。短主键最佳(小于25bytes),主键的长短影响索引的大小,索引的大小影响索 引页的大小,从而影响磁盘I/O。主键分为自然主键和人为主键。自然主键由实体的属性构成,自然主键可以是复合性的,在形成复合主键时,主键列不能太多, 复合主键使得Join*作复杂化、也增加了外键表的大小。人为主键是,在没有合适的自然属性键、或自然属性复杂或灵敏度高时,人为形成的。人为主键一般是 整型值(满足最小化要求),没有实际意义,也略微增加了表的大小;但减少了把它作为外键的表的大小。

OIDs Should Have No Business Meaning
A very critical issue that needs to be pointed out is that OIDs should have absolutely no business meaning
whatsoever. Nada. Zip. Zilch. Zero. Any column with a business meaning can potentially change, and if
there’s one thing that we learned over the years in the relational world it’s that it’s a fatal mistake to give
your keys meaning. If your users decide to change the business meaning, perhaps they want to add some
digits or make the number alphanumeric, you need to make changes to your database in every single spot
where you use that information. Anything that is used as a primary key in one table is virtually guaranteed
to be used in other tables as a foreign key. What should be a simple change, adding a digit to your
customer number, can be a huge maintenance nightmare. Yuck. In the relational database world, this OID
strategy is referred to as employing surrogate keys.


  (2)外键(Foreign Key):外键的作用是建立关系型数据库中表之间的关系(参照完整性),主键只能从独立的实体迁移到非独立的实体,成为后者的一个属性,被称为外键。

   (3)索引(Index):利用索引优化系统性能是显而易见的,对所有常用于查询中的Where子句的列和所有用于排序的列创建索引,可以避免整表扫描 或访问,在不改变表的物理结构的情况下,直接访问特定的数据列,这样减少数据存取时间;利用索引可以优化或排除耗时的分类*作;把数据分散到不同的页面 上,就分散了插入的数据;主键自动建立了唯一索引,因此唯一索引也能确保数据的唯一性(即实体完整性);索引码越小,定位就越直接;新建的索引效能最好, 因此定期更新索引非常必要。索引也有代价:有空间开销,建立它也要花费时间,在进行Insert、Delete和Update*作时,也有维护代价。索引 有两种:聚族索引和非聚族索引。一个表只能有一个聚族索引,可有多个非聚族索引。使用聚族索引查询数据要比使用非聚族索引快。在建索引前,应利用数据库系 统函数估算索引的大小。

  ① 聚族索引(Clustered Index):聚族索引的数据页按物理有序储存,占用空间小。选择策略是,被用于Where子句的列:包括范围查询、模糊查询或高度重复的列(连续磁盘扫 描);被用于连接Join*作的列;被用于Order by和Group by子句的列。聚族索引不利于插入*作,另外没有必要用主键建聚族索引。

   ② 非聚族索引(Nonclustered Index):与聚族索引相比,占用空间大,而且效率低。选择策略是,被用于Where子句的列:包括范围查询、模糊查询(在没有聚族索引时)、主键或外 键列、点(指针类)或小范围(返回的结果域小于整表数据的20%)查询;被用于连接Join*作的列、主键列(范围查询);被用于Order by和Group by子句的列;需要被覆盖的列。对只读表建多个非聚族索引有利。索引也有其弊端,一是创建索引要耗费时间,二是索引要占有大量磁盘空间,三是增加了维护代 价(在修改带索引的数据列时索引会减缓修改速度)。那么,在哪种情况下不建索引呢?对于小表(数据小于5页)、小到中表(不直接访问单行数据或结果集不用 排序)、单值域(返回值密集)、索引列值太长(大于20bitys)、容易变化的列、高度重复的列、Null值列,对没有被用于Where子语句和 Join查询的列都不能建索引。另外,对主要用于数据录入的,尽可能少建索引。当然,也要防止建立无效索引,当Where语句中多于5个条件时,维护索引 的开销大于索引的效益,这时,建立临时表存储有关数据更有效。

  批量导入数据时的注意事项:在实际应用中,大批量的计算(如电信话 单计费)用C语言程序做,这种基于主外键关系数据计算而得的批量数据(文本文件),可利用系统的自身功能函数(如Sybase的BCP命令)快速批量导 入,在导入数据库表时,可先删除相应库表的索引,这有利于加快导入速度,减少导入时间。在导入后再重建索引以便优化查询。

  (4) 锁:锁是并行处理的重要机制,能保持数据并发的一致性,即按事务进行处理;系统利用锁,保证数据完整性。因此,我们避免不了死锁,但在设计时可以充分考虑 如何避免长事务,减少排它锁时间,减少在事务中与用户的交互,杜绝让用户控制事务的长短;要避免批量数据同时执行,尤其是耗时并用到相同的数据表。锁的征 用:一个表同时只能有一个排它锁,一个用户用时,其它用户在等待。若用户数增加,则Server的性能下降,出现“假死”现象。如何避免死锁呢?从页级锁 到行级锁,减少了锁征用;给小表增加无效记录,从页级锁到行级锁没有影响,若在同一页内竞争有影响,可选择合适的聚族索引把数据分配到不同的页面;创建冗 余表;保持事务简短;同一批处理应该没有网络交互。

  (5)查询优化规则:在访问数据库表的数据(Access Data)时,要尽可能避免排序(Sort)、连接(Join)和相关子查询*作。经验告诉我们,在优化查询时,必须做到:
  ① 尽可能少的行;
  ② 避免排序或为尽可能少的行排序,若要做大量数据排序,最好将相关数据放在临时表中*作;用简单的键(列)排序,如整型或短字符串排序;
  ③ 避免表内的相关子查询;
  ④ 避免在Where子句中使用复杂的表达式或非起始的子字符串、用长字符串连接;
  ⑤ 在Where子句中多使用“与”(And)连接,少使用“或”(Or)连接;
  ⑥ 利用临时数据库。在查询多表、有多个连接、查询复杂、数据要过滤时,可以建临时表(索引)以减少I/O。但缺点是增加了空间开销。
除非每个列都有索引支持,否则在有连接的查询时分别找出两个动态索引,放在工作表中重新排序。

  3 基本表扩展设计
   基于第三范式设计的库表虽然有其优越性(见本文第一部分),然而在实际应用中有时不利于系统运行性能的优化:如需要部分数据时而要扫描整表,许多过程同 时竞争同一数据,反复用相同行计算相同的结果,过程从多表获取数据时引发大量的连接*作,当数据来源于多表时的连接*作;这都消耗了磁盘I/O和CPU时 间。

  尤其在遇到下列情形时,我们要对基本表进行扩展设计:许多过程要频繁访问一个表、子集数据访问、重复计算和冗余数据,有时用户要求一些过程优先或低的响应时间。

  如何避免这些不利因素呢?根据访问的频繁程度对相关表进行分割处理、存储冗余数据、存储衍生列、合并相关表处理,这些都是克服这些不利因素和优化系统运行的有效途径。

  3.1 分割表或储存冗余数据
  分割表分为水平分割表和垂直分割表两种。分割表增加了维护数据完整性的代价。
水 平分割表:一种是当多个过程频繁访问数据表的不同行时,水平分割表,并消除新表中的冗余数据列;若个别过程要访问整个数据,则要用连接*作,这也无妨分割 表;典型案例是电信话单按月分割存放。另一种是当主要过程要重复访问部分行时,最好将被重复访问的这些行单独形成子集表(冗余储存),这在不考虑磁盘空间 开销时显得十分重要;但在分割表以后,增加了维护难度,要用触发器立即更新、或存储过程或应用代码批量更新,这也会增加额外的磁盘I/O开销。

   垂直分割表(不破坏第三范式),一种是当多个过程频繁访问表的不同列时,可将表垂直分成几个表,减少磁盘I/O(每行的数据列少,每页存的数据行就多, 相应占用的页就少),更新时不必考虑锁,没有冗余数据。缺点是要在插入或删除数据时要考虑数据的完整性,用存储过程维护。另一种是当主要过程反复访问部分 列时,最好将这部分被频繁访问的列数据单独存为一个子集表(冗余储存),这在不考虑磁盘空间开销时显得十分重要;但这增加了重叠列的维护难度,要用触发器 立即更新、或存储过程或应用代码批量更新,这也会增加额外的磁盘I/O开销。垂直分割表可以达到最大化利用Cache的目的。

  总之,为主要过程分割表的方法适用于:各个过程需要表的不联结的子集,各个过程需要表的子集,访问频率高的主要过程不需要整表。在主要的、频繁访问的主表需要表的子集而其它主要频繁访问的过程需要整表时则产生冗余子集表。
注意,在分割表以后,要考虑重新建立索引。

  3.2 存储衍生数据
  对一些要做大量重复性计算的过程而言,若重复计算过程得到的结果相同(源列数据稳定,因此计算结果也不变),或计算牵扯多行数据需额外的磁盘I/O开销,或计算复杂需要大量的CPU时间,就考虑存储计算结果(冗余储存)。现予以分类说明:
  若在一行内重复计算,就在表内增加列存储结果。但若参与计算的列被更新时,必须要用触发器更新这个新列。

  若对表按类进行重复计算,就增加新表(一般而言,存放类和结果两列就可以了)存储相关结果。但若参与计算的列被更新时,就必须要用触发器立即更新、或存储过程或应用代码批量更新这个新表。

  若对多行进行重复性计算(如排名次),就在表内增加列存储结果。但若参与计算的列被更新时,必须要用触发器或存储过程更新这个新列。

  总之,存储冗余数据有利于加快访问速度;但违反了第三范式,这会增加维护数据完整性的代价,必须用触发器立即更新、或存储过程或应用代码批量更新,以维护数据的完整性。

  3.3 消除昂贵结合
   对于频繁同时访问多表的一些主要过程,考虑在主表内存储冗余数据,即存储冗余列或衍生列(它不依赖于主键),但破坏了第三范式,也增加了维护难度。在源 表的相关列发生变化时,必须要用触发器或存储过程更新这个冗余列。当主要过程总同时访问两个表时可以合并表,这样可以减少磁盘I/O*作,但破坏了第三范 式,也增加了维护难度。对父子表和1:1关系表合并方法不同:合并父子表后,产生冗余表;合并1:1关系表后,在表内产生冗余数据。

  4 数据库对象的放置策略
  数据库对象的放置策略是均匀地把数据分布在系统的磁盘中,平衡I/O访问,避免I/O瓶颈。

  ⑴ 访问分散到不同的磁盘,即使用户数据尽可能跨越多个设备,多个I/O运转,避免I/O竞争,克服访问瓶颈;分别放置随机访问和连续访问数据。
  ⑵ 分离系统数据库I/O和应用数据库I/O。把系统审计表和临时库表放在不忙的磁盘上。
  ⑶ 把事务日志放在单独的磁盘上,减少磁盘I/O开销,这还有利于在障碍后恢复,提高了系统的安全性。
  ⑷ 把频繁访问的“活性”表放在不同的磁盘上;把频繁用的表、频繁做Join*作的表分别放在单独的磁盘上,甚至把把频繁访问的表的字段放在不同的磁盘上,把访问分散到不同的磁盘上,避免I/O争夺;
   ⑸ 利用段分离频繁访问的表及其索引(非聚族的)、分离文本和图像数据。段的目的是平衡I/O,避免瓶颈,增加吞吐量,实现并行扫描,提高并发度,最大化磁盘 的吞吐量。利用逻辑段功能,分别放置“活性”表及其非聚族索引以平衡I/O。当然最好利用系统的默认段。另外,利用段可以使备份和恢复数据更加灵活,使系 统授权更加灵活。



Resources:

Friday, August 10, 2007

在ubuntu下面配置subversion。

http://wiki.ubuntu.org.cn/SubVersion
这篇文章是ubuntu中文网站上的文档,跟着安装了一遍, 不错, 成功了。

Monday, June 18, 2007

硬盘安装ubuntu7.0.4

因为对6.10的那个ubuntu的启动画面相当不满,所以一直想升级到7.0.4,但是刚刚听别人说7.0.4的启动画面一样的老土,只好先停在这里,再等等吧。。。一个不幸的事情是我的笔记本光驱好像坏了,逼得我现在一门心思的再琢磨怎么从硬盘安装,在网上找了写资料,决定自己先记录下来,虽然现在不升级,正所谓居安思危,有备无患。
-----------------------------
1.从 http://releases.ubuntu.com/feisty/ 下载 ubuntu-7.04-alternate-i386.iso 并放到C:\,并且确认C:为FAT32分区 (这点就要命,我的window分区都是ntfs,好像是因为intz什么的不支持ntfs格式)
2.
下载 http://archive.ubuntu.com/ubuntu/dists/feisty/main/installer-i386/current/images/hd-media/ 里的文件,同样拷贝到C:\
initrd.gz
vmlinuz
3.有的文档说要执行这一步,有的没说,我也写在这里:
下载grub_for_dos-0.4.2,将里面的 grldr提取 复制到 C:\,编辑C:\BOOT.INI,加入一行代码:C:\GRLDR=”GRUB”
4.启动到grub,出现菜单时按下C键,进入grub的命令行模式,输入如下命令,即可启动安装程序:
grub> kernel (hd0,0)/vmlinuz root=/dev/ram ramdisk_size=256000 devfs=mount,dall
grub> initrd (hd0,0)/initrd.gz
grub> boot
天,不知道/dev/ram是什么意思? 为什么不是/dev/hd0呢?
理论上,就应该可以看到安装界面了!