2007年5月17日
http://www.tkk7.com/zhuanggl/archive/2010/07/14/326119.html
http://blog.csdn.net/cleanfield/article/details/6339428
http://tech.idv2.com/2008/07/10/memcached-001/
axis可以根據(jù)wsdl文件生成對應(yīng)的客戶端、服務(wù)端java代碼,非常方便,具體使用方式為: java org.apache.axis.wsdl.WSDL2Java-t -s D:\test\webservice\TestCaseAndWsdl\TestService\TaskService.wsdl 上述classpath沒有設(shè)置,需要自行設(shè)定引用jar文件。
當(dāng)關(guān)系數(shù)據(jù)庫試圖在一個單一表中存儲數(shù) TB 的數(shù)據(jù)時,總性能經(jīng)常會降低。顯然,對所有數(shù)據(jù)編索引不僅對于讀而且對于寫都很耗時。因為 NoSQL 數(shù)據(jù)商店尤其適合存儲大型數(shù)據(jù)(如 Google 的 Bigtable),顯然 NoSQL 是一種非關(guān)系數(shù)據(jù)庫方法。對于傾向于使用 ACID-ity 和實體結(jié)構(gòu)關(guān)系數(shù)據(jù)庫的開發(fā)人員及需要這種結(jié)構(gòu)的項目來說,切分是一個令人振奮的可選方法。 切分 是數(shù)據(jù)庫分區(qū)的一個分支,但是它不是本地數(shù)據(jù)庫技術(shù) — 切分發(fā)生在應(yīng)用程序級別。在各種切分實現(xiàn)中,Hibernate Shards 是 Java™ 技術(shù)世界中最受歡迎的一個。這個靈活絕妙的項目可以讓您使用映射至邏輯數(shù)據(jù)庫的 POJO 對切分?jǐn)?shù)據(jù)集進(jìn)行幾乎無縫操作(我將在下文簡要介紹 “幾乎” 的原因)。使用 Hibernate Shards 時,您無須將您的 POJO 特別映射至切分 — 您可以像使用 Hibernate 方法對任何常見關(guān)系數(shù)據(jù)庫進(jìn)行映射時一樣對其進(jìn)行映射。Hibernate Shards 可以為您管理低級別的切分任務(wù)。 事實上,Hibernate Shards 的編碼工作比較簡單。其中關(guān)鍵的部分在于判斷 如何進(jìn)行切分以及對什么進(jìn)行切分。 切分簡介 數(shù)據(jù)庫切分 是一個固有的關(guān)系流程,可以通過一些邏輯數(shù)據(jù)塊將一個表的行分為不同的小組。例如,如果您正在根據(jù)時間戳對一個名為 foo 的超大型表進(jìn)行分區(qū),2010 年 8 月之前的所有數(shù)據(jù)都將進(jìn)入分區(qū) A,而之后的數(shù)據(jù)則全部進(jìn)入分區(qū) B。分區(qū)可以加快讀寫速度,因為它們的目標(biāo)是單獨分區(qū)中的較小型數(shù)據(jù)集。 分區(qū)功能并不總是可用的(MySQL 直到 5.1 版本后才支持),而且其需要的商業(yè)系統(tǒng)的成本也讓人望而卻步。更重要的是,大部分分區(qū)實現(xiàn)在同一個物理機(jī)上存儲數(shù)據(jù),所以受到硬件基礎(chǔ)的影響。除此之外, 分區(qū)也不能鑒別硬件的可靠性或者說缺乏可靠性。因此,很多智慧的人們開始尋找進(jìn)行伸縮的新方法。 切分 實質(zhì)上是數(shù)據(jù)庫級別的分區(qū):它不是通過數(shù)據(jù)塊分割數(shù)據(jù)表的行,而是通過一些邏輯數(shù)據(jù)元素對數(shù)據(jù)庫本身進(jìn)行分割(通常跨不同的計算機(jī))。也就是說,切分不是將數(shù)據(jù)表 分割成小塊,而是將整個數(shù)據(jù)庫 分割成小塊。 切分的一個典型示例是基于根據(jù)區(qū)域?qū)σ粋€存儲世界范圍客戶數(shù)據(jù)的大型數(shù)據(jù)庫進(jìn)行分割:切分 A 用于存儲美國的客戶信息,切分 B 用戶存儲亞洲的客戶信息,切分 C 歐洲,等。這些切分分別處于不同的計算機(jī)上,且每個切分將存儲所有相關(guān)數(shù)據(jù),如客戶喜好或訂購歷史。 切分的好處(如分區(qū)一樣)在于它可以壓縮大型數(shù)據(jù):單獨的數(shù)據(jù)表在每個切分中相對較小,這樣就可以支持更快速的讀寫速度,從而提高性能。切分 還可以改善可靠性,因為即便一個切分意外失效,其他切分仍然可以服務(wù)數(shù)據(jù)。而且因為切分是在應(yīng)用程序?qū)用孢M(jìn)行的,您可以對不支持常規(guī)分區(qū)的數(shù)據(jù)庫進(jìn)行切分 處理。資金成本較低同樣也是一個潛在優(yōu)勢。 切分和策略 像很多其他技術(shù)一樣,進(jìn)行切分時也需要作出部分妥協(xié)。因為切分不是一項本地數(shù)據(jù)庫技術(shù) — 也就是說,必須在應(yīng)用程序中實現(xiàn) —在開始切分之前需要制定出您的切分策略。進(jìn)行切分時主鍵和跨切分查詢都扮演重要角色,主要通過定義您不可以做什么實現(xiàn)。 主鍵 切分利用多個數(shù)據(jù)庫,其中所有數(shù)據(jù)庫都獨立起作用,不干涉其他切分。因此,如果您依賴于數(shù)據(jù)庫序列(如自動主鍵生成),很有可能在一個數(shù)據(jù)庫集中將出現(xiàn)同 一個主鍵。可以跨分布式數(shù)據(jù)庫協(xié)調(diào)序列,但是這樣會增加系統(tǒng)的復(fù)雜程度。避免相同主鍵最安全的方法就是讓應(yīng)用程序(應(yīng)用程序?qū)⒐芾砬蟹窒到y(tǒng))生成主鍵。 跨切分查詢 大部分切分實現(xiàn)(包括 Hibernate Shards)不支持跨切分查詢,這就意味著,如果您想利用不同切分的兩個數(shù)據(jù)集,就必須處理額外的長度。(有趣的是,Amazon 的 SimpleDB 也禁止跨域查詢)例如,如果將美國客戶信息存儲在切分 1 中,還需要將所有相關(guān)數(shù)據(jù)存儲在此。如果您嘗試將那些數(shù)據(jù)存儲在切分 2 中,情況就會變得復(fù)雜,系統(tǒng)性能也可能受影響。這種情況還與之前提到的一點有關(guān) — 如果您因為某種原因需要進(jìn)行跨切分連接,最好采用一種可以消除重復(fù)的方式管理鍵! 很明顯,在建立數(shù)據(jù)庫前必須全面考慮切分策略。一旦選擇了一個特定的方向之后,您差不多就被它綁定了 — 進(jìn)行切分后很難隨便移動數(shù)據(jù)了。 一個策略示例 因為切分將您綁定在一個線型數(shù)據(jù)模型中(也就是說,您無法輕松連接不同切分中的數(shù)據(jù)),您必須對如何在每個切分中對數(shù)據(jù)進(jìn)行邏輯組織有一個清 晰的概念。這可以通過聚焦域中的主要節(jié)點實現(xiàn)。如在一個電子商務(wù)系統(tǒng)中,主要節(jié)點可以是一個訂單或者一個客戶。因此,如果您選擇 “客戶” 作為切分策略的節(jié)點,那么與客戶有關(guān)的所有數(shù)據(jù)將移動至各自的切分中,但是您仍然必須選擇將這些數(shù)據(jù)移動至哪個切分。 對于客戶來說,您可以根據(jù)所在地(歐洲、亞洲、非洲等)切分,或者您也可以根據(jù)其他元素進(jìn)行切分。這由您決定。但是,您的切分策略應(yīng)該包含將 數(shù)據(jù)均勻分布至所有切分的方法。切分的總體概念是將大型數(shù)據(jù)集分割為小型數(shù)據(jù)集;因此,如果一個特定的電子商務(wù)域包含一個大型的歐洲客戶集以及一個相對小 的美國客戶集,那么基于客戶所在地的切分可能沒有什么意義。 回到比賽 — 使用切分! 現(xiàn)在讓我們回到我經(jīng)常提到的賽跑應(yīng)用程序示例中,我可以根據(jù)比賽或參賽者進(jìn)行切分。在本示例中,我將根據(jù)比賽進(jìn)行切分,因為我看到域是根據(jù)參 加不同比賽的參賽者進(jìn)行組織的。因此,比賽是域的根。我也將根據(jù)比賽距離進(jìn)行切分,因為比賽應(yīng)用程序包含不同長度和不同參賽者的多項比賽。 請注意:在進(jìn)行上述決定時,我已經(jīng)接受了一個妥協(xié):如果一個參賽者參加了不止一項比賽,他們分屬不同的切分,那該怎么辦 呢?Hibernate Shards (像大多數(shù)切分實現(xiàn)一樣)不支持跨切分連接。我必須忍受這些輕微不便,允許參賽者被包含在多個切分中 — 也就是說,我將在參賽者參加的多個比賽切分中重建該參賽者。 為了簡便起見,我將創(chuàng)建兩個切分:一個用于 10 英里以下的比賽;另一個用于 10 英里以上的比賽。 實現(xiàn) Hibernate Shards Hibernate Shards 幾乎可以與現(xiàn)有 Hibernate 項目無縫結(jié)合使用。唯一問題是 Hibernate Shards 需要一些特定信息和行為。比如,需要一個切分訪問策略、一個切分選擇策略和一個切分處理策略。這些是您必須實現(xiàn)的接口,雖然部分情況下,您可以使用默認(rèn)策 略。我們將在后面的部分逐個了解各個接口。 ShardAccessStrategy 執(zhí)行查詢時,Hibernate Shards 需要一個決定首個切分、第二個切分及后續(xù)切分的機(jī)制。Hibernate Shards 無需確定查詢什么(這是 Hibernate Core 和基礎(chǔ)數(shù)據(jù)庫需要做的),但是它確實意識到,在獲得答案之前可能需要對多個切分進(jìn)行查詢。因此,Hibernate Shards 提供了兩種極具創(chuàng)意的邏輯實現(xiàn)方法:一種方法是根據(jù)序列機(jī)制(一次一個)對切分進(jìn)行查詢,直到獲得答案為止;另一種方法是并行訪問策略,這種方法使用一個 線程模型一次對所有切分進(jìn)行查詢。 為了使問題簡單,我將使用序列策略,名稱為 SequentialShardAccessStrategy 。我們將稍后對其進(jìn)行配置。 ShardSelectionStrategy 當(dāng)創(chuàng)建一個新對象時(例如,當(dāng)通過 Hibernate 創(chuàng)建一個新 Race 或 Runner 時),Hibernate Shards 需要知道需將對應(yīng)的數(shù)據(jù)寫入至哪些切分。因此,您必須實現(xiàn)該接口并對切分邏輯進(jìn)行編碼。如果您想進(jìn)行默認(rèn)實現(xiàn),有一個名為 RoundRobinShardSelectionStrategy 的策略,它使用一個循環(huán)策略將數(shù)據(jù)輸入切分中。 對于賽跑應(yīng)用程序,我需要提供根據(jù)比賽距離進(jìn)行切分的行為。因此,我們需要實現(xiàn) ShardSelectionStrategy 接口并提供依據(jù) Race 對象的 distance 采用 selectShardIdForNewObject 方法進(jìn)行切分的簡易邏輯。(我將稍候在 Race 對象中展示。) 運行時,當(dāng)在我的域?qū)ο笊险{(diào)用某一類似 save 的方法時,該接口的行為將被深層用于 Hibernate 的核心。 清單 1. 一個簡單的切分選擇策略 import org.hibernate.shards.ShardId; import org.hibernate.shards.strategy.selection.ShardSelectionStrategy;
public class RacerShardSelectionStrategy implements ShardSelectionStrategy {
public ShardId selectShardIdForNewObject(Object obj) { if (obj instanceof Race) { Race rce = (Race) obj; return this.determineShardId(rce.getDistance()); } else if (obj instanceof Runner) { Runner runnr = (Runner) obj; if (runnr.getRaces().isEmpty()) { throw new IllegalArgumentException("runners must have at least one race"); } else { double dist = 0.0; for (Race rce : runnr.getRaces()) { dist = rce.getDistance(); break; } return this.determineShardId(dist); } } else { throw new IllegalArgumentException("a non-shardable object is being created"); } }
private ShardId determineShardId(double distance){ if (distance > 10.0) { return new ShardId(1); } else { return new ShardId(0); } } }
| 如您在 清單 1 中所看到的,如果持久化對象是一場 Race ,那么其距離被確定,而且(因此)選擇了一個切分。在這種情況下,有兩個切分:0 和 1,其中切分 1 中包含 10 英里以上的比賽,切分 0 中包含所有其他比賽。 如果持久化一個 Runner 或其他對象,情況會稍微復(fù)雜一些。我已經(jīng)編碼了一個邏輯規(guī)則,其中有三個原則: - 一名
Runner 在沒有對應(yīng)的 Race 時無法存在。 - 如果
Runner 被創(chuàng)建時參加了多場 Race s,這名 Runner 將被持久化到尋找到的首場 Race 所屬的切分中。(順便說一句,該原則對未來有負(fù)面影響。) - 如果還保存了其他域?qū)ο螅F(xiàn)在將引發(fā)一個異常。
然后,您就可以擦掉額頭的熱汗了,因為大部分艱難的工作已經(jīng)搞定了。隨著比賽應(yīng)用程序的增長,我所使用的邏輯可能會顯得不夠靈活,但是它完全可以順利地完成這次演示! ShardResolutionStrategy 當(dāng)通過鍵搜索一個對象時,Hibernate Shards 需要一種可以決定首個切分的方法。將需要使用 SharedResolutionStrategy 接口對其進(jìn)行指引。 如我之前提到的那樣,切分迫使您重視主鍵,因為您將需要親自管理這些主鍵。幸運的是,Hibernate 在提供鍵或 UUID 生成方面表現(xiàn)良好。因此 Hibernate Shards 創(chuàng)造性地提供一個 ID 生成器,名為 ShardedUUIDGenerator ,它可以靈活地將切分 ID 信息嵌入到 UUID 中。 如果您最后使用 ShardedUUIDGenerator 進(jìn)行鍵生成(我在本文中也將采取這種方法),那么您也可以使用 Hibernate Shards 提供的創(chuàng)新 ShardResolutionStrategy 實現(xiàn),名為 AllShardsShardResolutionStrategy ,這可以決定依據(jù)一個特定對象的 ID 搜索什么切分。 配置好 Hibernate Shards 工作所需的三個接口后,我們就可以對切分示例應(yīng)用程序的第二步進(jìn)行實現(xiàn)了。現(xiàn)在應(yīng)該啟動 Hibernate 的 SessionFactory 了。 配置 Hibernate Shards Hibernate 的其中一個核心接口對象是它的 SessionFactory 。Hibernate 的所有神奇都是在其配置 Hibernate 應(yīng)用程序過程中通過這個小對象實現(xiàn)的,例如,通過加載映射文件和配置。如果您使用了注釋或 Hibernate 珍貴的 .hbm 文件,那么您還需要一個 SessionFactory 來讓 Hibernate 知道哪些對象是可以持久化的,以及將它們持久化到 哪里。 因此,使用 Hibernate Shards 時,您必須使用一個增強(qiáng)的 SessionFactory 類型來配置多個數(shù)據(jù)庫。它可以被命名為 ShardedSessionFactory ,而且它當(dāng)然是 SessionFactory 類型的。當(dāng)創(chuàng)建一個 ShardedSessionFactory 時,您必須提供之前配置好的三個切分實現(xiàn)類型(ShardAccessStrategy 、ShardSelectionStrategy 和 ShardResolutionStrategy )。您還需提供 POJO 所需的所有映射文件。(如果您使用一個基于備注的 Hibernate POJO 配置,情況可能會有所不同。)最后,一個 ShardedSessionFactory 示例需要每個切分都對應(yīng)多個 Hibernate 配置文件。 創(chuàng)建一個 Hibernate 配置 我已經(jīng)創(chuàng)建了一個 ShardedSessionFactoryBuilder 類型,它有一個主要方法 createSessionFactory ,可以創(chuàng)建一個配置合理的 SessionFactory 。之后,我將將所有的一切都與 Spring 連接在一起(現(xiàn)在誰不使用一個 IOC 容器?)。現(xiàn)在,清單 2 顯示了 ShardedSessionFactoryBuilder 的主要作用:創(chuàng)建一個 Hibernate 配置 : 清單 2. 創(chuàng)建一個 Hibernate 配置 private Configuration getPrototypeConfig(String hibernateFile, List<String> resourceFiles) { Configuration config = new Configuration().configure(hibernateFile); for (String res : resourceFiles) { configs.addResource(res); } return config; }
| 如您在 清單 2 中所看到的,可以從 Hibernate 配置文件中創(chuàng)建了一個簡單的 Configuration 。 該文件包含如下信息,如使用的是什么類型的數(shù)據(jù)庫、用戶名和密碼等,以及所有必須的資源文件,如 POJO 所用的 .hbm 文件。在進(jìn)行切分的情況下,您通常需要使用多個數(shù)據(jù)庫配置,但是 Hibernate Shards 支持您僅使用一個 hibernate.cfg.xml 文件,從而簡化了整個過程(但是,如您在 清單 4 中所看到的,您將需要對使用的每一個切分準(zhǔn)備一個 hibernate.cfg.xml 文件)。 下一步,在清單 3 中,我將所有的切分配置都收集到了一個 List 中: 清單 3. 切分配置列表 List<ShardConfiguration> shardConfigs = new ArrayList<ShardConfiguration>(); for (String hibconfig : this.hibernateConfigurations) { shardConfigs.add(buildShardConfig(hibconfig)); }
| Spring 配置 在 清單 3 中,對 hibernateConfigurations 的引用指向了 String s List ,其中每個 String 都包含了 Hibernate 配置文件的名字。該 List 通過 Spring 自動連接。清單 4 是我的 Spring 配置文件中的一段摘錄: 清單 4. Spring 配置文件中的一部分 <bean id="shardedSessionFactoryBuilder" class="org.disco.racer.shardsupport.ShardedSessionFactoryBuilder"> <property name="resourceConfigurations"> <list> <value>racer.hbm.xml</value> </list> </property> <property name="hibernateConfigurations"> <list> <value>shard0.hibernate.cfg.xml</value> <value>shard1.hibernate.cfg.xml</value> </list> </property> </bean>
| 如您在 清單 4 中所看到的,ShardedSessionFactoryBuilder 正在與一個 POJO 映射文件和兩個切分配置文件連接。清單 5 中是 POJO 文件的一段摘錄: 清單 5. 比賽 POJO 映射 <class name="org.disco.racer.domain.Race" table="race"dynamic-update="true" dynamic-insert="true">
<id name="id" column="RACE_ID" unsaved-value="-1"> <generator class="org.hibernate.shards.id.ShardedUUIDGenerator"/> </id>
<set name="participants" cascade="save-update" inverse="false" table="race_participants" lazy="false"> <key column="race_id"/> <many-to-many column="runner_id" class="org.disco.racer.domain.Runner"/> </set>
<set name="results" inverse="true" table="race_results" lazy="false"> <key column="race_id"/> <one-to-many class="org.disco.racer.domain.Result"/> </set>
<property name="name" column="NAME" type="string"/> <property name="distance" column="DISTANCE" type="double"/> <property name="date" column="DATE" type="date"/> <property name="description" column="DESCRIPTION" type="string"/> </class>
| 請注意,清單 5 中的 POJO 映射的唯一獨特方面是 ID 的生成器類 — 這就是 ShardedUUIDGenerator ,它(如您想象的一樣)將切分 ID 信息嵌入到 UUID 中。這就是我的 POJO 映射中切分的唯一獨特方面。 切分配置文件 下一步,如清單 6 中所示,我已經(jīng)配置了一個切分 — 在本示例中,除切分 ID 和連接信息外,切分 0 和切分 1 的文件是一樣的。 清單 6. Hibernate Shards 配置文件 <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory name="HibernateSessionFactory0"> <property name="dialect">org.hibernate.dialect.HSQLDialect</property> <property name="connection.driver_class">org.hsqldb.jdbcDriver</property> <property name="connection.url"> jdbc:hsqldb:file:/.../db01/db01 </property> <property name="connection.username">SA</property> <property name="connection.password"></property> <property name="hibernate.connection.shard_id">0</property> <property name="hibernate.shard.enable_cross_shard_relationship_checks">true </property> </session-factory> </hibernate-configuration>
| 如其名字所示,enable_cross_shard_relationship_checks 屬性對跨切分關(guān)系進(jìn)行了檢查。根據(jù) Hibernate Shards 文檔記錄,該屬性非常耗時,在生成環(huán)境中應(yīng)該關(guān)閉。 最后,ShardedSessionFactoryBuilder 通過創(chuàng)建 ShardStrategyFactory ,然后添加三個類型(包括 清單 1 中的 RacerShardSelectionStrategy ),將一切都整合到了一起,如清單 7 中所示: 清單 7. 創(chuàng)建 ShardStrategyFactory private ShardStrategyFactory buildShardStrategyFactory() { ShardStrategyFactory shardStrategyFactory = new ShardStrategyFactory() { public ShardStrategy newShardStrategy(List<ShardId> shardIds) { ShardSelectionStrategy pss = new RacerShardSelectionStrategy(); ShardResolutionStrategy prs = new AllShardsShardResolutionStrategy(shardIds); ShardAccessStrategy pas = new SequentialShardAccessStrategy(); return new ShardStrategyImpl(pss, prs, pas); } }; return shardStrategyFactory; }
| 最后,我執(zhí)行了那個名為 createSessionFactory 的絕妙方法,在本示例中創(chuàng)建了一個 ShardedSessionFactory ,如清單 8 所示: 清單 8. 創(chuàng)建 ShardedSessionFactory public SessionFactory createSessionFactory() { Configuration prototypeConfig = this.getPrototypeConfig (this.hibernateConfigurations.get(0), this.resourceConfigurations);
List<ShardConfiguration> shardConfigs = new ArrayList<ShardConfiguration>(); for (String hibconfig : this.hibernateConfigurations) { shardConfigs.add(buildShardConfig(hibconfig)); }
ShardStrategyFactory shardStrategyFactory = buildShardStrategyFactory(); ShardedConfiguration shardedConfig = new ShardedConfiguration( prototypeConfig, shardConfigs,shardStrategyFactory); return shardedConfig.buildShardedSessionFactory(); }
| 使用 Spring 連接域?qū)ο?/a> 現(xiàn)在可以深呼吸一下了,因為我們馬上就成功了。到目前為止,我已經(jīng)創(chuàng)建一個可以合理配置 ShardedSessionFactory 的生成器類,其實就是實現(xiàn) Hibernate 無處不在的 SessionFactory 類型。ShardedSessionFactory 完成了切分中所有的神奇。它利用我在 清單 1 中所部署的切分選擇策略,并從我配置的兩個切分中讀寫數(shù)據(jù)。(清單 6 顯示了切分 0 和切分 1 的配置幾乎相同。) 現(xiàn)在我需要做的就是連接我的域?qū)ο螅诒臼纠校驗樗鼈円蕾囉?Hibernate,需要一個 SessionFactory 類型進(jìn)行工作。我將僅使用我的 ShardedSessionFactoryBuilder 提供一種 SessionFactory 類型,如清單 9 中所示: 清單 9. 在 Spring 中連接 POJO <bean id="mySessionFactory" factory-bean="shardedSessionFactoryBuilder" factory-method="createSessionFactory"> </bean>
<bean id="race_dao" class="org.disco.racer.domain.RaceDAOImpl"> <property name="sessionFactory"> <ref bean="mySessionFactory"/> </property> </bean>
| 如您在 清單 9 中所看到的,我首先在 Spring 中創(chuàng)建了一個類似工廠的 bean;也就是說,我的 RaceDAOImpl 類型有一個名為 sessionFactory 的屬性,是 SessionFactory 類型。之后,mySessionFactory 引用通過在 ShardedSessionFactoryBuilder 上調(diào)用 createSessionFactory 方法創(chuàng)建了一個 SessionFactory 示例,如 清單 4 中所示。 當(dāng)我為我的 Race 對象示例使用 Spring(我主要將其作為一個巨型工廠使用,以返回預(yù)配置的對象)時,一切事情就都搞定了。雖然沒有展示,RaceDAOImpl 類型是一個利用 Hibernate 模板進(jìn)行數(shù)據(jù)存儲和檢索的對象。我的 Race 類型包含一個 RaceDAOImpl 示例,它將所有與數(shù)據(jù)商店相關(guān)的活動都推遲至此。很默契,不是嗎? 請注意,我的 DAO 與 Hibernate Shards 在代碼方面并沒有綁定,而是通過配置進(jìn)行了綁定。配置(如 清單 5 中的)將它們綁定在一個特定切分 UUID 生成方案中,也就是說了我可以在需要切分時從已有 Hibernate 實現(xiàn)中重新使用域?qū)ο蟆?/p> 切分:使用 easyb 的測試驅(qū)動 接下來,我需要驗證我的切分實現(xiàn)可以工作。我有兩個數(shù)據(jù)庫并通過距離進(jìn)行切分,所以當(dāng)我創(chuàng)建一場馬拉松時(10 英里以上的比賽),該 Race 示例應(yīng)在切分 1 中找到。一個小型的比賽,如 5 公里的比賽(3.1 英里),將在切分 0 中找到。創(chuàng)建一場 Race 后,我可以檢查單個數(shù)據(jù)庫的記錄。 在清單 10 中,我已經(jīng)創(chuàng)建了一場馬拉松,然后繼續(xù)驗證記錄確實是在切分 1 中而非切分 0 中。使事情更加有趣(和簡單)的是,我使用了 easyb,這是一個基于 Groovy 的行為驅(qū)動開發(fā)架構(gòu),利用自然語言驗證。easyb 也可以輕松處理 Java 代碼。即便不了解 Groovy 或 easyb,您也可以通過查看清單 10 中的代碼,看到一切如期進(jìn)行。(請注意,我?guī)椭鷦?chuàng)建了 easyb,并且在 developerWorks 中對這個話題發(fā)表過文章。) 清單 10. 一個驗證切分正確性的 easyb 故事中一段摘錄 scenario "races greater than 10.0 miles should be in shard 1 or db02", { given "a newly created race that is over 10.0 miles", { new Race("Leesburg Marathon", new Date(), 26.2, "Race the beautiful streets of Leesburg!").create() } then "everything should work fine w/respect to Hibernate", { rce = Race.findByName("Leesburg Marathon") rce.distance.shouldBe 26.2 } and "the race should be stored in shard 1 or db02", { sql = Sql.newInstance(db02url, name, psswrd, driver) sql.eachRow("select race_id, distance, name from race where name=?", ["Leesburg Marathon"]) { row -> row.distance.shouldBe 26.2 } sql.close() } and "the race should NOT be stored in shard 0 or db01", { sql = Sql.newInstance(db01url, name, psswrd, driver) sql.eachRow("select race_id, distance, name from race where name=?", ["Leesburg Marathon"]) { row -> fail "shard 0 contains a marathon!" } sql.close() } }
| 當(dāng)然,我的工作還沒有完 — 我還需要創(chuàng)建一個短程比賽,并驗證其位于切分 0 中而非切分 1 中。您可以在本文提供的 代碼下載 中看到該驗證操作! 切分的利弊 切分可以增加應(yīng)用程序的讀寫速度,尤其是如果您的應(yīng)用程序包含大量數(shù)據(jù) — 如數(shù) TB — 或者您的域處于無限制發(fā)展中,如 Google 或 Facebook。 在進(jìn)行切分之前,一定要確定應(yīng)用程序的規(guī)模和增長對其有利。切分的成本(或者說缺點)包括對如何存儲和檢索數(shù)據(jù)的特定應(yīng)用程序邏輯進(jìn)行編碼的成本。進(jìn)行切分后,您多多少少都被鎖定在您的切分模型中,因為重新切分并非易事。 如果能夠正確實現(xiàn),切分可以用于解決傳統(tǒng) RDBMS 規(guī)模和速度問題。切分對于綁定于關(guān)系基礎(chǔ)架構(gòu)、無法繼續(xù)升級硬件以滿足大量可伸縮數(shù)據(jù)存儲要求的組織來說是一個非常成本高效的決策。
最后更新時間 2009-04-10 更新人 dormando@rydia.net 這里收集了經(jīng)常被問到的關(guān)于memcached的問題 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">集群架構(gòu)方面的問題 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached是怎么工作的? Memcached的神奇來自兩階段哈希(two-stage hash)。Memcached就像一個巨大的、存儲了很多<key,value>對的哈希表。通過key,可以存儲或查詢?nèi)我獾臄?shù)據(jù)。 客戶端可以把數(shù)據(jù)存儲在多臺memcached上。當(dāng)查詢數(shù)據(jù)時,客戶端首先參考節(jié)點列表計算出key的哈希值(階段一哈希),進(jìn)而選中一個節(jié)點; 客戶端將請求發(fā)送給選中的節(jié)點,然后memcached節(jié)點通過一個內(nèi)部的哈希算法(階段二哈希),查找真正的數(shù)據(jù)(item)。 舉個列子,假設(shè)有3個客戶端1, 2, 3,3臺memcached A, B, C: Client 1想把數(shù)據(jù)"barbaz"以key "foo"存儲。Client 1首先參考節(jié)點列表(A, B, C),計算key "foo"的哈希值,假設(shè)memcached B被選中。接著,Client 1直接connect到memcached B,通過key "foo"把數(shù)據(jù)"barbaz"存儲進(jìn)去。Client 2使用與Client 1相同的客戶端庫(意味著階段一的哈希算法相同),也擁有同樣的memcached列表(A, B, C)。 于是,經(jīng)過相同的哈希計算(階段一),Client 2計算出key "foo"在memcached B上,然后它直接請求memcached B,得到數(shù)據(jù)"barbaz"。 各種客戶端在memcached中數(shù)據(jù)的存儲形式是不同的(perl Storable, php serialize, java hibernate, JSON等)。一些客戶端實現(xiàn)的哈希算法也不一樣。但是,memcached服務(wù)器端的行為總是一致的。 最后,從實現(xiàn)的角度看,memcached是一個非阻塞的、基于事件的服務(wù)器程序。這種架構(gòu)可以很好地解決C10K problem ,并具有極佳的可擴(kuò)展性。 可以參考A Story of Caching ,這篇文章簡單解釋了客戶端與memcached是如何交互的。 memcached最大的優(yōu)勢是什么? 請仔細(xì)閱讀上面的問題(即memcached是如何工作的)。Memcached最大的好處就是它帶來了極佳的水平可擴(kuò)展性,特別是在一個巨大的系 統(tǒng)中。由于客戶端自己做了一次哈希,那么我們很容易增加大量memcached到集群中。memcached之間沒有相互通信,因此不會增加 memcached的負(fù)載;沒有多播協(xié)議,不會網(wǎng)絡(luò)通信量爆炸(implode)。memcached的集群很好用。內(nèi)存不夠了?增加幾臺 memcached吧;CPU不夠用了?再增加幾臺吧;有多余的內(nèi)存?在增加幾臺吧,不要浪費了。 基于memcached的基本原則,可以相當(dāng)輕松地構(gòu)建出不同類型的緩存架構(gòu)。除了這篇FAQ,在其他地方很容易找到詳細(xì)資料的。 看看下面的幾個問題吧,它們在memcached、服務(wù)器的local cache和MySQL的query cache之間做了比較。這幾個問題會讓您有更全面的認(rèn)識。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached和MySQL的query cache相比,有什么優(yōu)缺點? 把memcached引入應(yīng)用中,還是需要不少工作量的。MySQL有個使用方便的query cache,可以自動地緩存SQL查詢的結(jié)果,被緩存的SQL查詢可以被反復(fù)地快速執(zhí)行。Memcached與之相比,怎么樣呢?MySQL的query cache是集中式的,連接到該query cache的MySQL服務(wù)器都會受益。 - 當(dāng)您修改表時,MySQL的query cache會立刻被刷新(flush)。存儲一個memcached item只需要很少的時間,但是當(dāng)寫操作很頻繁時,MySQL的query cache會經(jīng)常讓所有緩存數(shù)據(jù)都失效。
- 在多核CPU上,MySQL的query cache會遇到擴(kuò)展問題(scalability issues)。在多核CPU上,query cache會增加一個全局鎖(global lock), 由于需要刷新更多的緩存數(shù)據(jù),速度會變得更慢。
- 在MySQL的query cache中,我們是不能存儲任意的數(shù)據(jù)的(只能是SQL查詢結(jié)果)。而利用memcached,我們可以搭建出各種高效的緩存。比如,可以執(zhí)行多個獨立 的查詢,構(gòu)建出一個用戶對象(user object),然后將用戶對象緩存到memcached中。而query cache是SQL語句級別的,不可能做到這一點。在小的網(wǎng)站中,query cache會有所幫助,但隨著網(wǎng)站規(guī)模的增加,query cache的弊將大于利。
- query cache能夠利用的內(nèi)存容量受到MySQL服務(wù)器空閑內(nèi)存空間的限制。給數(shù)據(jù)庫服務(wù)器增加更多的內(nèi)存來緩存數(shù)據(jù),固然是很好的。但是,有了memcached,只要您有空閑的內(nèi)存,都可以用來增加memcached集群的規(guī)模,然后您就可以緩存更多的數(shù)據(jù)。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached和服務(wù)器的local cache(比如PHP的APC、mmap文件等)相比,有什么優(yōu)缺點? 首先,local cache有許多與上面(query cache)相同的問題。local cache能夠利用的內(nèi)存容量受到(單臺)服務(wù)器空閑內(nèi)存空間的限制。不過,local cache有一點比memcached和query cache都要好,那就是它不但可以存儲任意的數(shù)據(jù),而且沒有網(wǎng)絡(luò)存取的延遲。 - local cache的數(shù)據(jù)查詢更快。考慮把highly common的數(shù)據(jù)放在local cache中吧。如果每個頁面都需要加載一些數(shù)量較少的數(shù)據(jù),考慮把它們放在local cached吧。
- local cache缺少集體失效(group invalidation)的特性。在memcached集群中,刪除或更新一個key會讓所有的觀察者覺察到。但是在local cache中, 我們只能通知所有的服務(wù)器刷新cache(很慢,不具擴(kuò)展性),或者僅僅依賴緩存超時失效機(jī)制。
- local cache面臨著嚴(yán)重的內(nèi)存限制,這一點上面已經(jīng)提到。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached的cache機(jī)制是怎樣的? Memcached主要的cache機(jī)制是LRU(最近最少用)算法+超時失效。當(dāng)您存數(shù)據(jù)到memcached中,可以指定該數(shù)據(jù)在緩存中可以呆 多久Which is forever, or some time in the future。如果memcached的內(nèi)存不夠用了,過期的slabs會優(yōu)先被替換,接著就輪到最老的未被使用的slabs。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached如何實現(xiàn)冗余機(jī)制? 不 實現(xiàn)!我們對這個問題感到很驚訝。Memcached應(yīng)該是應(yīng)用的緩存層。它的設(shè)計本身就不帶有任何冗余機(jī)制。如果一個memcached節(jié)點失去了所有 數(shù)據(jù),您應(yīng)該可以從數(shù)據(jù)源(比如數(shù)據(jù)庫)再次獲取到數(shù)據(jù)。您應(yīng)該特別注意,您的應(yīng)用應(yīng)該可以容忍節(jié)點的失效。不要寫一些糟糕的查詢代碼,寄希望于 memcached來保證一切!如果您擔(dān)心節(jié)點失效會大大加重數(shù)據(jù)庫的負(fù)擔(dān),那么您可以采取一些辦法。比如您可以增加更多的節(jié)點(來減少丟失一個節(jié)點的影 響),熱備節(jié)點(在其他節(jié)點down了的時候接管IP),等等。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached如何處理容錯的? 不處理!:) 在memcached節(jié)點失效的情況下,集群沒有必要做任何容錯處理。如果發(fā)生了節(jié)點失效,應(yīng)對的措施完全取決于用戶。節(jié)點失效時,下面列出幾種方案供您選擇: - 忽略它! 在失效節(jié)點被恢復(fù)或替換之前,還有很多其他節(jié)點可以應(yīng)對節(jié)點失效帶來的影響。
- 把失效的節(jié)點從節(jié)點列表中移除。做這個操作千萬要小心!在默認(rèn)情況下(余數(shù)式哈希算法),客戶端添加或移除節(jié)點,會導(dǎo)致所有的緩存數(shù)據(jù)不可用!因為哈希參照的節(jié)點列表變化了,大部分key會因為哈希值的改變而被映射到(與原來)不同的節(jié)點上。
- 啟動熱備節(jié)點,接管失效節(jié)點所占用的IP。這樣可以防止哈希紊亂(hashing chaos)。
- 如果希望添加和移除節(jié)點,而不影響原先的哈希結(jié)果,可以使用一致性哈希算法(consistent hashing)。您可以百度一下一致性哈希算法。支持一致性哈希的客戶端已經(jīng)很成熟,而且被廣泛使用。去嘗試一下吧!
- 兩次哈希(reshing)。當(dāng)客戶端存取數(shù)據(jù)時,如果發(fā)現(xiàn)一個節(jié)點down了,就再做一次哈希(哈希算法與前一次不同),重新選擇另一個節(jié)點 (需要注意的時,客戶端并沒有把down的節(jié)點從節(jié)點列表中移除,下次還是有可能先哈希到它)。如果某個節(jié)點時好時壞,兩次哈希的方法就有風(fēng)險了,好的節(jié) 點和壞的節(jié)點上都可能存在臟數(shù)據(jù)(stale data)。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">如何將memcached中item批量導(dǎo)入導(dǎo)出? 您不應(yīng)該這樣做!Memcached是一個非阻塞的服務(wù)器。任何可能導(dǎo)致memcached暫停或瞬時拒絕服務(wù)的操作都應(yīng)該值得深思熟慮。向 memcached中批量導(dǎo)入數(shù)據(jù)往往不是您真正想要的!想象看,如果緩存數(shù)據(jù)在導(dǎo)出導(dǎo)入之間發(fā)生了變化,您就需要處理臟數(shù)據(jù)了;如果緩存數(shù)據(jù)在導(dǎo)出導(dǎo)入 之間過期了,您又怎么處理這些數(shù)據(jù)呢? 因此,批量導(dǎo)出導(dǎo)入數(shù)據(jù)并不像您想象中的那么有用。不過在一個場景倒是很有用。如果您有大量的從不變化 的數(shù)據(jù),并且希望緩存很快熱(warm)起來,批量導(dǎo)入緩存數(shù)據(jù)是很有幫助的。雖然這個場景并不典型,但卻經(jīng)常發(fā)生,因此我們會考慮在將來實現(xiàn)批量導(dǎo)出導(dǎo)入的功能。 Steven Grimm,一如既往地,,在郵件列表中給出了另一個很好的例子:http://lists.danga.com/pipermail/memcached/2007-July/004802.html 。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">但是我確實需要把memcached中的item批量導(dǎo)出導(dǎo)入,怎么辦?? 好吧好吧。如果您需要批量導(dǎo)出導(dǎo)入,最可能的原因一般是重新生成緩存數(shù)據(jù)需要消耗很長的時間,或者數(shù)據(jù)庫壞了讓您飽受痛苦。 如果一個memcached節(jié)點down了讓您很痛苦,那么您還會陷入其他很多麻煩。您的系統(tǒng)太脆弱了。您需要做一些優(yōu)化工作。比如處理"驚群"問 題(比如 memcached節(jié)點都失效了,反復(fù)的查詢讓您的數(shù)據(jù)庫不堪重負(fù)...這個問題在FAQ的其他提到過),或者優(yōu)化不好的查詢。記住,Memcached 并不是您逃避優(yōu)化查詢的借口。 如果您的麻煩僅僅是重新生成緩存數(shù)據(jù)需要消耗很長時間(15秒到超過5分鐘),您可以考慮重新使用數(shù)據(jù)庫。這里給出一些提示: - 使用MogileFS(或者CouchDB等類似的軟件)在存儲item。把item計算出來并dump到磁盤上。MogileFS可以很方便地 覆寫item,并提供快速地訪問。.您甚至可以把MogileFS中的item緩存在memcached中,這樣可以加快讀取速度。 MogileFS+Memcached的組合可以加快緩存不命中時的響應(yīng)速度,提高網(wǎng)站的可用性。
- 重新使用MySQL。MySQL的 InnoDB主鍵查詢的速度非常快。如果大部分緩存數(shù)據(jù)都可以放到VARCHAR字段中,那么主鍵查詢的性能將更好。從memcached中按key查詢 幾乎等價于MySQL的主鍵查詢:將key 哈希到64-bit的整數(shù),然后將數(shù)據(jù)存儲到MySQL中。您可以把原始(不做哈希)的key存儲都普通的字段中,然后建立二級索引來加快查 詢...key被動地失效,批量刪除失效的key,等等。
上面的方法都可以引入memcached,在重啟memcached的時候仍然提供很好的性能。由于您不需要當(dāng)心"hot"的item被 memcached LRU算法突然淘汰,用戶再也不用花幾分鐘來等待重新生成緩存數(shù)據(jù)(當(dāng)緩存數(shù)據(jù)突然從內(nèi)存中消失時),因此上面的方法可以全面提高性能。 關(guān)于這些方法的細(xì)節(jié),詳見博客:http://dormando.livejournal.com/495593.html 。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached是如何做身份驗證的? 沒有身份認(rèn)證機(jī)制!memcached是運行在應(yīng)用下層的軟件(身份驗證應(yīng)該是應(yīng)用上層的職責(zé))。memcached的客戶端和服務(wù)器端之所以是輕量級的,部分原因就是完全沒有實現(xiàn)身份驗證機(jī)制。這樣,memcached可以很快地創(chuàng)建新連接,服務(wù)器端也無需任何配置。
如果您希望限制訪問,您可以使用防火墻,或者讓memcached監(jiān)聽unix domain socket。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached的多線程是什么?如何使用它們? 線 程就是定律(threads rule)!在Steven Grimm和Facebook的努力下,memcached 1.2及更高版本擁有了多線程模式。多線程模式允許memcached能夠充分利用多個CPU,并在CPU之間共享所有的緩存數(shù)據(jù)。memcached使 用一種簡單的鎖機(jī)制來保證數(shù)據(jù)更新操作的互斥。相比在同一個物理機(jī)器上運行多個memcached實例,這種方式能夠更有效地處理multi gets。
如果您的系統(tǒng)負(fù)載并不重,也許您不需要啟用多線程工作模式。如果您在運行一個擁有大規(guī)模硬件的、龐大的網(wǎng)站,您將會看到多線程的好處。
更多信息請參見:http://code.sixapart.com/svn/memcached/trunk/server/doc/threads.txt 。
簡 單地總結(jié)一下:命令解析(memcached在這里花了大部分時間)可以運行在多線程模式下。memcached內(nèi)部對數(shù)據(jù)的操作是基于很多全局鎖的(因 此這部分工作不是多線程的)。未來對多線程模式的改進(jìn),將移除大量的全局鎖,提高memcached在負(fù)載極高的場景下的性能。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached能接受的key的最大長度是多少? key 的最大長度是250個字符。需要注意的是,250是memcached服務(wù)器端內(nèi)部的限制,如果您使用的客戶端支持"key的前綴"或類似特性,那么 key(前綴+原始key)的最大長度是可以超過250個字符的。我們推薦使用使用較短的key,因為可以節(jié)省內(nèi)存和帶寬。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached對item的過期時間有什么限制? 過期時間最大可以達(dá)到30天。memcached把傳入的過期時間(時間段)解釋成時間點后,一旦到了這個時間點,memcached就把item置為失效狀態(tài)。這是一個簡單但obscure的機(jī)制。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached最大能存儲多大的單個item? 1MB。如果你的數(shù)據(jù)大于1MB,可以考慮在客戶端壓縮或拆分到多個key中。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">為什么單個item的大小被限制在1M byte之內(nèi)? 啊...這是一個大家經(jīng)常問的問題!
簡單的回答:因為內(nèi)存分配器的算法就是這樣的。
詳 細(xì)的回答:Memcached的內(nèi)存存儲引擎(引擎將來可插拔...),使用slabs來管理內(nèi)存。內(nèi)存被分成大小不等的slabs chunks(先分成大小相等的slabs,然后每個slab被分成大小相等chunks,不同slab的chunk大小是不相等的)。chunk的大小 依次從一個最小數(shù)開始,按某個因子增長,直到達(dá)到最大的可能值。
如果最小值為400B,最大值是1MB,因子是1.20,各個slab的chunk的大小依次是:slab1 - 400B slab2 - 480B slab3 - 576B ...
slab中chunk越大,它和前面的slab之間的間隙就越大。因此,最大值越大,內(nèi)存利用率越低。Memcached必須為每個slab預(yù)先分配內(nèi)存,因此如果設(shè)置了較小的因子和較大的最大值,會需要更多的內(nèi)存。
還有其他原因使得您不要這樣向memcached中存取很大的數(shù)據(jù)...不要嘗試把巨大的網(wǎng)頁放到mencached中。把這樣大的數(shù)據(jù)結(jié)構(gòu)load和unpack到內(nèi)存中需要花費很長的時間,從而導(dǎo)致您的網(wǎng)站性能反而不好。
如果您確實需要存儲大于1MB的數(shù)據(jù),你可以修改slabs.c:POWER_BLOCK的值,然后重新編譯memcached;或者使用低效的malloc/free。其他的建議包括數(shù)據(jù)庫、MogileFS等。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">我可以在不同的memcached節(jié)點上使用大小不等的緩存空間嗎?這么做之后,memcached能夠更有效地使用內(nèi)存嗎? Memcache 客戶端僅根據(jù)哈希算法來決定將某個key存儲在哪個節(jié)點上,而不考慮節(jié)點的內(nèi)存大小。因此,您可以在不同的節(jié)點上使用大小不等的緩存。但是一般都是這樣做 的:擁有較多內(nèi)存的節(jié)點上可以運行多個memcached實例,每個實例使用的內(nèi)存跟其他節(jié)點上的實例相同。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">什么是二進(jìn)制協(xié)議,我該關(guān)注嗎? 關(guān)于二進(jìn)制最好的信息當(dāng)然是二進(jìn)制協(xié)議規(guī)范:http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol 。
二進(jìn)制協(xié)議嘗試為端提供一個更有效的、可靠的協(xié)議,減少客戶端/服務(wù)器端因處理協(xié)議而產(chǎn)生的CPU時間。 根據(jù)Facebook的測試,解析ASCII協(xié)議是memcached中消耗CPU時間最多的環(huán)節(jié)。所以,我們?yōu)槭裁床桓倪M(jìn)ASCII協(xié)議呢? 在這個郵件列表的thread中可以找到一些舊的信息:http://lists.danga.com/pipermail/memcached/2007-July/004636.html 。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached的內(nèi)存分配器是如何工作的?為什么不適用malloc/free!?為何要使用slabs? 實 際上,這是一個編譯時選項。默認(rèn)會使用內(nèi)部的slab分配器。您確實確實應(yīng)該使用內(nèi)建的slab分配器。最早的時候,memcached只使用 malloc/free來管理內(nèi)存。然而,這種方式不能與OS的內(nèi)存管理以前很好地工作。反復(fù)地malloc/free造成了內(nèi)存碎片,OS最終花費大量 的時間去查找連續(xù)的內(nèi)存塊來滿足malloc的請求,而不是運行memcached進(jìn)程。如果您不同意,當(dāng)然可以使用malloc!只是不要在郵件列表中 抱怨啊:)
slab分配器就是為了解決這個問題而生的。內(nèi)存被分配并劃分成chunks,一直被重復(fù)使用。因為內(nèi)存被劃分成大小不等的 slabs,如果item的大小與被選擇存放它的slab不是很合適的話,就會浪費一些內(nèi)存。Steven Grimm正在這方面已經(jīng)做出了有效的改進(jìn)。
郵件列表中有一些關(guān)于slab的改進(jìn)(power of n 還是 power of 2)和權(quán)衡方案:http://lists.danga.com/pipermail/memcached/2006-May/002163.html http://lists.danga.com/pipermail/memcached/2007-March/003753.html 。
如果您想使用malloc/free,看看它們工作地怎么樣,您可以在構(gòu)建過程中定義USE_SYSTEM_MALLOC。這個特性沒有經(jīng)過很好的測試,所以太不可能得到開發(fā)者的支持。 更多信息:http://code.sixapart.com/svn/memcached/trunk/server/doc/memory_management.txt 。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached是原子的嗎? 當(dāng)然!好吧,讓我們來明確一下: 所有的被發(fā)送到memcached的單個命令是完全原子的。如果您針對同一份數(shù)據(jù)同時發(fā)送了一個set命令和一個get命令,它們不會影響對方。它們將被串行化、先后執(zhí)行。即使在多線程模式,所有的命令都是原子的,除非程序有bug:) 命令序列不是原子的。如果您通過get命令獲取了一個item,修改了它,然后想把它set回memcached,我們不保證這個item沒有被其他進(jìn)程(process,未必是操作系統(tǒng)中的進(jìn)程)操作過。在并發(fā)的情況下,您也可能覆寫了一個被其他進(jìn)程set的item。
memcached 1.2.5以及更高版本,提供了gets和cas命令,它們可以解決上面的問題。如果您使用gets命令查詢某個key的item,memcached會 給您返回該item當(dāng)前值的唯一標(biāo)識。如果您覆寫了這個item并想把它寫回到memcached中,您可以通過cas命令把那個唯一標(biāo)識一起發(fā)送給 memcached。如果該item存放在memcached中的唯一標(biāo)識與您提供的一致,您的寫操作將會成功。如果另一個進(jìn)程在這期間也修改了這個 item,那么該item存放在memcached中的唯一標(biāo)識將會改變,您的寫操作就會失敗。
通常,基于memcached中item的值來修改item,是一件棘手的事情。除非您很清楚自己在做什么,否則請不要做這樣的事情。
SQL SERVER的排序規(guī)則平時使用不是很多,也許不少初學(xué)者還比較陌生,但有一個錯誤大家應(yīng)是經(jīng)常碰到: SQL SERVER數(shù)據(jù)庫,在跨庫多表連接查詢時,若兩數(shù)據(jù)庫默認(rèn)字符集不同,系統(tǒng)就會返回這樣的錯誤: “無法解決 equal to 操作的排序規(guī)則沖突。” 一.錯誤分析: 這個錯誤是因為排序規(guī)則不一致造成的,我們做個測試,比如: create table #t1( name varchar(20) collate Albanian_CI_AI_WS, value int)
create table #t2( name varchar(20) collate Chinese_PRC_CI_AI_WS, value int )
表建好后,執(zhí)行連接查詢:
select * from #t1 A inner join #t2 B on A.name=B.name
這樣,錯誤就出現(xiàn)了:
服務(wù)器: 消息 446,級別 16,狀態(tài) 9,行 1 無法解決 equal to 操作的排序規(guī)則沖突。 要排除這個錯誤,最簡單方法是,表連接時指定它的排序規(guī)則,這樣錯誤就不再出現(xiàn)了。語句這樣寫:
select * from #t1 A inner join #t2 B on A.name=B.name collate Chinese_PRC_CI_AI_WS
二.排序規(guī)則簡介:
什么叫排序規(guī)則呢?MS是這樣描述的:"在 Microsoft SQL Server 2000 中,字符串的物理存儲由排序規(guī)則控制。排序規(guī)則指定表示每個字符的位模式以及存儲和比較字符所使用的規(guī)則。 在查詢分析器內(nèi)執(zhí)行下面語句,可以得到SQL SERVER支持的所有排序規(guī)則。
select * from ::fn_helpcollations()
排序規(guī)則名稱由兩部份構(gòu)成,前半部份是指本排序規(guī)則所支持的字符集。 如: Chinese_PRC_CS_AI_WS 前半部份:指UNICODE字符集,Chinese_PRC_指針對大陸簡體字UNICODE的排序規(guī)則。 排序規(guī)則的后半部份即后綴 含義: _BIN 二進(jìn)制排序 _CI(CS) 是否區(qū)分大小寫,CI不區(qū)分,CS區(qū)分 _AI(AS) 是否區(qū)分重音,AI不區(qū)分,AS區(qū)分 _KI(KS) 是否區(qū)分假名類型,KI不區(qū)分,KS區(qū)分 _WI(WS) 是否區(qū)分寬度 WI不區(qū)分,WS區(qū)分
區(qū)分大小寫:如果想讓比較將大寫字母和小寫字母視為不等,請選擇該選項。 區(qū)分重音:如果想讓比較將重音和非重音字母視為不等,請選擇該選項。如果選擇該選項,比較還將重音不同的字母視為不等。 區(qū)分假名:如果想讓比較將片假名和平假名日語音節(jié)視為不等,請選擇該選項。 區(qū)分寬度:如果想讓比較將半角字符和全角字符視為不等,請選擇該選項 三.排序規(guī)則的應(yīng)用: SQL SERVER提供了大量的WINDOWS和SQLSERVER專用的排序規(guī)則,但它的應(yīng)用往往被開發(fā)人員所忽略。其實它在實踐中大有用處。
例1:讓表NAME列的內(nèi)容按拼音排序:
create table #t(id int,name varchar(20)) insert #t select 1,'中' union all select 2,'國' union all select 3,'人' union all select 4,'阿'
select * from #t order by name collate Chinese_PRC_CS_AS_KS_WS drop table #t /*結(jié)果: id name ----------- -------------------- 4 阿 2 國 3 人 1 中 */
例2:讓表NAME列的內(nèi)容按姓氏筆劃排序:
create table #t(id int,name varchar(20))
insert #t select 1,'三' union all select 2,'乙' union all select 3,'二' union all select 4,'一' union all select 5,'十' select * from #t order by name collate Chinese_PRC_Stroke_CS_AS_KS_WS drop table #t /*結(jié)果: id name ----------- -------------------- 4 一 2 乙 3 二 5 十 1 三 */
四.在實踐中排序規(guī)則應(yīng)用的擴(kuò)展 SQL SERVER漢字排序規(guī)則可以按拼音、筆劃等排序,那么我們?nèi)绾卫眠@種功能來處理漢字的一些難題呢?我現(xiàn)在舉個例子:
用排序規(guī)則的特性計算漢字筆劃
要計算漢字筆劃,我們得先做準(zhǔn)備工作,我們知道,WINDOWS多國漢字,UNICODE目前 收錄漢字共20902個。簡體GBK碼漢字UNICODE值從19968開始。 首先,我們先用SQLSERVER方法得到所有漢字,不用字典,我們簡單利用SQL語句就可以得到:
select top 20902 code=identity(int,19968,1) into #t from syscolumns a,syscolumns b
再用以下語句,我們就得到所有漢字,它是按UNICODE值排序的:
select code,nchar(code) as CNWord from #t
然后,我們用SELECT語句,讓它按筆劃排序。
select code,nchar(code) as CNWord from #t order by nchar(code) collate Chinese_PRC_Stroke_CS_AS_KS_WS,code
結(jié)果: code CNWord ----------- ------ 19968 一 20008 丨 20022 丶 20031 丿 20032 乀 20033 乁 20057 乙 20058 乚 20059 乛 20101 亅 19969 丁 ..........
從上面的結(jié)果,我們可以清楚的看到,一筆的漢字,code是從19968到20101,從小到大排,但到了二筆漢字的第一個字“丁”,CODE為 19969,就不按順序而重新開始了。有了這結(jié)果,我們就可以輕松的用SQL語句得到每種筆劃漢字歸類的第一個或最后一個漢字。 下面用語句得到最后一個漢字:
create table #t1(id int identity,code int,cnword nvarchar(2))
insert #t1(code,cnword) select code,nchar(code) as CNWord from #t order by nchar(code) collate Chinese_PRC_Stroke_CS_AS_KS_WS,code
select A.cnword from #t1 A left join #t1 B on A.id=B.id-1 and A.code<B.code where B.code is null order by A.id
得到36個漢字,每個漢字都是每種筆劃數(shù)按Chinese_PRC_Stroke_CS_AS_KS_WS排序規(guī)則排序后的 最后一個漢字:
亅阝馬風(fēng)龍齊龜齒鴆齔龕龂齠齦齪龍龠龎龐龑龡龢龝齹龣龥齈龞麷鸞麣龖龗齾齉龘
上面可以看出:“亅”是所有一筆漢字排序后的最后一個字,“阝”是所有二筆漢字排序后的最后一個字......等等。 但同時也發(fā)現(xiàn),從第33個漢字“龗(33筆)”后面的筆劃有些亂,不正確。但沒關(guān)系,比“龗”筆劃多的只有四個漢字,我們手工加上:齾35筆,齉36筆,靐39筆,龘64筆 建漢字筆劃表(TAB_HZBH): create table tab_hzbh(id int identity,cnword nchar(1)) --先插入前33個漢字 insert tab_hzbh select top 33 A.cnword from #t1 A left join #t1 B on A.id=B.id-1 and A.code<B.code where B.code is null order by A.id --再加最后四個漢字 set identity_insert tab_hzbh on go insert tab_hzbh(id,cnword) select 35,N'齾' union all select 36,N'齉' union all select 39,N'靐' union all select 64,N'龘' go set identity_insert tab_hzbh off go
到此為止,我們可以得到結(jié)果了,比如我們想得到漢字“國”的筆劃:
declare @a nchar(1) set @a='國' select top 1 id from tab_hzbh where cnword>=@a collate Chinese_PRC_Stroke_CS_AS_KS_WS order by id
id ----------- 8 (結(jié)果:漢字“國”筆劃數(shù)為8)
上面所有準(zhǔn)備過程,只是為了寫下面這個函數(shù),這個函數(shù)撇開上面建的所有臨時表和固定表,為了通用和代碼轉(zhuǎn)移方便,把表tab_hzbh的內(nèi)容寫在語句內(nèi),然后計算用戶輸入一串漢字的總筆劃:
create function fun_getbh(@str nvarchar(4000)) returns int as begin declare @word nchar(1),@n int set @n=0 while len(@str)>0 begin set @word=left(@str,1) --如果非漢字,筆劃當(dāng)0計 set @n=@n+(case when unicode(@word) between 19968 and 19968+20901 then (select top 1 id from ( select 1 as id,N'亅' as word union all select 2,N'阝' union all select 3,N'馬' union all select 4,N'風(fēng)' union all select 5,N'龍' union all select 6,N'齊' union all select 7,N'龜' union all select 8,N'齒' union all select 9,N'鴆' union all select 10,N'齔' union all select 11,N'龕' union all select 12,N'龂' union all select 13,N'齠' union all select 14,N'齦' union all select 15,N'齪' union all select 16,N'龍' union all select 17,N'龠' union all select 18,N'龎' union all select 19,N'龐' union all select 20,N'龑' union all select 21,N'龡' union all select 22,N'龢' union all select 23,N'龝' union all select 24,N'齹' union all select 25,N'龣' union all select 26,N'龥' union all select 27,N'齈' union all select 28,N'龞' union all select 29,N'麷' union all select 30,N'鸞' union all select 31,N'麣' union all select 32,N'龖' union all select 33,N'龗' union all select 35,N'齾' union all select 36,N'齉' union all select 39,N'靐' union all select 64,N'龘' ) T where word>=@word collate Chinese_PRC_Stroke_CS_AS_KS_WS order by id ASC) else 0 end) set @str=right(@str,len(@str)-1) end return @n end
--函數(shù)調(diào)用實例: select dbo.fun_getbh('中華人民共和國'),dbo.fun_getbh('中華人民共和國') 執(zhí)行結(jié)果:筆劃總數(shù)分別為39和46,簡繁體都行。
當(dāng)然,你也可以把上面“UNION ALL”內(nèi)的漢字和筆劃改存在固定表內(nèi),在漢字列建CLUSTERED INDEX,列排序規(guī)則設(shè)定為: Chinese_PRC_Stroke_CS_AS_KS_WS 這樣速度更快。如果你用的是BIG5碼的操作系統(tǒng),你得另外生成漢字,方法一樣。但有一點要記住:這些漢字是通過SQL語句SELECT出來的,不是手工輸入的,更不是查字典得來的,因為新華字典畢竟不同于UNICODE字符集,查字典的結(jié)果會不正確。
用排序規(guī)則的特性得到漢字拼音首字母
用得到筆劃總數(shù)相同的方法,我們也可以寫出求漢字拼音首字母的函數(shù)。如下:
create function fun_getPY(@str nvarchar(4000)) returns nvarchar(4000) as begin declare @word nchar(1),@PY nvarchar(4000) set @PY='' while len(@str)>0 begin set @word=left(@str,1) --如果非漢字字符,返回原字符 set @PY=@PY+(case when unicode(@word) between 19968 and 19968+20901 then (select top 1 PY from ( select 'A' as PY,N'驁' as word union all select 'B',N'簿' union all select 'C',N'錯' union all select 'D',N'鵽' union all select 'E',N'樲' union all select 'F',N'鰒' union all select 'G',N'腂' union all select 'H',N'夻' union all select 'J',N'攈' union all select 'K',N'穒' union all select 'L',N'鱳' union all select 'M',N'旀' union all select 'N',N'桛' union all select 'O',N'漚' union all select 'P',N'曝' union all select 'Q',N'囕' union all select 'R',N'鶸' union all select 'S',N'蜶' union all select 'T',N'籜' union all select 'W',N'鶩' union all select 'X',N'鑂' union all select 'Y',N'韻' union all select 'Z',N'咗' ) T where word>=@word collate Chinese_PRC_CS_AS_KS_WS order by PY ASC) else @word end) set @str=right(@str,len(@str)-1) end return @PY end
--函數(shù)調(diào)用實例: select dbo.fun_getPY('中華人民共和國'),dbo.fun_getPY('中華人民共和國') 結(jié)果都為:ZHRMGHG
你若有興趣,也可用相同的方法,擴(kuò)展為得到漢字全拼的函數(shù),甚至還可以得到全拼的讀音聲調(diào),不過全拼分類大多了。得到全拼最好是用對照表,兩萬多漢字搜索速度很快,用對照表還可以充分利用表的索引。 排序規(guī)則還有很多其它的巧妙用法。歡迎大家共同探討。
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html The JavaTM Virtual Machine Specification Second Edition Tim Lindholm Frank Yellin
- Preface
- 1 Introduction
- 1.1 A Bit of History
- 1.2 The Java Virtual Machine
- 1.3 Summary of Chapters
- 1.4 Notation
- 2 Java Programming Language Concepts
- 2.1 Unicode
- 2.2 Identifiers
- 2.3 Literals
- 2.4 Types and Values
- 2.4.1 Primitive Types and Values 2.4.2 Operators on Integral Values
- 2.4.3 Floating-Point Types, Value Sets, and Values
- 2.4.4 Operators on Floating-Point Values
- 2.4.5 Operators on
boolean Values
- 2.4.6 Reference Types, Objects, and Reference Values
- 2.4.7 The Class
Object
- 2.4.8 The Class
String
- 2.4.9 Operators on Objects
- 2.5 Variables
- 2.5.1 Initial Values of Variables
- 2.5.2 Variables Have Types, Objects Have Classes
- 2.6 Conversions and Promotions
- 2.6.1 Identity Conversions
- 2.6.2 Widening Primitive Conversions
- 2.6.3 Narrowing Primitive Conversions
- 2.6.4 Widening Reference Conversions
- 2.6.5 Narrowing Reference Conversions
- 2.6.6 Value Set Conversion
- 2.6.7 Assignment Conversion
- 2.6.8 Method Invocation Conversion
- 2.6.9 Casting Conversion
- 2.6.10 Numeric Promotion
- 2.7 Names and Packages
- 2.7.1 Simple Names and Qualified Names
- 2.7.2 Packages
- 2.7.3 Members
- 2.7.4 Qualified Names and Access Control
- 2.7.5 Fully Qualified Names
- 2.8 Classes
- 2.8.1 Class Names
- 2.8.2 Class Modifiers
- 2.8.3 Superclasses and Subclasses
- 2.8.4 The Class Members
- 2.9 Fields
- 2.9.1 Field Modifiers
- 2.9.2 Initialization of Fields
- 2.10 Methods
- 2.10.1 Formal Parameters
- 2.10.2 Method Signature
- 2.10.3 Method Modifiers
- 2.11 Static Initializers
- 2.12 Constructors
- 2.12.1 Constructor Modifiers
2.13 Interfaces
- 2.13.1 Interface Modifiers
- 2.13.2 Superinterfaces
- 2.13.3 Interface Members
- 2.13.4 Overriding, Inheritance, and Overloading in Interfaces
- 2.14 Nested Classes and Interfaces
- 2.15 Arrays
- 2.15.1 Array Types
- 2.15.2 Array Variables
- 2.15.3 Array Creation
- 2.15.4 Array Access
- 2.16 Exceptions
- 2.16.1 The Causes of Exceptions
- 2.16.2 Handling an Exception
- 2.16.3 The Exception Hierarchy
- 2.16.4 The Classes Exception and RuntimeException
- 2.17 Execution
- 2.17.1 Virtual Machine Start-up
- 2.17.2 Loading
- 2.17.3 Linking: Verification, Preparation, and Resolution
- 2.17.4 Initialization
- 2.17.5 Detailed Initialization Procedure
- 2.17.6 Creation of New Class Instances
- 2.17.7 Finalization of Class Instances
- 2.17.8 Unloading of Classes and Interfaces
- 2.17.9 Virtual Machine Exit
- 2.18 FP-strict Expressions
- 2.19 Threads
- 3 The Structure of the Java Virtual Machine
- 3.1 The
class File Format
- 3.2 Data Types
- 3.3 Primitive Types and Values
- 3.3.1 Integral Types and Values
- 3.3.2 Floating-Point Types, Value Sets, and Values
- 3.3.3 The
returnAddress Type and Values
- 3.3.4 The
boolean Type 3.4 Reference Types and Values
- 3.5 Runtime Data Areas
- 3.5.1 The
pc Register
- 3.5.2 Java Virtual Machine Stacks
- 3.5.3 Heap
- 3.5.4 Method Area
- 3.5.5 Runtime Constant Pool
- 3.5.6 Native Method Stacks
3.6 Frames
- 3.6.1 Local Variables
- 3.6.2 Operand Stacks
- 3.6.3 Dynamic Linking
- 3.6.4 Normal Method Invocation Completion
- 3.6.5 Abrupt Method Invocation Completion
- 3.6.6 Additional Information
3.7 Representation of Objects
- 3.8 Floating-Point Arithmetic
- 3.8.1 Java Virtual Machine Floating-Point Arithmetic and IEEE 754
- 3.8.2 Floating-Point Modes
- 3.8.3 Value Set Conversion
3.9 Specially Named Initialization Methods
- 3.10 Exceptions
- 3.11 Instruction Set Summary
- 3.11.1 Types and the Java Virtual Machine
- 3.11.2 Load and Store Instructions
- 3.11.3 Arithmetic Instructions
- 3.11.4 Type Conversion Instructions
- 3.11.5 Object Creation and Manipulation
- 3.11.6 Operand Stack Management Instructions
- 3.11.7 Control Transfer Instructions
- 3.11.8 Method Invocation and Return Instructions
- 3.11.9 Throwing Exceptions
- 3.11.10 Implementing finally
- 3.11.11 Synchronization
3.12 Class Libraries
- 3.13 Public Design, Private Implementation
- 4 The
class File Format
- 4.1 The
ClassFile Structure
- 4.2 The Internal Form of Fully Qualified Class and Interface Names
- 4.3 Descriptors
- 4.3.1 Grammar Notation
- 4.3.2 Field Descriptors
- 4.3.3 Method Descriptors
4.4 The Constant Pool
- 4.4.1 The
CONSTANT_Class_info Structure
- 4.4.2 The
CONSTANT_Fieldref_info , CONSTANT_Methodref_info , and CONSTANT_InterfaceMethodref_info Structures
- 4.4.3 The
CONSTANT_String_info Structure
- 4.4.4 The
CONSTANT_Integer_info and CONSTANT_Float_info Structures
- 4.4.5 The
CONSTANT_Long_info and CONSTANT_Double_info Structures
- 4.4.6 The
CONSTANT_NameAndType_info Structure
- 4.4.7 The
CONSTANT_Utf8_info Structure 4.5 Fields
- 4.6 Methods
- 4.7 Attributes
- 4.7.1 Defining and Naming New Attributes
- 4.7.2 The
ConstantValue Attribute
- 4.7.3 The
Code Attribute
- 4.7.4 The
Exceptions Attribute
- 4.7.5 The
InnerClasses Attribute
- 4.7.6 The
Synthetic Attribute
- 4.7.7 The
SourceFile Attribute
- 4.7.8 The
LineNumberTable Attribute
- 4.7.9 The
LocalVariableTable Attribute
- 4.7.10 The
Deprecated Attribute 4.8 Constraints on Java Virtual Machine Code
- 4.8.1 Static Constraints
- 4.8.2 Structural Constraints
4.9 Verification of class Files
- 4.9.1 The Verification Process
- 4.9.2 The Bytecode Verifier
- 4.9.3 Values of Types
long and double
- 4.9.4 Instance Initialization Methods and Newly Created Objects
- 4.9.5 Exception Handlers
- 4.9.6 Exceptions and finally
4.10 Limitations of the Java Virtual Machine
- 5 Loading, Linking, and Initializing
- 5.1 The Runtime Constant Pool
- 5.2 Virtual Machine Start-up
- 5.3 Creation and Loading
- 5.3.1 Loading Using the Bootstrap Class Loader
- 5.3.2 Loading Using a User-defined Class Loader
- 5.3.3 Creating Array Classes
- 5.3.4 Loading Constraints
- 5.3.5 Deriving a Class from a
class File Representation 5.4 Linking
- 5.4.1 Verification
- 5.4.2 Preparation
- 5.4.3 Resolution
- 5.4.4 Access Control
5.5 Initialization
- 5.6 Binding Native Method Implementations
- 6 The Java Virtual Machine Instruction Set
- 6.1 Assumptions: The Meaning of "Must"
- 6.2 Reserved Opcodes
- 6.3 Virtual Machine Errors
- 6.4 Format of Instruction Descriptions
- 7 Compiling for the Java Virtual Machine
- 7.1 Format of Examples
- 7.2 Use of Constants, Local Variables, and Control Constructs
- 7.3 Arithmetic
- 7.4 Accessing the Runtime Constant Pool
- 7.5 More Control Examples
- 7.6 Receiving Arguments
- 7.7 Invoking Methods
- 7.8 Working with Class Instances
- 7.9 Arrays
- 7.10 Compiling Switches
- 7.11 Operations on the Operand Stack
- 7.12 Throwing and Handling Exceptions
- 7.13 Compiling
finally
- 7.14 Synchronization
- 7.15 Compiling Nested Classes and Interfaces
- 8 Threads and Locks
- 8.1 Terminology and Framework
- 8.2 Execution Order and Consistency
- 8.3 Rules About Variables
- 8.4 Nonatomic Treatment of
double and long Variables
- 8.5 Rules About Locks
- 8.6 Rules About the Interaction of Locks and Variables
- 8.7 Rules for
volatile Variables
- 8.8 Prescient Store Operations
- 8.9 Discussion
- 8.10 Example: Possible Swap
- 8.11 Example: Out-of-Order Writes
- 8.12 Threads
- 8.13 Locks and Synchronization
- 8.14 Wait Sets and Notification
- 9 Opcode Mnemonics by Opcode
- Appendix: Summary of Clarifications and Amendments
- Index
本文來自和你在一起的博客,原文標(biāo)題為《JVM調(diào)優(yōu)總結(jié)(一)-- 一些概念》。 數(shù)據(jù)類型 Java虛擬機(jī)中,數(shù)據(jù)類型可以分為兩類:基本類型和引用類型。基本類型的變量保存原始值,即:他代表的值就是數(shù)值本身;而引用類型的變量保存引用值。“引用值”代表了某個對象的引用,而不是對象本身,對象本身存放在這個引用值所表示的地址的位置。 基本類型包括:byte, short, int, long, char, float, double, Boolean, returnAddress 引用類型包括:類類型,接口類型和數(shù)組。 堆與棧 堆和棧是程序運行的關(guān)鍵,很有必要把他們的關(guān)系說清楚。 棧是運行時的單位,而堆是存儲的單位。 棧解決程序的運行問題,即程序如何執(zhí)行,或者說如何處理數(shù)據(jù);堆解決的是數(shù)據(jù)存儲的問題,即數(shù)據(jù)怎么放、放在哪兒。 在Java中一個線程就會相應(yīng)有一個線程棧與之對應(yīng),這點很容易理解,因為不同的線程執(zhí)行邏輯有所不同,因此需要一個獨立的線程棧。而堆則是所有線 程共享的。棧因為是運行單位,因此里面存儲的信息都是跟當(dāng)前線程(或程序)相關(guān)信息的。包括局部變量、程序運行狀態(tài)、方法返回值等等;而堆只負(fù)責(zé)存儲對象 信息。 為什么要把堆和棧區(qū)分出來呢?棧中不是也可以存儲數(shù)據(jù)嗎? 第一,從軟件設(shè)計的角度看,棧代表了處理邏輯,而堆代表了數(shù)據(jù)。這樣分開,使得處理邏輯更為清晰。分而治之的思想。這種隔離、模塊化的思想在軟件設(shè)計的方方面面都有體現(xiàn)。 第二,堆與棧的分離,使得堆中的內(nèi)容可以被多個棧共享(也可以理解為多個線程訪問同一個對象)。這種共享的收益是很多的。一方面這種共享提供了一種有效的數(shù)據(jù)交互方式(如:共享內(nèi)存),另一方面,堆中的共享常量和緩存可以被所有棧訪問,節(jié)省了空間。 第三,棧因為運行時的需要,比如保存系統(tǒng)運行的上下文,需要進(jìn)行地址段的劃分。由于棧只能向上增長,因此就會限制住棧存儲內(nèi)容的能力。而堆不同,堆中的對象是可以根據(jù)需要動態(tài)增長的,因此棧和堆的拆分,使得動態(tài)增長成為可能,相應(yīng)棧中只需記錄堆中的一個地址即可。 第四,面向?qū)ο缶褪嵌押蜅5耐昝澜Y(jié)合。其實,面向?qū)ο蠓绞降某绦蚺c以前結(jié)構(gòu)化的程序在執(zhí)行上沒有任何區(qū)別。但是,面向?qū)ο蟮囊耄沟脤Υ龁栴}的思 考方式發(fā)生了改變,而更接近于自然方式的思考。當(dāng)我們把對象拆開,你會發(fā)現(xiàn),對象的屬性其實就是數(shù)據(jù),存放在堆中;而對象的行為(方法),就是運行邏輯, 放在棧中。我們在編寫對象的時候,其實即編寫了數(shù)據(jù)結(jié)構(gòu),也編寫的處理數(shù)據(jù)的邏輯。不得不承認(rèn),面向?qū)ο蟮脑O(shè)計,確實很美。 在Java中,Main函數(shù)就是棧的起始點,也是程序的起始點。 程序要運行總是有一個起點的。同C語言一樣,java中的Main就是那個起點。無論什么java程序,找到main就找到了程序執(zhí)行的入口:) 堆中存什么?棧中存什么? 堆中存的是對象。棧中存的是基本數(shù)據(jù)類型和堆中對象的引用。一個對象的大小是不可估計的,或者說是可以動態(tài)變化的,但是在棧中,一個對象只對應(yīng)了一個4btye的引用(堆棧分離的好處:))。 為什么不把基本類型放堆中呢?因為其占用的空間一般是1~8個字節(jié)——需要空間比較少,而且因為是基本類型,所以不會出現(xiàn)動態(tài)增長的情況——長度固 定,因此棧中存儲就夠了,如果把他存在堆中是沒有什么意義的(還會浪費空間,后面說明)。可以這么說,基本類型和對象的引用都是存放在棧中,而且都是幾個 字節(jié)的一個數(shù),因此在程序運行時,他們的處理方式是統(tǒng)一的。但是基本類型、對象引用和對象本身就有所區(qū)別了,因為一個是棧中的數(shù)據(jù)一個是堆中的數(shù)據(jù)。最常 見的一個問題就是,Java中參數(shù)傳遞時的問題。 Java中的參數(shù)傳遞時傳值呢?還是傳引用? 要說明這個問題,先要明確兩點: 1. 不要試圖與C進(jìn)行類比,Java中沒有指針的概念 2. 程序運行永遠(yuǎn)都是在棧中進(jìn)行的,因而參數(shù)傳遞時,只存在傳遞基本類型和對象引用的問題。不會直接傳對象本身。 明確以上兩點后。Java在方法調(diào)用傳遞參數(shù)時,因為沒有指針,所以它都是進(jìn)行傳值調(diào)用(這點可以參考C的傳值調(diào)用)。因此,很多書里面都說Java是進(jìn)行傳值調(diào)用,這點沒有問題,而且也簡化的C中復(fù)雜性。 但是傳引用的錯覺是如何造成的呢?在運行棧中,基本類型和引用的處理是一樣的,都是傳值,所以,如果是傳引用的方法調(diào)用,也同時可以理解為“傳引用 值”的傳值調(diào)用,即引用的處理跟基本類型是完全一樣的。但是當(dāng)進(jìn)入被調(diào)用方法時,被傳遞的這個引用的值,被程序解釋(或者查找)到堆中的對象,這個時候才 對應(yīng)到真正的對象。如果此時進(jìn)行修改,修改的是引用對應(yīng)的對象,而不是引用本身,即:修改的是堆中的數(shù)據(jù)。所以這個修改是可以保持的了。 對象,從某種意義上說,是由基本類型組成的。可以把一個對象看作為一棵樹,對象的屬性如果還是對象,則還是一顆樹(即非葉子節(jié)點),基本類型則為樹 的葉子節(jié)點。程序參數(shù)傳遞時,被傳遞的值本身都是不能進(jìn)行修改的,但是,如果這個值是一個非葉子節(jié)點(即一個對象引用),則可以修改這個節(jié)點下面的所有內(nèi) 容。
堆和棧中,棧是程序運行最根本的東西。程序運行可以沒有堆,但是不能沒有棧。而堆是為棧進(jìn)行數(shù)據(jù)存儲服務(wù),說白了堆就是一塊共享的內(nèi)存。不過,正是因為堆和棧的分離的思想,才使得Java的垃圾回收成為可能。 Java中,棧的大小通過-Xss來設(shè)置,當(dāng)棧中存儲數(shù)據(jù)比較多時,需要適當(dāng)調(diào)大這個值,否則會出現(xiàn)java.lang.StackOverflowError異常。常見的出現(xiàn)這個異常的是無法返回的遞歸,因為此時棧中保存的信息都是方法返回的記錄點。
二 JAVA垃圾收集器 2.1 垃圾收集簡史 垃圾收集提供了內(nèi)存管理的機(jī)制,使得應(yīng)用程序不需要在關(guān)注內(nèi)存如何釋放,內(nèi)存用完后,垃圾收集會進(jìn)行收集,這樣就減輕了因為人為的管理內(nèi)存而造成的 錯誤,比如在C++語言里,出現(xiàn)內(nèi)存泄露時很常見的。Java語言是目前使用最多的依賴于垃圾收集器的語言,但是垃圾收集器策略從20世紀(jì)60年代就已經(jīng) 流行起來了,比如Smalltalk,Eiffel等編程語言也集成了垃圾收集器的機(jī)制。 2.2 常見的垃圾收集策略  所有的垃圾收集算法都面臨同一個問題,那就是找出應(yīng)用程序不可到達(dá)的內(nèi)存塊,將其釋放,這里面得不可到達(dá)主要是指應(yīng)用程序已經(jīng)沒有內(nèi)存塊的引用了, 而在JAVA中,某個對象對應(yīng)用程序是可到達(dá)的是指:這個對象被根(根主要是指類的靜態(tài)變量,或者活躍在所有線程棧的對象的引用)引用或者對象被另一個可 到達(dá)的對象引用。 2.2.1 Reference Counting(引用計數(shù)) 引用計數(shù)是最簡單直接的一種方式,這種方式在每一個對象中增加一個引用的計數(shù),這個計數(shù)代表當(dāng)前程序有多少個引用引用了此對象,如果此對象的引用計數(shù)變?yōu)?,那么此對象就可以作為垃圾收集器的目標(biāo)對象來收集。 優(yōu)點: 簡單,直接,不需要暫停整個應(yīng)用 缺點: 1.需要編譯器的配合,編譯器要生成特殊的指令來進(jìn)行引用計數(shù)的操作,比如每次將對象賦值給新的引用,或者者對象的引用超出了作用域等。 2.不能處理循環(huán)引用的問題 2.2.2 跟蹤收集器 跟蹤收集器首先要暫停整個應(yīng)用程序,然后開始從根對象掃描整個堆,判斷掃描的對象是否有對象引用,這里面有三個問題需要搞清楚:  1.如果每次掃描整個堆,那么勢必讓GC的時間變長,從而影響了應(yīng)用本身的執(zhí)行。因此在JVM里面采用了分代收集,在新生代收集的時候minor gc只需要掃描新生代,而不需要掃描老生代。 2.JVM采用了分代收集以后,minor gc只掃描新生代,但是minor gc怎么判斷是否有老生代的對象引用了新生代的對象,JVM采用了卡片標(biāo)記的策略,卡片標(biāo)記將老生代分成了一塊一塊的,劃分以后的每一個塊就叫做一個卡 片,JVM采用卡表維護(hù)了每一個塊的狀態(tài),當(dāng)JAVA程序運行的時候,如果發(fā)現(xiàn)老生代對象引用或者釋放了新生代對象的引用,那么就JVM就將卡表的狀態(tài)設(shè) 置為臟狀態(tài),這樣每次minor gc的時候就會只掃描被標(biāo)記為臟狀態(tài)的卡片,而不需要掃描整個堆。具體如下圖: 3.GC在收集一個對象的時候會判斷是否有引用指向?qū)ο螅贘AVA中的引用主要有四種:Strong reference,Soft reference,Weak reference,Phantom reference. ◆ Strong Reference 強(qiáng)引用是JAVA中默認(rèn)采用的一種方式,我們平時創(chuàng)建的引用都屬于強(qiáng)引用。如果一個對象沒有強(qiáng)引用,那么對象就會被回收。 - public void testStrongReference(){
- Object referent = new Object();
- Object strongReference = referent;
- referent = null;
- System.gc();
- assertNotNull(strongReference);
- }
◆ Soft Reference 軟引用的對象在GC的時候不會被回收,只有當(dāng)內(nèi)存不夠用的時候才會真正的回收,因此軟引用適合緩存的場合,這樣使得緩存中的對象可以盡量的再內(nèi)存中待長久一點。 - Public void testSoftReference(){
- String str = "test";
- SoftReference<String> softreference = new SoftReference<String>(str);
- str=null;
- System.gc();
- assertNotNull(softreference.get());
- }
◆ Weak reference 弱引用有利于對象更快的被回收,假如一個對象沒有強(qiáng)引用只有弱引用,那么在GC后,這個對象肯定會被回收。 - Public void testWeakReference(){
- String str = "test";
- WeakReference<String> weakReference = new WeakReference<String>(str);
- str=null;
- System.gc();
- assertNull(weakReference.get());
- }
◆ Phantom reference 2.2.2.1 Mark-Sweep Collector(標(biāo)記-清除收集器) 標(biāo)記清除收集器最早由Lisp的發(fā)明人于1960年提出,標(biāo)記清除收集器停止所有的工作,從根掃描每個活躍的對象,然后標(biāo)記掃描過的對象,標(biāo)記完成以后,清除那些沒有被標(biāo)記的對象。 優(yōu)點: 1 解決循環(huán)引用的問題 2 不需要編譯器的配合,從而就不執(zhí)行額外的指令 缺點: 1.每個活躍的對象都要進(jìn)行掃描,收集暫停的時間比較長。 2.2.2.2 Copying Collector(復(fù)制收集器)復(fù)制收集器將內(nèi)存分為兩塊一樣大小空間,某一個時刻,只有一個空間處于活躍的狀態(tài),當(dāng)活躍的空間滿的時候,GC就會將活 躍的對象復(fù)制到未使用的空間中去,原來不活躍的空間就變?yōu)榱嘶钴S的空間。復(fù)制收集器具體過程可以參考下圖:  優(yōu)點: 1 只掃描可以到達(dá)的對象,不需要掃描所有的對象,從而減少了應(yīng)用暫停的時間 缺點: 1.需要額外的空間消耗,某一個時刻,總是有一塊內(nèi)存處于未使用狀態(tài) 2.復(fù)制對象需要一定的開銷 2.2.2.3 Mark-Compact Collector(標(biāo)記-整理收集器)標(biāo)記整理收集器汲取了標(biāo)記清除和復(fù)制收集器的優(yōu)點,它分兩個階段執(zhí)行,在第一個階段,首先掃描所有活躍的對象,并 標(biāo)記所有活躍的對象,第二個階段首先清除未標(biāo)記的對象,然后將活躍的的對象復(fù)制到堆得底部。標(biāo)記整理收集器的過程示意圖請參考下圖:Mark- compact策略極大的減少了內(nèi)存碎片,并且不需要像Copy Collector一樣需要兩倍的空間。
垃圾收集器策略從20世紀(jì)60年代就已經(jīng)流行起來了,相比于其他編程語言,Java語言是目前使用最多的依賴于垃圾收集器的語言。 AD: JVM內(nèi)存模型是Java的核心技術(shù)之一,之前51CTO曾為大家介紹過JVM分代垃圾回收策略的基礎(chǔ)概念,現(xiàn)在很多編程語言都引入了類似Java JVM的內(nèi)存模型和垃圾收集器的機(jī)制,下面我們將主要針對Java中的JVM內(nèi)存模型及垃圾收集的具體策略進(jìn)行綜合的分析。 一 JVM內(nèi)存模型 1.1 Java棧 Java棧是與每一個線程關(guān)聯(lián)的,JVM在創(chuàng)建每一個線程的時候,會分配一定的棧空間給線程。它主要用來存儲線程執(zhí)行過程中的局部變量,方法的返回 值,以及方法調(diào)用上下文。棧空間隨著線程的終止而釋放。StackOverflowError:如果在線程執(zhí)行的過程中,棧空間不夠用,那么JVM就會拋 出此異常,這種情況一般是死遞歸造成的。 1.2 堆 Java中堆是由所有的線程共享的一塊內(nèi)存區(qū)域,堆用來保存各種JAVA對象,比如數(shù)組,線程對象等。 1.2.1 Generation JVM堆一般又可以分為以下三部分:  ◆ Perm Perm代主要保存class,method,filed對象,這部門的空間一般不會溢出,除非一次性加載了很多的類,不過在涉及到熱部署的應(yīng)用服 務(wù)器的時候,有時候會遇到j(luò)ava.lang.OutOfMemoryError : PermGen space 的錯誤,造成這個錯誤的很大原因就有可能是每次都重新部署,但是重新部署后,類的class沒有被卸載掉,這樣就造成了大量的class對象保存在了 perm中,這種情況下,一般重新啟動應(yīng)用服務(wù)器可以解決問題。 ◆ Tenured Tenured區(qū)主要保存生命周期長的對象,一般是一些老的對象,當(dāng)一些對象在Young復(fù)制轉(zhuǎn)移一定的次數(shù)以后,對象就會被轉(zhuǎn)移到Tenured區(qū),一般如果系統(tǒng)中用了application級別的緩存,緩存中的對象往往會被轉(zhuǎn)移到這一區(qū)間。 ◆ Young Young區(qū)被劃分為三部分,Eden區(qū)和兩個大小嚴(yán)格相同的Survivor區(qū),其中Survivor區(qū)間中,某一時刻只有其中一個是被使用的, 另外一個留做垃圾收集時復(fù)制對象用,在Young區(qū)間變滿的時候,minor GC就會將存活的對象移到空閑的Survivor區(qū)間中,根據(jù)JVM的策略,在經(jīng)過幾次垃圾收集后,任然存活于Survivor的對象將被移動到 Tenured區(qū)間。 1.2.2 Sizing the Generations JVM提供了相應(yīng)的參數(shù)來對內(nèi)存大小進(jìn)行配置。正如上面描述,JVM中堆被分為了3個大的區(qū)間,同時JVM也提供了一些選項對Young,Tenured的大小進(jìn)行控制。  ◆ Total Heap -Xms :指定了JVM初始啟動以后初始化內(nèi)存 -Xmx:指定JVM堆得最大內(nèi)存,在JVM啟動以后,會分配-Xmx參數(shù)指定大小的內(nèi)存給JVM,但是不一定全部使用,JVM會根據(jù)-Xms參數(shù)來調(diào)節(jié)真正用于JVM的內(nèi)存 -Xmx -Xms之差就是三個Virtual空間的大小 ◆ Young Generation -XX:NewRatio=8意味著tenured 和 young的比值8:1,這樣eden+2*survivor=1/9 堆內(nèi)存 -XX:SurvivorRatio=32意味著eden和一個survivor的比值是32:1,這樣一個Survivor就占Young區(qū)的1/34. -Xmn 參數(shù)設(shè)置了年輕代的大小 ◆ Perm Generation -XX:PermSize=16M -XX:MaxPermSize=64M Thread Stack -XX:Xss=128K 1.3 堆棧分離的好處 呵呵,其它的先不說了,就來說說面向?qū)ο蟮脑O(shè)計吧,當(dāng)然除了面向?qū)ο蟮脑O(shè)計帶來的維護(hù)性,復(fù)用性和擴(kuò)展性方面的好處外,我們看看面向?qū)ο笕绾吻擅畹?利用了堆棧分離。如果從JAVA內(nèi)存模型的角度去理解面向?qū)ο蟮脑O(shè)計,我們就會發(fā)現(xiàn)對象它完美的表示了堆和棧,對象的數(shù)據(jù)放在堆中,而我們編寫的那些方法 一般都是運行在棧中,因此面向?qū)ο蟮脑O(shè)計是一種非常完美的設(shè)計方式,它完美的統(tǒng)一了數(shù)據(jù)存儲和運行。
轉(zhuǎn)自:http://hi.baidu.com/fecasmoy123/blog/item/7f91fd8da34ac918b31bbabf.html
servlet 2.3 新增功能:
2000年10月份出來
Servlet API 2.3中最重大的改變是增加了filters
servlet 2.4 新增功能:
2003年11月份出來
1、web.xml DTD改用了XML Schema;
Servlet 2.3之前的版本使用DTD作為部署描述文件的定義,其web.xml的格式為如下所示:
<?xml version="1.0" encoding="IS0-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//sunMicrosystems,Inc.//DTD WebApplication 2.3f//EN"
"
<web-app>
.......
</web-app>
Servlet 2.4版首次使用XML Schema定義作為部署描述文件,這樣Web容器更容易校驗web.xml語法。同時XML Schema提供了更好的擴(kuò)充性,其web.xml中的格式如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="
xmlns:workflow="
xmins:xsi="
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
.........
</web-app>
注意: 改為Schema后主要加強(qiáng)了兩項功能:
(1) 元素不依照順序設(shè)定
(2) 更強(qiáng)大的驗證機(jī)制
主要體現(xiàn)在:
a.檢查元素的值是否為合法的值
b.檢查元素的值是否為合法的文字字符或者數(shù)字字符
c.檢查Servlet,Filter,EJB-ref等等元素的名稱是否唯一
2.新增Filter四種設(shè)定:REQUEST、FORWARD、INCLUDE和ERROR。
3.新增Request Listener、Event和Request Attribute Listener、Enent。
4.取消SingleThreadModel接口。當(dāng)Servlet實現(xiàn)SingleThreadModel接口時,它能確保同時間內(nèi),只能有一個thread執(zhí)行此Servlet。
5.<welcome-file-list>可以為Servlet。
6.ServletRequest接口新增一些方法。
public String getLocalName()
public String getLocalAddr()
public int getLocalPort()
public int getRemotePort()
Servlet 2.5的新特征
2005年9月發(fā)布Servlet 2.5
Servlet2.5一些變化的介紹:
1) 基于最新的J2SE 5.0開發(fā)的。
2) 支持annotations 。
3) web.xml中的幾處配置更加方便。
4) 去除了少數(shù)的限制。
5) 優(yōu)化了一些實例
servlet的各個版本對監(jiān)聽器的變化有:
(1)servlet2.2和jsp1.1
新增Listener:HttpSessionBindingListener
新增Event: HttpSessionBindingEvent
(2)servlet2.3和jsp1.2
新增Listener:ServletContextListener,ServletContextAttributeListener
,HttpSessionListener,HttpSessionActivationListener,HttpSessionAttributeListener
新增Event: ServletContextEvent,ServletContextAttributeEvent,HttpSessionEvent
(3)servlet2.4和jsp2.0
新增Listener:ServletRequestListener,ServletRequestAttribureListener
新增Event: ServletRequestEvent,ServletRequestAttributeEvent
摘要: PostgreSQL與MySQL數(shù)據(jù)庫的比較 閱讀全文
Two Simple Rules for HTTP Caching
In practice, you only need two settings to optimize caching:
- Don’t cache HTML
- Cache everything else forever
“Wooah…hang on!”, we hear you say. “Cache all my scripts and images forever?“
Yes, that’s right. You don’t need anything else in between. Caching
indefinitely is fine as long as you don’t allow your HTML to be cached.
“But what about if I need to issue code patches to my JavaScript? I can’t allowbrowsers to hold on to all my images either. I often need to update those as well.”
Simple – just change the URL of the item in your HTML and it will bypass the existing entry in the cache.
In practice, caching ‘forever’ typically means setting an Expires header value of Sun, 17-Jan-2038 19:14:07 GMT
since that’s the maximum value supported by the 32 bit Unix time/date
format. If you’re using IIS6 you’ll find that the UI won’t allow
anything beyond 31-Dec-2035. The advantage of setting long expiry dates
is that the content can be read from the local browser cache whenever
the user revisits the web page or goes to another page that uses the
same images, script or CSS files.
You’ll see long expiry dates like this if you look at a Google web
page with HttpWatch. For example, here are the response headers used for
the main Google logo on the home page:

If Google needs to change the logo for a special occasion like
Halloween they just change the name of the file in the page’s HTML to
something like halloween2007.gif.
The diagram below shows how a JavaScript file is loaded into the browser cache on the first visit to a web page:

On any subsequent visits the browser only has to fetch the page’s HTML:

The JavaScript file can be read directly from the browser cache on
the user’s hard disk. This avoids a network round trip and is typically
100 to 1000 times faster than downloading the file over a broadband
connection.
The key to this caching scheme is to keep tight control over your
HTML as it holds the references to everything else on your web site. One
way to do this is to ensure that your pages have a Cache-Control: no-cache header. This will prevent any caching of the HTML and will ensure the browser requests the page’s HTML every time.
If you do this, you can update any content on the page just by
changing the URL that refers to it in the HTML. The old version will
still be in the browser’s cache, but the updated version will be
downloaded because of the modified URL.
For instance, if you had a file called topMenu.js and you fixed some
bugs in it, you might rename the file topMenu-v2.js to force it to be
downloaded:

Now this is all very well, but whenever there’s a discussion of
longer expiration times, the marketing people get very twitchy and
concerned that they won’t be able to re-brand a site if stylesheets and
images are cached for long periods of time.
In fact, choosing an expiration time of anything other than zero or
infinite is inherently uncertain. The only way to know exactly when you
can release a new version to all users simultaneously is to choose a
specific time of day for your cache expiry; say midnight. It’s better to
set indefinite caching on all your page-linked items so that you get
the maximum amount of caching, and then force updates as required.
Now, by this point, you might have the marketing types on board but
you’ll be losing the developers. The developers by now are seeing all
the extra work involved in changing the filenames of all their CSS,
javascript and images both in their source controlled projects and in
their deployment scripts.
So here’s the icing on the cake; you don’t actually need to
change the filename, just the URL. A simple way to do this is to append a
query string parameter onto the end of the existing URL when the
resource has changed.
Here’s the previous example that updated a JavaScript file. The
difference this time is that it uses a query string parameter ‘v2′ to
bypass the existing cache entry:

The web server will simply ignore the query string parameter unless you have chosen to do anything with it programmatically.
There’s one final optimization you can make. The Cache-Control: no-cache
response header works well for dynamic pages as it ensures that pages
will always be refreshed from the server; even when pressing the Back
button. However, for HTML that changes less frequently it is better to
use the Last-Modified header instead. This will avoid a
complete download of the page’s HTML, if it has not changed since it was
last cached by the browser.
The Last-Modified header is added automatically by IIS
for static HTML files and can be added programmatically in dynamic pages
(e.g. ASPX and PHP). When this header is present, the browser will
revalidate the local, cached copy of an HTML page in each new browser
session. If the page is unchanged the web server returns a 304 Not Modified response indicating the browser can use the cached version of the page.
So to summarize:
- Don’t cache HTML
- Use
Cache-Control: no-cache for dynamic HTML pages
- Use the
Last-Modified header with the current file time for static HTML
- Cache everything else forever
- For all other file types set an
Expires header to the maximum future date your web server will allow
- Modify URLs by appending a query string in your HTML to any page element you wish to ‘expire’ immediately.
當(dāng)你建立好一個WEB服務(wù)后,通常有兩個類型的緩存需要配置:
- 設(shè)置網(wǎng)站有更新的時候html資源馬上過期,以便正在瀏覽的用戶可以很快地得到更新.
- 設(shè)置所有其它資源(例如圖片,CSS,javascript腳本)在一定時間后過期.
這個緩存方案涵蓋Two Simple Rules for HTTP Caching文章中提到關(guān)于如何處理更新的一些思想.
現(xiàn)在HttpWatch 6.0支持Firefox了,我們想探討一下Firefox在處理緩存上與IE有些什么不同.設(shè)置較長過期時間的使用方式(上面第二條)仍可以直接用于Firefox,但配置1在兩者之間還是存在細(xì)微差別的.
在之前的文章 中,我們把第一條劃分為:
- 某些時候動態(tài)HTML頁面需要即時從服務(wù)器更新以備隨時顯示-甚至是使用后退按鈕的時候.例如,顯示銀行帳號的狀態(tài)或在線訂單.
- 靜態(tài)HTML頁面,比如聯(lián)系,FAQs或者站點地圖等頁面,如果它們設(shè)置了
Last-Modified響應(yīng)頭 ,允許瀏覽器在需要的時候重新校驗,就可以利用到緩存.
一:火狐機(jī)制
本文剩下部分探討了Firefox中影響HTML頁面緩存的兩個重要不同點.
1. 使用no-cache防止Firefox緩存無效
你可以簡單地設(shè)置如下的響應(yīng)頭預(yù)防IE緩存任何東西:
Cache-Control: no-cache
使用了這個響應(yīng)頭的頁面不會保存在緩存里,IE總會重新從服務(wù)器加載;即使你使用后退按鈕.下面這個例子使用HttpWatch監(jiān)聽一個網(wǎng)上商店,當(dāng)我們在提交訂單表單后點擊后退按鈕,結(jié)果如下圖:

然而,這個響應(yīng)頭卻不能防止Firefox的緩存.這意味著,Firefox在正常訪問的情況下,將一直使用緩存的頁面,直到它發(fā)送GET請求重新檢驗.并且,如果是通過后退按鈕訪問頁面,Firefox不會再次訪問服務(wù)器,而是簡單直接地從緩存加載.
那怎樣才能關(guān)掉Firefox中的緩存呢? 答案很簡單,關(guān)不了. 因為Firefox依靠緩存中的副本為"文件->另存為","查看源代碼"這樣的操作服務(wù).但是,你可以控制頁面緩存到哪里及那些緩存條目可以用于顯示.
下面響應(yīng)頭在Firefox中可以防止持久化的緩存,強(qiáng)制頁面被緩存到內(nèi)存中:
Cache-Control:no-store
這個頭也可以防止使用后退按鈕時訪問了緩存頁面,它將觸發(fā)一個HTTP GET請求.
這兩個響應(yīng)頭的值組合使用可以在IE與Firefox得到期待的結(jié)果:
Cache-Control: no-cache, no-store
如下HttpWatch響應(yīng)頭標(biāo)簽所示:

2. 如果沒有設(shè)置過期時間Firefox會為你設(shè)置一個
當(dāng)IE遇到?jīng)]有Expires頭的http響應(yīng)時,它就認(rèn)為永遠(yuǎn)不能自動使用緩存條目,直到它重新從服務(wù)校驗.由于IE的臨時文件的一個設(shè)置項"檢查所在網(wǎng)頁的較新版本"默認(rèn)為"自動",所以通常都是一個會話做一次.
這就為控制靜態(tài)的html內(nèi)容的緩存提供了一個合理的方式.用戶新打開的IE會得到html的最新版本,而緩存的版本就在關(guān)閉IE前會一直被使用.
Firefox處理缺失Expires頭的方式不同.如果影響中有Last-Modified頭它就會使用HTTP 1.1規(guī)范RFC2616中指定的一個嘗試性的過期值:
(引用規(guī)范:)
并且,如果響應(yīng)中有Last-Modified時間值,嘗試性的過期值不能超過這個值到現(xiàn)在時間間隔的一個比率,一般設(shè)置這個比率為10%.
計算方式如下:(默認(rèn)過期時間)
過期時間 = 現(xiàn)在時間 + 0.1 * (Last-Modified到現(xiàn)在的時間差)
例如,如果你的靜態(tài)HTML文件上次修改時間是100天前,那過期時間就是10天之后.下面的示例是一個沒有Expires頭頁面的HttpWatch緩存標(biāo)簽:

Firefox自動設(shè)置了過期時間為8天后,因為這個頁面大概80天沒有被修改過了.
這意味著,為了保持控制好你的HTML頁面,正如我們在 Two Simple Rules for HTTP Caching 文章中討論過的,你最好為你的靜態(tài)資源如HTML,圖片,CSS文件等,在你的WEB服務(wù)器設(shè)置一個合適的Expires值.
結(jié)論
為了確保IE與Firefox的緩存行為一致,你應(yīng)該:
- 總是指定一個Expires頭. 一般設(shè)置-1使用html頁面能即時刷新或者對其它如圖片,CSS,javascript腳本資源設(shè)置一個特定的過期時間
- 如果你要強(qiáng)制頁面刷新,甚至是點擊后臺按鈕的時候,那就設(shè)置 Cache-Control: no-cache, no-store
----------------------------------
二:IE機(jī)制

1:每次都會到服務(wù)器檢查最后一次修改時間是否變化 無變化返回304
2:IE啟動時檢查
3:自動檢查
3.1:根據(jù)IE的默認(rèn)在瀏覽器不關(guān)閉時,再次訪問同一鏈接都讀本地cache
3.2:IE重啟時候會根據(jù)服務(wù)器設(shè)置的expire值去判斷是否去服務(wù)器請求判斷,無變化返回304
4:第一次下載后從不檢查
最好的機(jī)制是 3 。根據(jù)服務(wù)器的設(shè)定機(jī)制走,會提高訪問速度。
提高訪問速度,最好的辦法就是減少TCP通信。
-------------------------
三:服務(wù)器的設(shè)置
參考: http://hi.baidu.com/%C8%FD%BE%D6%CE%AA%B6%FE/blog/item/30dae1325363ed92a8018e5c.html
-------------------------
四:服務(wù)端設(shè)置和客戶端的關(guān)聯(lián)
服務(wù)器的設(shè)置告知瀏覽器怎么做緩存,緩存時間是多長。客戶端同時也可以是CDN。
-------------------------
五:工具類分析
IE httpwatch ,FF httpfox YSlow firebug
在Java5.0中對于RMI的使用發(fā)生了一些改變, 使得RMI的使用更加容易方便.
一. 定義RMI接口, 這一步最簡單, 與以前相比, 也沒有變化.
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Payment extends Remote {
public double calculatePayment(double principal, double annualRate, int terms)
throws RemoteException;
}
注意點:
1. 必須繼承java.rmi.Remote
2. 接口中的方法必須拋出java.rmi.RemoteException異常
二. 定義遠(yuǎn)程類實現(xiàn)接口
與以前相比, 也沒有發(fā)生變化
import java.rmi.RemoteException;
public class PaymentImpl implements Payment {
public double calculatePayment(double principal, double annRate, int years)
throws RemoteException {
double monthlyInt = annRate / 12;
double monthlyPayment = (principal * monthlyInt)
/ (1 - Math.pow(1/ (1 + monthlyInt), years * 12));
return format(monthlyPayment, 2);
}
public double format(double amount, int places) {
double temp = amount;
temp = temp * Math.pow(10, places);
temp = Math.round(temp);
temp = temp/Math.pow(10, places);
return temp;
}
}
注意點:
1. 遠(yuǎn)程對象必須實現(xiàn)第一步定義的接口, 在這里是Payment接口.
2. 遠(yuǎn)程對象中可以定義接口中不包括的方法, 比如: format, 但是這些方法不能遠(yuǎn)程使用.
3. 實現(xiàn)遠(yuǎn)程方法時, 可以不拋出java.rmi.RemoteException異常
三. 定義遠(yuǎn)程服務(wù)
此處與以前相比, 發(fā)生了一些變化, 需要注意.
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server {
public Server() {}
public static void main(String args[]) {
try {
PaymentImpl robj = new PaymentImpl();
Payment stub = (Payment) UnicastRemoteObject.exportObject(robj, 0);
Registry registry = LocateRegistry.getRegistry();
registry.bind("Mortgage", stub);
System.out.println("Mortgage Server is ready to listen");
} catch (Exception e) {
System.err.println("Server exception thrown: " + e.toString());
e.printStackTrace();
}
}
}
而傳統(tǒng)的做法是這樣的:
public class Server extends java.rmi.server.UnicastRemoteObject implements aRemoteInterface{
public Server(int port) {
super(port);
}
....
Naming.bind(uniqueName, this);
....
}
請自行進(jìn)行比較.
四. 定義客戶端類
相應(yīng)地, 這部分也有了變化.
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
private static Payment stub = null;
private Client() {}
public static void main(String[] args) {
double payment, principal = 80000;
double annualInterest = .065;
int years = 15;
try {
Registry reg = LocateRegistry.getRegistry("localhost");
stub = (Payment) reg.lookup("Mortgage");
} catch (Exception e) {
System.err.println("Client exception thrown: " + e.toString());
e.printStackTrace();
}
if (args.length == 3) {
try {
principal = Double.parseDouble(args[0]);
annualInterest = Double.parseDouble(args[1]);
years = Integer.parseInt(args[2]);
}
catch (Exception e) {
System.out.println("Wrong input " + e.getMessage() );
System.exit(0);
}
print(principal, annualInterest, years);
} else {
System.out.println("Usage: java Client principal annualInterest years ");
System.out.println("\nFor example: java Client 80000 .065 15 ");
System.out.println("\nYou will get the output like the following: \n");
print(principal, annualInterest, years);
System.exit(0);
}
}
public static void print(double pr, double annRate, int years){
double mpayment = 0;
try {
mpayment = stub.calculatePayment(pr, annRate, years);
}catch(Exception e) {
System.out.println("Remote method exception thrown: " + e.getMessage());
}
System.out.println("The principal is $" + (int)pr);
System.out.println("The annual interest rate is " + annRate*100 +"%");
System.out.println("The term is " + years + " years");
System.out.println("Your monthly payment is $" + mpayment);
}
}
注意點: lookup服務(wù)的方法與以前是不同的
五. 測試使用
現(xiàn)在我們有了4個類
Payment.java-- a remote interface
PaymentImpl.java-- a remote object class
Server.java-- an RMI server
Client.java-- an RMI client
1. 我們把它們都放到一個目錄下, 比如: 以 c:\myrmi
2. 使用javac編譯這4個類. javac -d . Payment.java PaymentImpl.java Server.java Client.java
3. 注意: Java5.0的新特性, 現(xiàn)在在不需要在啟動服務(wù)之前生成Stub了, stub現(xiàn)在自動生成.
而在Java5.0以前是需要用rmic手工生成的.
4. start rmiregistry, 出現(xiàn)一個窗口, 不要關(guān)閉.
5. start java Server, 出現(xiàn)一個窗口, 顯示 Mortgage server is ready to listen...
6. 啟動客戶端java Client, 如果出現(xiàn)
The principal is $150000
The annual interest rate is 6.0%
The term is 15 years
Your monthly payment is $1265.79
那就大功告成了.
注意點: 安全策略機(jī)制進(jìn)行了弱化, 不再需要指定策略文件 授這個權(quán),那個權(quán)的了.
我們都知道,IP是由四段數(shù)字組成,在此,我們先來了解一下3類常用的IP
A類IP段 0.0.0.0 到127.255.255.255
B類IP段 128.0.0.0 到191.255.255.255
C類IP段 192.0.0.0 到223.255.255.255
XP默認(rèn)分配的子網(wǎng)掩碼每段只有255或0
A類的默認(rèn)子網(wǎng)掩碼 255.0.0.0 一個子網(wǎng)最多可以容納1677萬多臺電腦
B類的默認(rèn)子網(wǎng)掩碼 255.255.0.0 一個子網(wǎng)最多可以容納6萬臺電腦
C類的默認(rèn)子網(wǎng)掩碼 255.255.255.0 一個子網(wǎng)最多可以容納254臺電腦
我以前認(rèn)為,要想把一些電腦搞在同一網(wǎng)段,只要IP的前三段一樣就可以了,今天,我才知道我錯了。如果照我這說的話,一個子網(wǎng)就只能容納254臺電腦?真是有點笑話。我們來說詳細(xì)看看吧。
要想在同一網(wǎng)段,只要網(wǎng)絡(luò)標(biāo)識相同就可以了,那要怎么看網(wǎng)絡(luò)標(biāo)識呢?首先要做的是把每段的IP轉(zhuǎn)換為二進(jìn)制。(有人說,我不會轉(zhuǎn)換耶,沒關(guān)系,我們用
Windows自帶計算器就行。打開計算器,點查看>科學(xué)型,輸入十進(jìn)制的數(shù)字,再點一下“二進(jìn)制”這個單選點,就可以切換至二進(jìn)制了。)
把子網(wǎng)掩碼切換至二進(jìn)制,我們會發(fā)現(xiàn),所有的子網(wǎng)掩碼是由一串[red]連續(xù)[/red]的1和一串[red]連續(xù)[/red]的0組成的(一共4段,每段8位,一共32位數(shù))。
255.0.0.0 11111111.00000000.00000000.00000000
255.255.0.0 11111111.11111111.00000000.00000000
255.255.255.0 11111111.11111111.11111111.00000000
這是A/B/C三類默認(rèn)子網(wǎng)掩碼的二進(jìn)制形式,其實,還有好多種子網(wǎng)掩碼,只要是一串連續(xù)的1和一串連續(xù)的0就可以了(每段都是8位)。如
11111111.11111111.11111000.00000000,這也是一段合法的子網(wǎng)掩碼。子網(wǎng)掩碼決定的是一個子網(wǎng)的計算機(jī)數(shù)目,計算機(jī)公
式是2的m次方,其中,我們可以把m看到是后面的多少顆0。如255.255.255.0轉(zhuǎn)換成二進(jìn)制,那就是
11111111.11111111.11111111.00000000,后面有8顆0,那m就是8,255.255.255.0這個子網(wǎng)掩碼可以容納
2的8次方(臺)電腦,也就是256臺,但是有兩個IP是不能用的,那就是最后一段不能為0和255,減去這兩臺,就是254臺。我們再來做一個。
255.255.248.0這個子網(wǎng)掩碼可以最多容納多少臺電腦?
計算方法:
把將其轉(zhuǎn)換為二進(jìn)制的四段數(shù)字(每段要是8位,如果是0,可以寫成8個0,也就是00000000)
11111111.1111111.11111000.00000000
然后,數(shù)數(shù)后面有幾顆0,一共是有11顆,那就是2的11次方,等于2048,這個子網(wǎng)掩碼最多可以容納2048臺電腦。
一個子網(wǎng)最多可以容納多少臺電腦你會算了吧,下面我們來個逆向算法的題。
一個公司有530臺電腦,組成一個對等局域網(wǎng),子網(wǎng)掩碼設(shè)多少最合適?
首先,無疑,530臺電腦用B類IP最合適(A類不用說了,太多,C類又不夠,肯定是B類),但是B類默認(rèn)的子網(wǎng)掩碼是255.255.0.0,可以容納6萬臺電腦,顯然不太合適,那子網(wǎng)掩碼設(shè)多少合適呢?我們先來列個公式。
2的m次方=560
首先,我們確定2一定是大于8次方的,因為我們知道2的8次方是256,也就是C類IP的最大容納電腦的數(shù)目,我們從9次方一個一個試2的9次方是
512,不到560,2的10次方是1024,看來2的10次方最合適了。子網(wǎng)掩碼一共由32位組成,已確定后面10位是0了,那前面的22位就是1,最
合適的子網(wǎng)掩碼就是:11111111.11111111.11111100.00000000,轉(zhuǎn)換成10進(jìn)制,那就是255.255.252.0。
分配和計算子網(wǎng)掩碼你會了吧,下面,我們來看看IP地址的網(wǎng)段。
相信好多人都和偶一樣,認(rèn)為IP只要前三段相同,就是在同一網(wǎng)段了,其實,不是這樣的,同樣,我樣把IP的每一段轉(zhuǎn)換為一個二進(jìn)制數(shù),這里就拿IP:192.168.0.1,子網(wǎng)掩碼:255.255.255.0做實驗吧。
192.168.0.1
11000000.10101000.00000000.00000001
(這里說明一下,和子網(wǎng)掩碼一樣,每段8位,不足8位的,前面加0補(bǔ)齊。)
IP 11000000.10101000.00000000.00000001
子網(wǎng)掩碼 11111111.11111111.11111111.00000000
在這里,向大家說一下到底怎么樣才算同一網(wǎng)段。
要想在同一網(wǎng)段,必需做到網(wǎng)絡(luò)標(biāo)識相同,那網(wǎng)絡(luò)標(biāo)識怎么算呢?各類IP的網(wǎng)絡(luò)標(biāo)識算法都是不一樣的。A類的,只算第一段。B類,只算第一、二段。C類,算第一、二、三段。
算法只要把IP和子網(wǎng)掩碼的每位數(shù)AND就可以了。
AND方法:0和1=0 0和0=0 1和1=1
如:And 192.168.0.1,255.255.255.0,先轉(zhuǎn)換為二進(jìn)制,然后AND每一位
IP 11000000.10101000.00000000.00000001
子網(wǎng)掩碼 11111111.11111111.11111111.00000000
得出AND結(jié)果 11000000.10101000.00000000.00000000
轉(zhuǎn)換為十進(jìn)制192.168.0.0,這就是網(wǎng)絡(luò)標(biāo)識,
再將子網(wǎng)掩碼反取,也就是00000000.00000000.00000000.11111111,與IP AND
得出結(jié)果00000000.00000000.00000000.00000001,轉(zhuǎn)換為10進(jìn)制,即0.0.0.1,
這0.0.0.1就是主機(jī)標(biāo)識。要想在同一網(wǎng)段,必需做到網(wǎng)絡(luò)標(biāo)識一樣。
我們再來看看這個改為默認(rèn)子網(wǎng)掩碼的B類IP
如IP:188.188.0.111,188.188.5.222,子網(wǎng)掩碼都設(shè)為255.255.254.0,在同一網(wǎng)段嗎?
先將這些轉(zhuǎn)換成二進(jìn)制
188.188.0.111 10111100.10111100.00000000.01101111
188.188.5.222 10111100.10111100.00000101.11011010
255.255.254.0 11111111.11111111.11111110.00000000
分別AND,得
10111100.10111100.00000000.00000000
10111100.10111100.00000100.00000000
網(wǎng)絡(luò)標(biāo)識不一樣,即不在同一網(wǎng)段。
判斷是不是在同一網(wǎng)段,你會了吧,下面,我們來點實際的。
一個公司有530臺電腦,組成一個對等局域網(wǎng),子網(wǎng)掩碼和IP設(shè)多少最合適?
子網(wǎng)掩碼不說了,前面算出結(jié)果來了11111111.11111111.11111100.00000000,也就是255.255.252.0
我們現(xiàn)在要確定的是IP如何分配,首先,選一個B類IP段,這里就選188.188.x.x吧
這樣,IP的前兩段確定的,關(guān)鍵是要確定第三段,只要網(wǎng)絡(luò)標(biāo)識相同就可以了。我們先來確定網(wǎng)絡(luò)號。(我們把子網(wǎng)掩碼中的1和IP中的?對就起來,0和*對應(yīng)起來,如下:)
255.255.252.0 11111111.11111111.11111100.00000000
188.188.x.x 10111100.10111100.??????**.********
網(wǎng)絡(luò)標(biāo)識 10111100.10111100.??????00.00000000
由此可知,?處隨便填(只能用0和1填,不一定全是0和1),我們就用全填0吧,*處隨便,這樣呢,我們的IP就是
10111100.10111100.000000**.********,一共有530臺電腦,IP的最后一段1~254可以分給254臺計算
機(jī),530/254=2.086,采用進(jìn)1法,得整數(shù)3,這樣,我們確定了IP的第三段要分成三個不同的數(shù)字,也就是說,把000000**中的**填三
次數(shù)字,只能填1和0,而且每次的數(shù)字都不一樣,至于填什么,就隨我們便了,如00000001,00000010,00000011,轉(zhuǎn)換成二進(jìn)制,分
別是1,2,3,這樣,第三段也確定了,這樣,就可以把IP分成188.188.1.y,188.188.2.y,188.188.3.y,y處隨便填,
只要在1~254范圍之內(nèi),并且這530臺電腦每臺和每臺的IP不一樣,就可以了。
有人也許會說,既然算法這么麻煩,干脆用A類IP和A類默認(rèn)子網(wǎng)掩碼得了,偶要告訴你的是,由于A類IP和A類默認(rèn)子網(wǎng)掩碼的主機(jī)數(shù)目過大,這樣做無疑是大海撈針,如果同時局域網(wǎng)訪問量過頻繁、過大,會影響效率的,所以,最好設(shè)置符合自己的IP和子網(wǎng)掩碼^_^
摘要: Eclipse 遠(yuǎn)程DEBUG 方法 閱讀全文
部分內(nèi)容參考:http://www.linuxdiyf.com/viewarticle.php?id=78024
在/etc/init.d下面新建一個文件dbora,內(nèi)容:
#!/bin/sh
su - $ORA_OWNER -c "lsnrctl start"
su - $ORA_OWNER -c "sqlplus /nolog @$ORACLE_HOME/dbstart.sql"
$ORACLE_HOME/dbstart.sql的內(nèi)容:
conn / as sysdba
startup
exit
將dbora改成可執(zhí)行
chmod 777 dbora
將dbstart.sql改成oracle所有
chown oracle /oracle/product/9.2.0/dbstart.sql
然后作一個連接
ln -s /etc/init.d/dbora /etc/rc2.d/S99dbora
ln -s /etc/init.d/dbora /etc/rc0.d/K10dbora
需要注意的地方是oracle用戶需要正確設(shè)置環(huán)境變量上面的自動啟動才會生效,具體如下
su - oracle
vi .profile (設(shè)置oracle的環(huán)境變量文件,這個文件在oracle用戶的home目錄下)
加入下面變量設(shè)置
ORACLE_OWNER=oracle;export ORACLE_OWNER
ORACLE_HOME=/opt/oracle/product/10.2.0/Db_1;export ORACLE_HOME
ORACLE_SID=orcl;export ORACLE_SID
PATH=$PATH:$ORACLE_HOME/bin
另外如果環(huán)境變量設(shè)置了NLS_LAN變量注意不要設(shè)錯,否則oracle會報:ora-12705錯誤。
用xmanager遠(yuǎn)程在solaris上安裝oracle10g
1.在oracle下載合適的oracle版本
2.用root登陸
3.設(shè)置oracle賬戶信息
1)groupadd dba
2)useradd -d dba -d /opt/oracle -m -s /bin/sh oracle
3)修改/etc/group文件最后一行:dba::100:oracle
4)設(shè)置/opt/oracle目錄權(quán)限 chmod -R 777 oracle
5)vi /etc/system 在最后一行加入(設(shè)置用戶變量)
set shmsys:shminfo_shmmax=4294967295
set shmsys:shminfo_shmmin=1
set shmsys:shminfo_shmmni=100
set shmsys:shminfo_shmseg=10
set semsys:seminfo_semmns=2000
set semsys:seminfo_semmsl=1000
set semsys:seminfo_semmni=100
set semsys:seminfo_semopm=100
set semsys:seminfo_semvmx=32767
set unlimit=3000000
6)passwd oracle 設(shè)置oracle用戶密碼
必需要先重啟才能安裝oracle(sytem設(shè)置才能生效)
注意安裝oracle時需要用oracle用戶安裝
3.設(shè)置display環(huán)境變量
csh
setenv DISPLAY xx.xx.xx.xx:1.0
xx.xx.xx.xx是xmanager所在的ip地址
4.su - oracle 運行oracle的./runInstaller開始安裝
5.手工設(shè)置oracle環(huán)境變量(我裝的時候自動設(shè)置沒有生效)
設(shè)置oracle環(huán)境變量,最好可以設(shè)置到.profile中,不用每次重啟麻煩
csh
setenv ORACLE_OWNER oracle
setenv ORACLE_HOME /opt/oracle/product/10.2.0/Db_1
setenv ORACLE_SID orcl
6.手工啟動oracle
1)su - oracle
2)sqlplus /nolog
3)/>connect sys/system as sysdba
4)/>startup
5)/>quit
6)./lsnrctl start &
看一下oracle的進(jìn)程
ps -ef | grep oracle
7.solaris 命令部分
設(shè)置環(huán)境變量在/.profile中
#vi .profile
LANG=zh_CN.GBK;export LANG
(必須向上面這樣設(shè)置才能生效)
重啟solais
init 6
查看內(nèi)存
/usr/sbin/swap -s
看進(jìn)程的全路徑
/usr/ucb/ps -auxww | more | grep java
websphere4 配置過程中遇到的問題
1.admin.config 中配置oracle driver 的classpath
-Dws.ext.dirs=
連接不上oracle數(shù)據(jù)庫,報java.sql.SQLException: ORA-01453: SET TRANSACTION 必須是事務(wù)處理的第一個語句錯誤
[08.06.06 15:50:48:622 CST] 7a4489 EJBEngine X WSVR0062E:未能啟動 EJB,admin#repository.jar#Node:org.omg.CORBA.portable.UnknownException: minor code: 0 completed: Maybe
[08.06.06 15:50:50:554 CST] 7a4489 EJBEngine I WSVR0037I:正在啟動 EJB jar:Tasks
[08.06.06 15:50:51:329 CST] 7a4489 Helpers W NMSV0610I: NamingException 從javax.naming.Context 實現(xiàn)中拋出。詳細(xì)信息請參照:
上下文實現(xiàn):com.ibm.ejs.ns.jndi.CNContextImpl
上下文方法:lookup
上下文名:domainRoots/UnspecifiedDomainName/legacyRoot
目標(biāo)名:ejsadmin/homes/NodeHome
其它數(shù)據(jù):
異常堆棧跟蹤:javax.naming.NamingException: Error during resolve. Root exception is org.omg.CORBA.portable.UnknownException: minor code: 0 completed: Maybe
使用合適的oracle driver,比如4.0需要用oracle8的class12.jar才行
2.websphere能夠啟動后,通過/bin/adminclient.sh配置應(yīng)用和datasource
1)資源配置jdbc 供應(yīng)商
2)企業(yè)應(yīng)用程序-》右鍵安裝企業(yè)應(yīng)用=》選擇一個空的ear文件
webspherejvm設(shè)置
adminclient->節(jié)點->avgg1->應(yīng)用服務(wù)器->Default Server
-Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Dclient.encoding.override=UTF-8 -Duser.language=zh -Duser.region=CN
3.解析get/post參數(shù)時報錯:
[08.06.11 14:32:38:143 CST] 24ed99 SRTServletRes W Failed to create a writer with encoding: GB2312. The default encoding will be used.
[08.06.11 14:32:44:328 CST] 24ed99 WebGroup X Servlet Error: : java.lang.IllegalArgumentException
at com.ibm.servlet.engine.webapp.RequestUtils.parseQueryString(RequestUtils.java:256)
通過反編譯websphere源代碼找到問題原因,代碼在WS_HOME/lib/webcontainer.jar中
com.ibm.servlet.engine.srt.STRRequestUtils 裝在converter.properties文件
負(fù)責(zé)獲取編碼
public static String getJvmConverter(String s)
{
String s1 = _jvmProps.getProperty(s);
if(s1 != null)
return s1;
else
return s;
}
STRServletRequest#
public String getReaderEncoding()
{
String s = getCharacterEncoding();
if(s == null)
s = SRTRequestUtils.getEncodingFromLocale(getLocale());
if(s == null)
s = System.getProperty("default.client.encoding");
if(s == null)
s = "ISO-8859-1";
return SRTRequestUtils.getJvmConverter(s);
}
需要配置/properties/converter.properties
GB2312=Cp1386 改為GB2312=Gb2312
通過Axis webservice客戶端進(jìn)行NTLM認(rèn)證的方法:
axis是通過httpclient進(jìn)行認(rèn)證的,所以需要先下載httpclient程序,還需要解碼程序commons-codec-1.3.jar
這兩個接口google一下就行了,Axis的客戶端代碼如下:
1 try
2 {
3 String endpoint = "http://localhost:7001/Test/WebServices.asmx?wsdl";
4 Service service = new Service();
5 Call call = (Call)service.createCall();
6
7 call.setTargetEndpointAddress(new URL(endpoint));
8 call.getMessageContext().setUsername("domain\\user");
9 call.getMessageContext().setPassword("password");
10 call.setClientHandlers(new CommonsHTTPSender(),null);
11
12 call.setOperationName(new QName("Method"));
13 call.addParameter("ParamName1", XMLType.XSD_STRING, ParameterMode.IN );
14 call.setReturnClass(String.class);
15 String ret = (String)call.invoke(new Object[]{new String("aaaa")});
16
17 System.out.println(ret);
18
19 }
20 catch(Exception ex)
21 {
22 ex.printStackTrace();
23 }
通過監(jiān)聽客戶端發(fā)出的http頭信息,可以發(fā)現(xiàn)發(fā)出的認(rèn)證信息如下:
Authorization: NTLM LKJSDLKFJLSDKJFLKSDAAAAAAAAAAAAAAAAAAAAAAA=
服務(wù)器如果采用一般的http認(rèn)證,可以HTTPSender代替CommonHttpSender,通過他的代碼可以發(fā)現(xiàn):
1 if (userID != null) {
2 StringBuffer tmpBuf = new StringBuffer();
3
4 tmpBuf.append(userID).append(":").append((passwd == null)
5 ? ""
6 : passwd);
7 otherHeaders.append(HTTPConstants.HEADER_AUTHORIZATION)
8 .append(": Basic ")
9 .append(Base64.encode(tmpBuf.toString().getBytes()))
10 .append("\r\n");
11 }
HttpSender直接生成http的Authorization頭信息進(jìn)行設(shè)置,發(fā)出的http header代碼例子:
Authorization: Basic SDKFJLSDKJLlsdlfksjdflksjw232lj
如果需要進(jìn)行webservice的自定義認(rèn)證可以自己實現(xiàn)axis的handler。
在用dom4j解析xml時如果報下面這個錯誤:
org.dom4j.DocumentException: Error on line 1 of document
file:///d:/xxx.xml :
缺少文件根組件。 Nested exception: 缺少文件根組件。
這是因為xml解析器的問題,以前一直在web環(huán)境下跑程序,用的是xerces解析器,就不會報上面的錯誤。
但是獨立運行的程序如果用jdk1.4.2和dom4j1.6.1時就回有上面的錯誤,因為用的是jdk自帶的解析器
crimson。
解決辦法就是
用xerces解析器,可以從tomcat中找到xercesImpl.jar和xml-api.jar放到工程中就可以了。
另外在jdk1.5下面也沒有這個問題,應(yīng)該是jdk1.5自帶的解析器解決了這個問題了。
根本原因是xml含有bom頭導(dǎo)致,就是EF BB BF。
如果報這個錯誤:Content is not allowed in prolog
使用dom4j1.6.1就ok了。
生活的態(tài)度
好的生活態(tài)度可以使人安定、平靜、充滿信心,反之則使人急躁、郁悶、自卑。
人啊,怎么活都是一輩子,快樂、幸福、積極的活著才有意義。
努力工作、技術(shù)專家、努力學(xué)習(xí)、珍惜自己的幸福。
websphere升級jdk的策略是前3個版本號必須一致,比如1.3.0這3位必須相同,或者升級websphere才能升級更高版本的jdk。
經(jīng)過這兩天查找的資料發(fā)現(xiàn)從Websphere4升級到sunjdk1.4.2這條路是行不通的,Websphere4和5似乎ibm已經(jīng)不繼續(xù)支持了,推薦
升級到更高的版本。
在網(wǎng)上找到一篇:如何升級 WebSphere Application Server v4.01 for Solaris 的 JDK 到 SUN JDK v1.3.1
按照這個配置嘗試了一下升級,結(jié)果報錯corba讀取數(shù)據(jù)會有問題,可能是序列化的問題,錯誤如下:
java.rmi.MarshalException: CORBA MARSHAL 0 No; nested exception is:
org.omg.CORBA.MARSHAL: Unable to read value from underlying bridge : null vmcid: 0x0 minor code: 0 completed: No
websphere升級的策略:http://www-1.ibm.com/support/docview.wss?uid=swg21138332
weblogic下通過ajax解析不了xml文件(通過IE可能是可以打開的),這是因為xmlhttp.responseXML.documentElement返回空造成的,
這個問題原因分兩種情況:
1.動態(tài)生成文件需要通過respnse.setContentType("text/xml;charset=UTF-8")來顯示的聲明文檔類型;
2.靜態(tài)xml,這需要在web.xml中加入下面的聲明
<mime-mapping>
<extension>xml</extension>
<mime-type>text/xml</mime-type>
</mime-mapping>
這時再次訪問時weblgoic就給加上contentType了。
注意此時如果以前訪問過一次這個靜態(tài)的xml,ie會緩存,所以一般設(shè)置完后ajax還是不能解析,這時
需要清除ie緩存,然后再試一次就ok了:)。
對于直接通過在ie下打開文件,比如word(xml也是一樣的),默認(rèn)weblogic訪問xxx.doc時返回如下的響應(yīng),沒有contentType
HTTP/1.1 200 OK
Date: Thu, 25 Oct 2007 07:22:26 GMT
Content-Length: 10752
Last-Modified: Thu, 25 Oct 2007 07:20:04 GMT
Accept-Ranges: bytes
所以也需要在web.xml加入mime-type才行
<mime-mapping>
<extension>doc</extension>
<mime-type>application/msword</mime-type>
</mime-mapping>
這時weblogic的響應(yīng)信息:
200 OK
Date: Thu, 25 Oct 2007 07:28:38 GMT
Content-Length: 10752
Content-Type: application/msword
Last-Modified: Thu, 25 Oct 2007 07:20:04 GMT
Accept-Ranges: bytes
這樣可以通過提示用戶打開還是保存,而不是一堆亂碼了。
對于下載的需求,可以在servlet設(shè)置response的header
response.setHeader("Content-Disposition","attachment;filename=xxx.xxx");
關(guān)于Content-Disposition的說明:http://www.ietf.org/rfc/rfc2183.txt
界面可以通過<a href="download?file=xxx">下載</a>實現(xiàn)
摘要: sybase 基本語法,存儲過程,觸發(fā)器 閱讀全文
以前挺麻煩的,現(xiàn)在非常簡單,使用swt3.2就ok了:) - Q: How do I make SWT use the Windows XP themes?
- A: In order for an application to use Windows XP themes, there must be a manifest file
located in the same place as the executable that launches the application.
Here is a sample manifest file to download.
The name of the manifest file must match the name of the executable.
In the case of eclipse, the executable is javaw.exe and the manifest file
must have the name javaw.exe.manifest. The manifest file must be in the
jre\bin folder for the VM you use to launch Eclipse. Note: the
eclipse.exe executable does not launch Eclipse; eclipse.exe
displays a splash screen and then invokes the Java VM.
Note: As of SWT 3.2, the manifest file is no longer needed.
Oracle Bfile的處理 oracleBfile是oracle的一個lob類型,但實際存儲文件時是存儲在磁盤上的,需要自己存儲 需要在oracle上創(chuàng)建一個目錄的引用 create or replace directory attach_dir as 'c:/oracle/tmp'
插入bfile時 insert into table values (bfilename('ATTACH_DIR','file.ext'));
注意目錄名必須大寫,oracle在創(chuàng)建目錄時內(nèi)部都給轉(zhuǎn)換成大寫的了 也可以在創(chuàng)建目錄時 create or replace directory "attach_dir" as 'c:/oracle/tmp' 加上雙引號這樣就可以用小寫的了(或大小寫混合)。
BFILE Datatype The BFILE datatype enables access to binary file LOBs that are stored in file systems outside the Oracle database. A BFILE column or attribute stores a BFILE locator, which serves as a pointer to a binary file on the server’s file system. The locator maintains the directory alias and the filename. You can change the filename and path of a BFILE without affecting the base table by using the BFILENAME function. Binary file LOBs do not participate in transactions and are not recoverable. Rather, the underlying operating system provides file integrity and durability. The maximum file size supported is 4 gigabytes. The database administrator must ensure that the file exists and that Oracle processes have operating system read permissions on the file. The BFILE datatype enables read-only support of large binary files. You cannot modify or replicate such a file. Oracle provides APIs to access file data. The primary interfaces that you use to access file data are the DBMS_LOB package and the OCI. See Also: n Oracle9i Supplied PL/SQL Packages and Types Reference and Oracle Call Interface Programmer’s Guide for more information about these interfaces and LOBs n the modify_col_properties clause of ALTER TABLE on page 11-2 and TO_LOB on page 6-178 for more information on converting LONG columns to LOB columns See Also: BFILENAME on page 6-23 for more information on this built-in SQL function See Also: n Oracle9i Application Developer’s Guide - Large Objects (LOBs) and Oracle Call Interface Programmer’s Guide for more information about LOBs. n CREATE DIRECTORY on page 13-49
摘要: 工作流-變量與表單的一些想法
變量-業(yè)務(wù)系統(tǒng)與工作流關(guān)聯(lián)的紐帶
表單-利用變量提供基本的接收用戶交互的功能(本質(zhì)上就是給用戶提供一個變量的一個修改方式)
閱讀全文
|
|
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
29 | 30 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|
常用鏈接
留言簿(12)
隨筆分類
隨筆檔案
文章分類
文章檔案
相冊
收藏夾
好友的blog
技術(shù)網(wǎng)站
搜索
積分與排名
最新評論

閱讀排行榜
評論排行榜
|
|