2009年11月4日
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可以根據wsdl文件生成對應的客戶端、服務端java代碼,非常方便,具體使用方式為: java org.apache.axis.wsdl.WSDL2Java-t -s D:\test\webservice\TestCaseAndWsdl\TestService\TaskService.wsdl 上述classpath沒有設置,需要自行設定引用jar文件。
當關系數據庫試圖在一個單一表中存儲數 TB 的數據時,總性能經常會降低。顯然,對所有數據編索引不僅對于讀而且對于寫都很耗時。因為 NoSQL 數據商店尤其適合存儲大型數據(如 Google 的 Bigtable),顯然 NoSQL 是一種非關系數據庫方法。對于傾向于使用 ACID-ity 和實體結構關系數據庫的開發人員及需要這種結構的項目來說,切分是一個令人振奮的可選方法。 切分 是數據庫分區的一個分支,但是它不是本地數據庫技術 — 切分發生在應用程序級別。在各種切分實現中,Hibernate Shards 是 Java™ 技術世界中最受歡迎的一個。這個靈活絕妙的項目可以讓您使用映射至邏輯數據庫的 POJO 對切分數據集進行幾乎無縫操作(我將在下文簡要介紹 “幾乎” 的原因)。使用 Hibernate Shards 時,您無須將您的 POJO 特別映射至切分 — 您可以像使用 Hibernate 方法對任何常見關系數據庫進行映射時一樣對其進行映射。Hibernate Shards 可以為您管理低級別的切分任務。 事實上,Hibernate Shards 的編碼工作比較簡單。其中關鍵的部分在于判斷 如何進行切分以及對什么進行切分。 切分簡介 數據庫切分 是一個固有的關系流程,可以通過一些邏輯數據塊將一個表的行分為不同的小組。例如,如果您正在根據時間戳對一個名為 foo 的超大型表進行分區,2010 年 8 月之前的所有數據都將進入分區 A,而之后的數據則全部進入分區 B。分區可以加快讀寫速度,因為它們的目標是單獨分區中的較小型數據集。 分區功能并不總是可用的(MySQL 直到 5.1 版本后才支持),而且其需要的商業系統的成本也讓人望而卻步。更重要的是,大部分分區實現在同一個物理機上存儲數據,所以受到硬件基礎的影響。除此之外, 分區也不能鑒別硬件的可靠性或者說缺乏可靠性。因此,很多智慧的人們開始尋找進行伸縮的新方法。 切分 實質上是數據庫級別的分區:它不是通過數據塊分割數據表的行,而是通過一些邏輯數據元素對數據庫本身進行分割(通常跨不同的計算機)。也就是說,切分不是將數據表 分割成小塊,而是將整個數據庫 分割成小塊。 切分的一個典型示例是基于根據區域對一個存儲世界范圍客戶數據的大型數據庫進行分割:切分 A 用于存儲美國的客戶信息,切分 B 用戶存儲亞洲的客戶信息,切分 C 歐洲,等。這些切分分別處于不同的計算機上,且每個切分將存儲所有相關數據,如客戶喜好或訂購歷史。 切分的好處(如分區一樣)在于它可以壓縮大型數據:單獨的數據表在每個切分中相對較小,這樣就可以支持更快速的讀寫速度,從而提高性能。切分 還可以改善可靠性,因為即便一個切分意外失效,其他切分仍然可以服務數據。而且因為切分是在應用程序層面進行的,您可以對不支持常規分區的數據庫進行切分 處理。資金成本較低同樣也是一個潛在優勢。 切分和策略 像很多其他技術一樣,進行切分時也需要作出部分妥協。因為切分不是一項本地數據庫技術 — 也就是說,必須在應用程序中實現 —在開始切分之前需要制定出您的切分策略。進行切分時主鍵和跨切分查詢都扮演重要角色,主要通過定義您不可以做什么實現。 主鍵 切分利用多個數據庫,其中所有數據庫都獨立起作用,不干涉其他切分。因此,如果您依賴于數據庫序列(如自動主鍵生成),很有可能在一個數據庫集中將出現同 一個主鍵??梢钥绶植际綌祿靺f調序列,但是這樣會增加系統的復雜程度。避免相同主鍵最安全的方法就是讓應用程序(應用程序將管理切分系統)生成主鍵。 跨切分查詢 大部分切分實現(包括 Hibernate Shards)不支持跨切分查詢,這就意味著,如果您想利用不同切分的兩個數據集,就必須處理額外的長度。(有趣的是,Amazon 的 SimpleDB 也禁止跨域查詢)例如,如果將美國客戶信息存儲在切分 1 中,還需要將所有相關數據存儲在此。如果您嘗試將那些數據存儲在切分 2 中,情況就會變得復雜,系統性能也可能受影響。這種情況還與之前提到的一點有關 — 如果您因為某種原因需要進行跨切分連接,最好采用一種可以消除重復的方式管理鍵! 很明顯,在建立數據庫前必須全面考慮切分策略。一旦選擇了一個特定的方向之后,您差不多就被它綁定了 — 進行切分后很難隨便移動數據了。 一個策略示例 因為切分將您綁定在一個線型數據模型中(也就是說,您無法輕松連接不同切分中的數據),您必須對如何在每個切分中對數據進行邏輯組織有一個清 晰的概念。這可以通過聚焦域中的主要節點實現。如在一個電子商務系統中,主要節點可以是一個訂單或者一個客戶。因此,如果您選擇 “客戶” 作為切分策略的節點,那么與客戶有關的所有數據將移動至各自的切分中,但是您仍然必須選擇將這些數據移動至哪個切分。 對于客戶來說,您可以根據所在地(歐洲、亞洲、非洲等)切分,或者您也可以根據其他元素進行切分。這由您決定。但是,您的切分策略應該包含將 數據均勻分布至所有切分的方法。切分的總體概念是將大型數據集分割為小型數據集;因此,如果一個特定的電子商務域包含一個大型的歐洲客戶集以及一個相對小 的美國客戶集,那么基于客戶所在地的切分可能沒有什么意義。 回到比賽 — 使用切分! 現在讓我們回到我經常提到的賽跑應用程序示例中,我可以根據比賽或參賽者進行切分。在本示例中,我將根據比賽進行切分,因為我看到域是根據參 加不同比賽的參賽者進行組織的。因此,比賽是域的根。我也將根據比賽距離進行切分,因為比賽應用程序包含不同長度和不同參賽者的多項比賽。 請注意:在進行上述決定時,我已經接受了一個妥協:如果一個參賽者參加了不止一項比賽,他們分屬不同的切分,那該怎么辦 呢?Hibernate Shards (像大多數切分實現一樣)不支持跨切分連接。我必須忍受這些輕微不便,允許參賽者被包含在多個切分中 — 也就是說,我將在參賽者參加的多個比賽切分中重建該參賽者。 為了簡便起見,我將創建兩個切分:一個用于 10 英里以下的比賽;另一個用于 10 英里以上的比賽。 實現 Hibernate Shards Hibernate Shards 幾乎可以與現有 Hibernate 項目無縫結合使用。唯一問題是 Hibernate Shards 需要一些特定信息和行為。比如,需要一個切分訪問策略、一個切分選擇策略和一個切分處理策略。這些是您必須實現的接口,雖然部分情況下,您可以使用默認策 略。我們將在后面的部分逐個了解各個接口。 ShardAccessStrategy 執行查詢時,Hibernate Shards 需要一個決定首個切分、第二個切分及后續切分的機制。Hibernate Shards 無需確定查詢什么(這是 Hibernate Core 和基礎數據庫需要做的),但是它確實意識到,在獲得答案之前可能需要對多個切分進行查詢。因此,Hibernate Shards 提供了兩種極具創意的邏輯實現方法:一種方法是根據序列機制(一次一個)對切分進行查詢,直到獲得答案為止;另一種方法是并行訪問策略,這種方法使用一個 線程模型一次對所有切分進行查詢。 為了使問題簡單,我將使用序列策略,名稱為 SequentialShardAccessStrategy 。我們將稍后對其進行配置。 ShardSelectionStrategy 當創建一個新對象時(例如,當通過 Hibernate 創建一個新 Race 或 Runner 時),Hibernate Shards 需要知道需將對應的數據寫入至哪些切分。因此,您必須實現該接口并對切分邏輯進行編碼。如果您想進行默認實現,有一個名為 RoundRobinShardSelectionStrategy 的策略,它使用一個循環策略將數據輸入切分中。 對于賽跑應用程序,我需要提供根據比賽距離進行切分的行為。因此,我們需要實現 ShardSelectionStrategy 接口并提供依據 Race 對象的 distance 采用 selectShardIdForNewObject 方法進行切分的簡易邏輯。(我將稍候在 Race 對象中展示。) 運行時,當在我的域對象上調用某一類似 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 或其他對象,情況會稍微復雜一些。我已經編碼了一個邏輯規則,其中有三個原則: - 一名
Runner 在沒有對應的 Race 時無法存在。 - 如果
Runner 被創建時參加了多場 Race s,這名 Runner 將被持久化到尋找到的首場 Race 所屬的切分中。(順便說一句,該原則對未來有負面影響。) - 如果還保存了其他域對象,現在將引發一個異常。
然后,您就可以擦掉額頭的熱汗了,因為大部分艱難的工作已經搞定了。隨著比賽應用程序的增長,我所使用的邏輯可能會顯得不夠靈活,但是它完全可以順利地完成這次演示! ShardResolutionStrategy 當通過鍵搜索一個對象時,Hibernate Shards 需要一種可以決定首個切分的方法。將需要使用 SharedResolutionStrategy 接口對其進行指引。 如我之前提到的那樣,切分迫使您重視主鍵,因為您將需要親自管理這些主鍵。幸運的是,Hibernate 在提供鍵或 UUID 生成方面表現良好。因此 Hibernate Shards 創造性地提供一個 ID 生成器,名為 ShardedUUIDGenerator ,它可以靈活地將切分 ID 信息嵌入到 UUID 中。 如果您最后使用 ShardedUUIDGenerator 進行鍵生成(我在本文中也將采取這種方法),那么您也可以使用 Hibernate Shards 提供的創新 ShardResolutionStrategy 實現,名為 AllShardsShardResolutionStrategy ,這可以決定依據一個特定對象的 ID 搜索什么切分。 配置好 Hibernate Shards 工作所需的三個接口后,我們就可以對切分示例應用程序的第二步進行實現了?,F在應該啟動 Hibernate 的 SessionFactory 了。 配置 Hibernate Shards Hibernate 的其中一個核心接口對象是它的 SessionFactory 。Hibernate 的所有神奇都是在其配置 Hibernate 應用程序過程中通過這個小對象實現的,例如,通過加載映射文件和配置。如果您使用了注釋或 Hibernate 珍貴的 .hbm 文件,那么您還需要一個 SessionFactory 來讓 Hibernate 知道哪些對象是可以持久化的,以及將它們持久化到 哪里。 因此,使用 Hibernate Shards 時,您必須使用一個增強的 SessionFactory 類型來配置多個數據庫。它可以被命名為 ShardedSessionFactory ,而且它當然是 SessionFactory 類型的。當創建一個 ShardedSessionFactory 時,您必須提供之前配置好的三個切分實現類型(ShardAccessStrategy 、ShardSelectionStrategy 和 ShardResolutionStrategy )。您還需提供 POJO 所需的所有映射文件。(如果您使用一個基于備注的 Hibernate POJO 配置,情況可能會有所不同。)最后,一個 ShardedSessionFactory 示例需要每個切分都對應多個 Hibernate 配置文件。 創建一個 Hibernate 配置 我已經創建了一個 ShardedSessionFactoryBuilder 類型,它有一個主要方法 createSessionFactory ,可以創建一個配置合理的 SessionFactory 。之后,我將將所有的一切都與 Spring 連接在一起(現在誰不使用一個 IOC 容器?)。現在,清單 2 顯示了 ShardedSessionFactoryBuilder 的主要作用:創建一個 Hibernate 配置 : 清單 2. 創建一個 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 配置文件中創建了一個簡單的 Configuration 。 該文件包含如下信息,如使用的是什么類型的數據庫、用戶名和密碼等,以及所有必須的資源文件,如 POJO 所用的 .hbm 文件。在進行切分的情況下,您通常需要使用多個數據庫配置,但是 Hibernate Shards 支持您僅使用一個 hibernate.cfg.xml 文件,從而簡化了整個過程(但是,如您在 清單 4 中所看到的,您將需要對使用的每一個切分準備一個 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 中所示,我已經配置了一個切分 — 在本示例中,除切分 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 屬性對跨切分關系進行了檢查。根據 Hibernate Shards 文檔記錄,該屬性非常耗時,在生成環境中應該關閉。 最后,ShardedSessionFactoryBuilder 通過創建 ShardStrategyFactory ,然后添加三個類型(包括 清單 1 中的 RacerShardSelectionStrategy ),將一切都整合到了一起,如清單 7 中所示: 清單 7. 創建 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; }
| 最后,我執行了那個名為 createSessionFactory 的絕妙方法,在本示例中創建了一個 ShardedSessionFactory ,如清單 8 所示: 清單 8. 創建 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 連接域對象 現在可以深呼吸一下了,因為我們馬上就成功了。到目前為止,我已經創建一個可以合理配置 ShardedSessionFactory 的生成器類,其實就是實現 Hibernate 無處不在的 SessionFactory 類型。ShardedSessionFactory 完成了切分中所有的神奇。它利用我在 清單 1 中所部署的切分選擇策略,并從我配置的兩個切分中讀寫數據。(清單 6 顯示了切分 0 和切分 1 的配置幾乎相同。) 現在我需要做的就是連接我的域對象,在本示例中,因為它們依賴于 Hibernate,需要一個 SessionFactory 類型進行工作。我將僅使用我的 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 中創建了一個類似工廠的 bean;也就是說,我的 RaceDAOImpl 類型有一個名為 sessionFactory 的屬性,是 SessionFactory 類型。之后,mySessionFactory 引用通過在 ShardedSessionFactoryBuilder 上調用 createSessionFactory 方法創建了一個 SessionFactory 示例,如 清單 4 中所示。 當我為我的 Race 對象示例使用 Spring(我主要將其作為一個巨型工廠使用,以返回預配置的對象)時,一切事情就都搞定了。雖然沒有展示,RaceDAOImpl 類型是一個利用 Hibernate 模板進行數據存儲和檢索的對象。我的 Race 類型包含一個 RaceDAOImpl 示例,它將所有與數據商店相關的活動都推遲至此。很默契,不是嗎? 請注意,我的 DAO 與 Hibernate Shards 在代碼方面并沒有綁定,而是通過配置進行了綁定。配置(如 清單 5 中的)將它們綁定在一個特定切分 UUID 生成方案中,也就是說了我可以在需要切分時從已有 Hibernate 實現中重新使用域對象。 切分:使用 easyb 的測試驅動 接下來,我需要驗證我的切分實現可以工作。我有兩個數據庫并通過距離進行切分,所以當我創建一場馬拉松時(10 英里以上的比賽),該 Race 示例應在切分 1 中找到。一個小型的比賽,如 5 公里的比賽(3.1 英里),將在切分 0 中找到。創建一場 Race 后,我可以檢查單個數據庫的記錄。 在清單 10 中,我已經創建了一場馬拉松,然后繼續驗證記錄確實是在切分 1 中而非切分 0 中。使事情更加有趣(和簡單)的是,我使用了 easyb,這是一個基于 Groovy 的行為驅動開發架構,利用自然語言驗證。easyb 也可以輕松處理 Java 代碼。即便不了解 Groovy 或 easyb,您也可以通過查看清單 10 中的代碼,看到一切如期進行。(請注意,我幫助創建了 easyb,并且在 developerWorks 中對這個話題發表過文章。) 清單 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() } }
| 當然,我的工作還沒有完 — 我還需要創建一個短程比賽,并驗證其位于切分 0 中而非切分 1 中。您可以在本文提供的 代碼下載 中看到該驗證操作! 切分的利弊 切分可以增加應用程序的讀寫速度,尤其是如果您的應用程序包含大量數據 — 如數 TB — 或者您的域處于無限制發展中,如 Google 或 Facebook。 在進行切分之前,一定要確定應用程序的規模和增長對其有利。切分的成本(或者說缺點)包括對如何存儲和檢索數據的特定應用程序邏輯進行編碼的成本。進行切分后,您多多少少都被鎖定在您的切分模型中,因為重新切分并非易事。 如果能夠正確實現,切分可以用于解決傳統 RDBMS 規模和速度問題。切分對于綁定于關系基礎架構、無法繼續升級硬件以滿足大量可伸縮數據存儲要求的組織來說是一個非常成本高效的決策。
最后更新時間 2009-04-10 更新人 dormando@rydia.net 這里收集了經常被問到的關于memcached的問題 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">集群架構方面的問題 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached是怎么工作的? Memcached的神奇來自兩階段哈希(two-stage hash)。Memcached就像一個巨大的、存儲了很多<key,value>對的哈希表。通過key,可以存儲或查詢任意的數據。 客戶端可以把數據存儲在多臺memcached上。當查詢數據時,客戶端首先參考節點列表計算出key的哈希值(階段一哈希),進而選中一個節點; 客戶端將請求發送給選中的節點,然后memcached節點通過一個內部的哈希算法(階段二哈希),查找真正的數據(item)。 舉個列子,假設有3個客戶端1, 2, 3,3臺memcached A, B, C: Client 1想把數據"barbaz"以key "foo"存儲。Client 1首先參考節點列表(A, B, C),計算key "foo"的哈希值,假設memcached B被選中。接著,Client 1直接connect到memcached B,通過key "foo"把數據"barbaz"存儲進去。Client 2使用與Client 1相同的客戶端庫(意味著階段一的哈希算法相同),也擁有同樣的memcached列表(A, B, C)。 于是,經過相同的哈希計算(階段一),Client 2計算出key "foo"在memcached B上,然后它直接請求memcached B,得到數據"barbaz"。 各種客戶端在memcached中數據的存儲形式是不同的(perl Storable, php serialize, java hibernate, JSON等)。一些客戶端實現的哈希算法也不一樣。但是,memcached服務器端的行為總是一致的。 最后,從實現的角度看,memcached是一個非阻塞的、基于事件的服務器程序。這種架構可以很好地解決C10K problem ,并具有極佳的可擴展性。 可以參考A Story of Caching ,這篇文章簡單解釋了客戶端與memcached是如何交互的。 memcached最大的優勢是什么? 請仔細閱讀上面的問題(即memcached是如何工作的)。Memcached最大的好處就是它帶來了極佳的水平可擴展性,特別是在一個巨大的系 統中。由于客戶端自己做了一次哈希,那么我們很容易增加大量memcached到集群中。memcached之間沒有相互通信,因此不會增加 memcached的負載;沒有多播協議,不會網絡通信量爆炸(implode)。memcached的集群很好用。內存不夠了?增加幾臺 memcached吧;CPU不夠用了?再增加幾臺吧;有多余的內存?在增加幾臺吧,不要浪費了。 基于memcached的基本原則,可以相當輕松地構建出不同類型的緩存架構。除了這篇FAQ,在其他地方很容易找到詳細資料的。 看看下面的幾個問題吧,它們在memcached、服務器的local cache和MySQL的query cache之間做了比較。這幾個問題會讓您有更全面的認識。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached和MySQL的query cache相比,有什么優缺點? 把memcached引入應用中,還是需要不少工作量的。MySQL有個使用方便的query cache,可以自動地緩存SQL查詢的結果,被緩存的SQL查詢可以被反復地快速執行。Memcached與之相比,怎么樣呢?MySQL的query cache是集中式的,連接到該query cache的MySQL服務器都會受益。 - 當您修改表時,MySQL的query cache會立刻被刷新(flush)。存儲一個memcached item只需要很少的時間,但是當寫操作很頻繁時,MySQL的query cache會經常讓所有緩存數據都失效。
- 在多核CPU上,MySQL的query cache會遇到擴展問題(scalability issues)。在多核CPU上,query cache會增加一個全局鎖(global lock), 由于需要刷新更多的緩存數據,速度會變得更慢。
- 在MySQL的query cache中,我們是不能存儲任意的數據的(只能是SQL查詢結果)。而利用memcached,我們可以搭建出各種高效的緩存。比如,可以執行多個獨立 的查詢,構建出一個用戶對象(user object),然后將用戶對象緩存到memcached中。而query cache是SQL語句級別的,不可能做到這一點。在小的網站中,query cache會有所幫助,但隨著網站規模的增加,query cache的弊將大于利。
- query cache能夠利用的內存容量受到MySQL服務器空閑內存空間的限制。給數據庫服務器增加更多的內存來緩存數據,固然是很好的。但是,有了memcached,只要您有空閑的內存,都可以用來增加memcached集群的規模,然后您就可以緩存更多的數據。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached和服務器的local cache(比如PHP的APC、mmap文件等)相比,有什么優缺點? 首先,local cache有許多與上面(query cache)相同的問題。local cache能夠利用的內存容量受到(單臺)服務器空閑內存空間的限制。不過,local cache有一點比memcached和query cache都要好,那就是它不但可以存儲任意的數據,而且沒有網絡存取的延遲。 - local cache的數據查詢更快??紤]把highly common的數據放在local cache中吧。如果每個頁面都需要加載一些數量較少的數據,考慮把它們放在local cached吧。
- local cache缺少集體失效(group invalidation)的特性。在memcached集群中,刪除或更新一個key會讓所有的觀察者覺察到。但是在local cache中, 我們只能通知所有的服務器刷新cache(很慢,不具擴展性),或者僅僅依賴緩存超時失效機制。
- local cache面臨著嚴重的內存限制,這一點上面已經提到。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached的cache機制是怎樣的? Memcached主要的cache機制是LRU(最近最少用)算法+超時失效。當您存數據到memcached中,可以指定該數據在緩存中可以呆 多久Which is forever, or some time in the future。如果memcached的內存不夠用了,過期的slabs會優先被替換,接著就輪到最老的未被使用的slabs。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached如何實現冗余機制? 不 實現!我們對這個問題感到很驚訝。Memcached應該是應用的緩存層。它的設計本身就不帶有任何冗余機制。如果一個memcached節點失去了所有 數據,您應該可以從數據源(比如數據庫)再次獲取到數據。您應該特別注意,您的應用應該可以容忍節點的失效。不要寫一些糟糕的查詢代碼,寄希望于 memcached來保證一切!如果您擔心節點失效會大大加重數據庫的負擔,那么您可以采取一些辦法。比如您可以增加更多的節點(來減少丟失一個節點的影 響),熱備節點(在其他節點down了的時候接管IP),等等。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached如何處理容錯的? 不處理!:) 在memcached節點失效的情況下,集群沒有必要做任何容錯處理。如果發生了節點失效,應對的措施完全取決于用戶。節點失效時,下面列出幾種方案供您選擇: - 忽略它! 在失效節點被恢復或替換之前,還有很多其他節點可以應對節點失效帶來的影響。
- 把失效的節點從節點列表中移除。做這個操作千萬要小心!在默認情況下(余數式哈希算法),客戶端添加或移除節點,會導致所有的緩存數據不可用!因為哈希參照的節點列表變化了,大部分key會因為哈希值的改變而被映射到(與原來)不同的節點上。
- 啟動熱備節點,接管失效節點所占用的IP。這樣可以防止哈希紊亂(hashing chaos)。
- 如果希望添加和移除節點,而不影響原先的哈希結果,可以使用一致性哈希算法(consistent hashing)。您可以百度一下一致性哈希算法。支持一致性哈希的客戶端已經很成熟,而且被廣泛使用。去嘗試一下吧!
- 兩次哈希(reshing)。當客戶端存取數據時,如果發現一個節點down了,就再做一次哈希(哈希算法與前一次不同),重新選擇另一個節點 (需要注意的時,客戶端并沒有把down的節點從節點列表中移除,下次還是有可能先哈希到它)。如果某個節點時好時壞,兩次哈希的方法就有風險了,好的節 點和壞的節點上都可能存在臟數據(stale data)。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">如何將memcached中item批量導入導出? 您不應該這樣做!Memcached是一個非阻塞的服務器。任何可能導致memcached暫停或瞬時拒絕服務的操作都應該值得深思熟慮。向 memcached中批量導入數據往往不是您真正想要的!想象看,如果緩存數據在導出導入之間發生了變化,您就需要處理臟數據了;如果緩存數據在導出導入 之間過期了,您又怎么處理這些數據呢? 因此,批量導出導入數據并不像您想象中的那么有用。不過在一個場景倒是很有用。如果您有大量的從不變化 的數據,并且希望緩存很快熱(warm)起來,批量導入緩存數據是很有幫助的。雖然這個場景并不典型,但卻經常發生,因此我們會考慮在將來實現批量導出導入的功能。 Steven Grimm,一如既往地,,在郵件列表中給出了另一個很好的例子:http://lists.danga.com/pipermail/memcached/2007-July/004802.html 。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">但是我確實需要把memcached中的item批量導出導入,怎么辦?? 好吧好吧。如果您需要批量導出導入,最可能的原因一般是重新生成緩存數據需要消耗很長的時間,或者數據庫壞了讓您飽受痛苦。 如果一個memcached節點down了讓您很痛苦,那么您還會陷入其他很多麻煩。您的系統太脆弱了。您需要做一些優化工作。比如處理"驚群"問 題(比如 memcached節點都失效了,反復的查詢讓您的數據庫不堪重負...這個問題在FAQ的其他提到過),或者優化不好的查詢。記住,Memcached 并不是您逃避優化查詢的借口。 如果您的麻煩僅僅是重新生成緩存數據需要消耗很長時間(15秒到超過5分鐘),您可以考慮重新使用數據庫。這里給出一些提示: - 使用MogileFS(或者CouchDB等類似的軟件)在存儲item。把item計算出來并dump到磁盤上。MogileFS可以很方便地 覆寫item,并提供快速地訪問。.您甚至可以把MogileFS中的item緩存在memcached中,這樣可以加快讀取速度。 MogileFS+Memcached的組合可以加快緩存不命中時的響應速度,提高網站的可用性。
- 重新使用MySQL。MySQL的 InnoDB主鍵查詢的速度非常快。如果大部分緩存數據都可以放到VARCHAR字段中,那么主鍵查詢的性能將更好。從memcached中按key查詢 幾乎等價于MySQL的主鍵查詢:將key 哈希到64-bit的整數,然后將數據存儲到MySQL中。您可以把原始(不做哈希)的key存儲都普通的字段中,然后建立二級索引來加快查 詢...key被動地失效,批量刪除失效的key,等等。
上面的方法都可以引入memcached,在重啟memcached的時候仍然提供很好的性能。由于您不需要當心"hot"的item被 memcached LRU算法突然淘汰,用戶再也不用花幾分鐘來等待重新生成緩存數據(當緩存數據突然從內存中消失時),因此上面的方法可以全面提高性能。 關于這些方法的細節,詳見博客:http://dormando.livejournal.com/495593.html 。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached是如何做身份驗證的? 沒有身份認證機制!memcached是運行在應用下層的軟件(身份驗證應該是應用上層的職責)。memcached的客戶端和服務器端之所以是輕量級的,部分原因就是完全沒有實現身份驗證機制。這樣,memcached可以很快地創建新連接,服務器端也無需任何配置。
如果您希望限制訪問,您可以使用防火墻,或者讓memcached監聽unix domain socket。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached的多線程是什么?如何使用它們? 線 程就是定律(threads rule)!在Steven Grimm和Facebook的努力下,memcached 1.2及更高版本擁有了多線程模式。多線程模式允許memcached能夠充分利用多個CPU,并在CPU之間共享所有的緩存數據。memcached使 用一種簡單的鎖機制來保證數據更新操作的互斥。相比在同一個物理機器上運行多個memcached實例,這種方式能夠更有效地處理multi gets。
如果您的系統負載并不重,也許您不需要啟用多線程工作模式。如果您在運行一個擁有大規模硬件的、龐大的網站,您將會看到多線程的好處。
更多信息請參見:http://code.sixapart.com/svn/memcached/trunk/server/doc/threads.txt 。
簡 單地總結一下:命令解析(memcached在這里花了大部分時間)可以運行在多線程模式下。memcached內部對數據的操作是基于很多全局鎖的(因 此這部分工作不是多線程的)。未來對多線程模式的改進,將移除大量的全局鎖,提高memcached在負載極高的場景下的性能。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached能接受的key的最大長度是多少? key 的最大長度是250個字符。需要注意的是,250是memcached服務器端內部的限制,如果您使用的客戶端支持"key的前綴"或類似特性,那么 key(前綴+原始key)的最大長度是可以超過250個字符的。我們推薦使用使用較短的key,因為可以節省內存和帶寬。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached對item的過期時間有什么限制? 過期時間最大可以達到30天。memcached把傳入的過期時間(時間段)解釋成時間點后,一旦到了這個時間點,memcached就把item置為失效狀態。這是一個簡單但obscure的機制。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached最大能存儲多大的單個item? 1MB。如果你的數據大于1MB,可以考慮在客戶端壓縮或拆分到多個key中。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">為什么單個item的大小被限制在1M byte之內? 啊...這是一個大家經常問的問題!
簡單的回答:因為內存分配器的算法就是這樣的。
詳 細的回答:Memcached的內存存儲引擎(引擎將來可插拔...),使用slabs來管理內存。內存被分成大小不等的slabs chunks(先分成大小相等的slabs,然后每個slab被分成大小相等chunks,不同slab的chunk大小是不相等的)。chunk的大小 依次從一個最小數開始,按某個因子增長,直到達到最大的可能值。
如果最小值為400B,最大值是1MB,因子是1.20,各個slab的chunk的大小依次是:slab1 - 400B slab2 - 480B slab3 - 576B ...
slab中chunk越大,它和前面的slab之間的間隙就越大。因此,最大值越大,內存利用率越低。Memcached必須為每個slab預先分配內存,因此如果設置了較小的因子和較大的最大值,會需要更多的內存。
還有其他原因使得您不要這樣向memcached中存取很大的數據...不要嘗試把巨大的網頁放到mencached中。把這樣大的數據結構load和unpack到內存中需要花費很長的時間,從而導致您的網站性能反而不好。
如果您確實需要存儲大于1MB的數據,你可以修改slabs.c:POWER_BLOCK的值,然后重新編譯memcached;或者使用低效的malloc/free。其他的建議包括數據庫、MogileFS等。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">我可以在不同的memcached節點上使用大小不等的緩存空間嗎?這么做之后,memcached能夠更有效地使用內存嗎? Memcache 客戶端僅根據哈希算法來決定將某個key存儲在哪個節點上,而不考慮節點的內存大小。因此,您可以在不同的節點上使用大小不等的緩存。但是一般都是這樣做 的:擁有較多內存的節點上可以運行多個memcached實例,每個實例使用的內存跟其他節點上的實例相同。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">什么是二進制協議,我該關注嗎? 關于二進制最好的信息當然是二進制協議規范:http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol 。
二進制協議嘗試為端提供一個更有效的、可靠的協議,減少客戶端/服務器端因處理協議而產生的CPU時間。 根據Facebook的測試,解析ASCII協議是memcached中消耗CPU時間最多的環節。所以,我們為什么不改進ASCII協議呢? 在這個郵件列表的thread中可以找到一些舊的信息:http://lists.danga.com/pipermail/memcached/2007-July/004636.html 。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached的內存分配器是如何工作的?為什么不適用malloc/free???為何要使用slabs? 實 際上,這是一個編譯時選項。默認會使用內部的slab分配器。您確實確實應該使用內建的slab分配器。最早的時候,memcached只使用 malloc/free來管理內存。然而,這種方式不能與OS的內存管理以前很好地工作。反復地malloc/free造成了內存碎片,OS最終花費大量 的時間去查找連續的內存塊來滿足malloc的請求,而不是運行memcached進程。如果您不同意,當然可以使用malloc!只是不要在郵件列表中 抱怨啊:)
slab分配器就是為了解決這個問題而生的。內存被分配并劃分成chunks,一直被重復使用。因為內存被劃分成大小不等的 slabs,如果item的大小與被選擇存放它的slab不是很合適的話,就會浪費一些內存。Steven Grimm正在這方面已經做出了有效的改進。
郵件列表中有一些關于slab的改進(power of n 還是 power of 2)和權衡方案:http://lists.danga.com/pipermail/memcached/2006-May/002163.html http://lists.danga.com/pipermail/memcached/2007-March/003753.html 。
如果您想使用malloc/free,看看它們工作地怎么樣,您可以在構建過程中定義USE_SYSTEM_MALLOC。這個特性沒有經過很好的測試,所以太不可能得到開發者的支持。 更多信息:http://code.sixapart.com/svn/memcached/trunk/server/doc/memory_management.txt 。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached是原子的嗎? 當然!好吧,讓我們來明確一下: 所有的被發送到memcached的單個命令是完全原子的。如果您針對同一份數據同時發送了一個set命令和一個get命令,它們不會影響對方。它們將被串行化、先后執行。即使在多線程模式,所有的命令都是原子的,除非程序有bug:) 命令序列不是原子的。如果您通過get命令獲取了一個item,修改了它,然后想把它set回memcached,我們不保證這個item沒有被其他進程(process,未必是操作系統中的進程)操作過。在并發的情況下,您也可能覆寫了一個被其他進程set的item。
memcached 1.2.5以及更高版本,提供了gets和cas命令,它們可以解決上面的問題。如果您使用gets命令查詢某個key的item,memcached會 給您返回該item當前值的唯一標識。如果您覆寫了這個item并想把它寫回到memcached中,您可以通過cas命令把那個唯一標識一起發送給 memcached。如果該item存放在memcached中的唯一標識與您提供的一致,您的寫操作將會成功。如果另一個進程在這期間也修改了這個 item,那么該item存放在memcached中的唯一標識將會改變,您的寫操作就會失敗。
通常,基于memcached中item的值來修改item,是一件棘手的事情。除非您很清楚自己在做什么,否則請不要做這樣的事情。
SQL SERVER的排序規則平時使用不是很多,也許不少初學者還比較陌生,但有一個錯誤大家應是經常碰到: SQL SERVER數據庫,在跨庫多表連接查詢時,若兩數據庫默認字符集不同,系統就會返回這樣的錯誤: “無法解決 equal to 操作的排序規則沖突。” 一.錯誤分析: 這個錯誤是因為排序規則不一致造成的,我們做個測試,比如: 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 )
表建好后,執行連接查詢:
select * from #t1 A inner join #t2 B on A.name=B.name
這樣,錯誤就出現了:
服務器: 消息 446,級別 16,狀態 9,行 1 無法解決 equal to 操作的排序規則沖突。 要排除這個錯誤,最簡單方法是,表連接時指定它的排序規則,這樣錯誤就不再出現了。語句這樣寫:
select * from #t1 A inner join #t2 B on A.name=B.name collate Chinese_PRC_CI_AI_WS
二.排序規則簡介:
什么叫排序規則呢?MS是這樣描述的:"在 Microsoft SQL Server 2000 中,字符串的物理存儲由排序規則控制。排序規則指定表示每個字符的位模式以及存儲和比較字符所使用的規則。 在查詢分析器內執行下面語句,可以得到SQL SERVER支持的所有排序規則。
select * from ::fn_helpcollations()
排序規則名稱由兩部份構成,前半部份是指本排序規則所支持的字符集。 如: Chinese_PRC_CS_AI_WS 前半部份:指UNICODE字符集,Chinese_PRC_指針對大陸簡體字UNICODE的排序規則。 排序規則的后半部份即后綴 含義: _BIN 二進制排序 _CI(CS) 是否區分大小寫,CI不區分,CS區分 _AI(AS) 是否區分重音,AI不區分,AS區分 _KI(KS) 是否區分假名類型,KI不區分,KS區分 _WI(WS) 是否區分寬度 WI不區分,WS區分
區分大小寫:如果想讓比較將大寫字母和小寫字母視為不等,請選擇該選項。 區分重音:如果想讓比較將重音和非重音字母視為不等,請選擇該選項。如果選擇該選項,比較還將重音不同的字母視為不等。 區分假名:如果想讓比較將片假名和平假名日語音節視為不等,請選擇該選項。 區分寬度:如果想讓比較將半角字符和全角字符視為不等,請選擇該選項 三.排序規則的應用: SQL SERVER提供了大量的WINDOWS和SQLSERVER專用的排序規則,但它的應用往往被開發人員所忽略。其實它在實踐中大有用處。
例1:讓表NAME列的內容按拼音排序:
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 /*結果: id name ----------- -------------------- 4 阿 2 國 3 人 1 中 */
例2:讓表NAME列的內容按姓氏筆劃排序:
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 /*結果: id name ----------- -------------------- 4 一 2 乙 3 二 5 十 1 三 */
四.在實踐中排序規則應用的擴展 SQL SERVER漢字排序規則可以按拼音、筆劃等排序,那么我們如何利用這種功能來處理漢字的一些難題呢?我現在舉個例子:
用排序規則的特性計算漢字筆劃
要計算漢字筆劃,我們得先做準備工作,我們知道,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
結果: code CNWord ----------- ------ 19968 一 20008 丨 20022 丶 20031 丿 20032 乀 20033 乁 20057 乙 20058 乚 20059 乛 20101 亅 19969 丁 ..........
從上面的結果,我們可以清楚的看到,一筆的漢字,code是從19968到20101,從小到大排,但到了二筆漢字的第一個字“丁”,CODE為 19969,就不按順序而重新開始了。有了這結果,我們就可以輕松的用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個漢字,每個漢字都是每種筆劃數按Chinese_PRC_Stroke_CS_AS_KS_WS排序規則排序后的 最后一個漢字:
亅阝馬風龍齊龜齒鴆齔龕龂齠齦齪龍龠龎龐龑龡龢龝齹龣龥齈龞麷鸞麣龖龗齾齉龘
上面可以看出:“亅”是所有一筆漢字排序后的最后一個字,“阝”是所有二筆漢字排序后的最后一個字......等等。 但同時也發現,從第33個漢字“龗(33筆)”后面的筆劃有些亂,不正確。但沒關系,比“龗”筆劃多的只有四個漢字,我們手工加上:齾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
到此為止,我們可以得到結果了,比如我們想得到漢字“國”的筆劃:
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 (結果:漢字“國”筆劃數為8)
上面所有準備過程,只是為了寫下面這個函數,這個函數撇開上面建的所有臨時表和固定表,為了通用和代碼轉移方便,把表tab_hzbh的內容寫在語句內,然后計算用戶輸入一串漢字的總筆劃:
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) --如果非漢字,筆劃當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'風' 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
--函數調用實例: select dbo.fun_getbh('中華人民共和國'),dbo.fun_getbh('中華人民共和國') 執行結果:筆劃總數分別為39和46,簡繁體都行。
當然,你也可以把上面“UNION ALL”內的漢字和筆劃改存在固定表內,在漢字列建CLUSTERED INDEX,列排序規則設定為: Chinese_PRC_Stroke_CS_AS_KS_WS 這樣速度更快。如果你用的是BIG5碼的操作系統,你得另外生成漢字,方法一樣。但有一點要記?。哼@些漢字是通過SQL語句SELECT出來的,不是手工輸入的,更不是查字典得來的,因為新華字典畢竟不同于UNICODE字符集,查字典的結果會不正確。
用排序規則的特性得到漢字拼音首字母
用得到筆劃總數相同的方法,我們也可以寫出求漢字拼音首字母的函數。如下:
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
--函數調用實例: select dbo.fun_getPY('中華人民共和國'),dbo.fun_getPY('中華人民共和國') 結果都為:ZHRMGHG
你若有興趣,也可用相同的方法,擴展為得到漢字全拼的函數,甚至還可以得到全拼的讀音聲調,不過全拼分類大多了。得到全拼最好是用對照表,兩萬多漢字搜索速度很快,用對照表還可以充分利用表的索引。 排序規則還有很多其它的巧妙用法。歡迎大家共同探討。
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
本文來自和你在一起的博客,原文標題為《JVM調優總結(一)-- 一些概念》。 數據類型 Java虛擬機中,數據類型可以分為兩類:基本類型和引用類型?;绢愋偷淖兞勘4嬖贾担矗核淼闹稻褪菙抵当旧?;而引用類型的變量保存引用值。“引用值”代表了某個對象的引用,而不是對象本身,對象本身存放在這個引用值所表示的地址的位置。 基本類型包括:byte, short, int, long, char, float, double, Boolean, returnAddress 引用類型包括:類類型,接口類型和數組。 堆與棧 堆和棧是程序運行的關鍵,很有必要把他們的關系說清楚。 棧是運行時的單位,而堆是存儲的單位。 棧解決程序的運行問題,即程序如何執行,或者說如何處理數據;堆解決的是數據存儲的問題,即數據怎么放、放在哪兒。 在Java中一個線程就會相應有一個線程棧與之對應,這點很容易理解,因為不同的線程執行邏輯有所不同,因此需要一個獨立的線程棧。而堆則是所有線 程共享的。棧因為是運行單位,因此里面存儲的信息都是跟當前線程(或程序)相關信息的。包括局部變量、程序運行狀態、方法返回值等等;而堆只負責存儲對象 信息。 為什么要把堆和棧區分出來呢?棧中不是也可以存儲數據嗎? 第一,從軟件設計的角度看,棧代表了處理邏輯,而堆代表了數據。這樣分開,使得處理邏輯更為清晰。分而治之的思想。這種隔離、模塊化的思想在軟件設計的方方面面都有體現。 第二,堆與棧的分離,使得堆中的內容可以被多個棧共享(也可以理解為多個線程訪問同一個對象)。這種共享的收益是很多的。一方面這種共享提供了一種有效的數據交互方式(如:共享內存),另一方面,堆中的共享常量和緩存可以被所有棧訪問,節省了空間。 第三,棧因為運行時的需要,比如保存系統運行的上下文,需要進行地址段的劃分。由于棧只能向上增長,因此就會限制住棧存儲內容的能力。而堆不同,堆中的對象是可以根據需要動態增長的,因此棧和堆的拆分,使得動態增長成為可能,相應棧中只需記錄堆中的一個地址即可。 第四,面向對象就是堆和棧的完美結合。其實,面向對象方式的程序與以前結構化的程序在執行上沒有任何區別。但是,面向對象的引入,使得對待問題的思 考方式發生了改變,而更接近于自然方式的思考。當我們把對象拆開,你會發現,對象的屬性其實就是數據,存放在堆中;而對象的行為(方法),就是運行邏輯, 放在棧中。我們在編寫對象的時候,其實即編寫了數據結構,也編寫的處理數據的邏輯。不得不承認,面向對象的設計,確實很美。 在Java中,Main函數就是棧的起始點,也是程序的起始點。 程序要運行總是有一個起點的。同C語言一樣,java中的Main就是那個起點。無論什么java程序,找到main就找到了程序執行的入口:) 堆中存什么?棧中存什么? 堆中存的是對象。棧中存的是基本數據類型和堆中對象的引用。一個對象的大小是不可估計的,或者說是可以動態變化的,但是在棧中,一個對象只對應了一個4btye的引用(堆棧分離的好處:))。 為什么不把基本類型放堆中呢?因為其占用的空間一般是1~8個字節——需要空間比較少,而且因為是基本類型,所以不會出現動態增長的情況——長度固 定,因此棧中存儲就夠了,如果把他存在堆中是沒有什么意義的(還會浪費空間,后面說明)。可以這么說,基本類型和對象的引用都是存放在棧中,而且都是幾個 字節的一個數,因此在程序運行時,他們的處理方式是統一的。但是基本類型、對象引用和對象本身就有所區別了,因為一個是棧中的數據一個是堆中的數據。最常 見的一個問題就是,Java中參數傳遞時的問題。 Java中的參數傳遞時傳值呢?還是傳引用? 要說明這個問題,先要明確兩點: 1. 不要試圖與C進行類比,Java中沒有指針的概念 2. 程序運行永遠都是在棧中進行的,因而參數傳遞時,只存在傳遞基本類型和對象引用的問題。不會直接傳對象本身。 明確以上兩點后。Java在方法調用傳遞參數時,因為沒有指針,所以它都是進行傳值調用(這點可以參考C的傳值調用)。因此,很多書里面都說Java是進行傳值調用,這點沒有問題,而且也簡化的C中復雜性。 但是傳引用的錯覺是如何造成的呢?在運行棧中,基本類型和引用的處理是一樣的,都是傳值,所以,如果是傳引用的方法調用,也同時可以理解為“傳引用 值”的傳值調用,即引用的處理跟基本類型是完全一樣的。但是當進入被調用方法時,被傳遞的這個引用的值,被程序解釋(或者查找)到堆中的對象,這個時候才 對應到真正的對象。如果此時進行修改,修改的是引用對應的對象,而不是引用本身,即:修改的是堆中的數據。所以這個修改是可以保持的了。 對象,從某種意義上說,是由基本類型組成的??梢园岩粋€對象看作為一棵樹,對象的屬性如果還是對象,則還是一顆樹(即非葉子節點),基本類型則為樹 的葉子節點。程序參數傳遞時,被傳遞的值本身都是不能進行修改的,但是,如果這個值是一個非葉子節點(即一個對象引用),則可以修改這個節點下面的所有內 容。
堆和棧中,棧是程序運行最根本的東西。程序運行可以沒有堆,但是不能沒有棧。而堆是為棧進行數據存儲服務,說白了堆就是一塊共享的內存。不過,正是因為堆和棧的分離的思想,才使得Java的垃圾回收成為可能。 Java中,棧的大小通過-Xss來設置,當棧中存儲數據比較多時,需要適當調大這個值,否則會出現java.lang.StackOverflowError異常。常見的出現這個異常的是無法返回的遞歸,因為此時棧中保存的信息都是方法返回的記錄點。
二 JAVA垃圾收集器 2.1 垃圾收集簡史 垃圾收集提供了內存管理的機制,使得應用程序不需要在關注內存如何釋放,內存用完后,垃圾收集會進行收集,這樣就減輕了因為人為的管理內存而造成的 錯誤,比如在C++語言里,出現內存泄露時很常見的。Java語言是目前使用最多的依賴于垃圾收集器的語言,但是垃圾收集器策略從20世紀60年代就已經 流行起來了,比如Smalltalk,Eiffel等編程語言也集成了垃圾收集器的機制。 2.2 常見的垃圾收集策略  所有的垃圾收集算法都面臨同一個問題,那就是找出應用程序不可到達的內存塊,將其釋放,這里面得不可到達主要是指應用程序已經沒有內存塊的引用了, 而在JAVA中,某個對象對應用程序是可到達的是指:這個對象被根(根主要是指類的靜態變量,或者活躍在所有線程棧的對象的引用)引用或者對象被另一個可 到達的對象引用。 2.2.1 Reference Counting(引用計數) 引用計數是最簡單直接的一種方式,這種方式在每一個對象中增加一個引用的計數,這個計數代表當前程序有多少個引用引用了此對象,如果此對象的引用計數變為0,那么此對象就可以作為垃圾收集器的目標對象來收集。 優點: 簡單,直接,不需要暫停整個應用 缺點: 1.需要編譯器的配合,編譯器要生成特殊的指令來進行引用計數的操作,比如每次將對象賦值給新的引用,或者者對象的引用超出了作用域等。 2.不能處理循環引用的問題 2.2.2 跟蹤收集器 跟蹤收集器首先要暫停整個應用程序,然后開始從根對象掃描整個堆,判斷掃描的對象是否有對象引用,這里面有三個問題需要搞清楚:  1.如果每次掃描整個堆,那么勢必讓GC的時間變長,從而影響了應用本身的執行。因此在JVM里面采用了分代收集,在新生代收集的時候minor gc只需要掃描新生代,而不需要掃描老生代。 2.JVM采用了分代收集以后,minor gc只掃描新生代,但是minor gc怎么判斷是否有老生代的對象引用了新生代的對象,JVM采用了卡片標記的策略,卡片標記將老生代分成了一塊一塊的,劃分以后的每一個塊就叫做一個卡 片,JVM采用卡表維護了每一個塊的狀態,當JAVA程序運行的時候,如果發現老生代對象引用或者釋放了新生代對象的引用,那么就JVM就將卡表的狀態設 置為臟狀態,這樣每次minor gc的時候就會只掃描被標記為臟狀態的卡片,而不需要掃描整個堆。具體如下圖: 3.GC在收集一個對象的時候會判斷是否有引用指向對象,在JAVA中的引用主要有四種:Strong reference,Soft reference,Weak reference,Phantom reference. ◆ Strong Reference 強引用是JAVA中默認采用的一種方式,我們平時創建的引用都屬于強引用。如果一個對象沒有強引用,那么對象就會被回收。 - public void testStrongReference(){
- Object referent = new Object();
- Object strongReference = referent;
- referent = null;
- System.gc();
- assertNotNull(strongReference);
- }
◆ Soft Reference 軟引用的對象在GC的時候不會被回收,只有當內存不夠用的時候才會真正的回收,因此軟引用適合緩存的場合,這樣使得緩存中的對象可以盡量的再內存中待長久一點。 - Public void testSoftReference(){
- String str = "test";
- SoftReference<String> softreference = new SoftReference<String>(str);
- str=null;
- System.gc();
- assertNotNull(softreference.get());
- }
◆ Weak reference 弱引用有利于對象更快的被回收,假如一個對象沒有強引用只有弱引用,那么在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(標記-清除收集器) 標記清除收集器最早由Lisp的發明人于1960年提出,標記清除收集器停止所有的工作,從根掃描每個活躍的對象,然后標記掃描過的對象,標記完成以后,清除那些沒有被標記的對象。 優點: 1 解決循環引用的問題 2 不需要編譯器的配合,從而就不執行額外的指令 缺點: 1.每個活躍的對象都要進行掃描,收集暫停的時間比較長。 2.2.2.2 Copying Collector(復制收集器)復制收集器將內存分為兩塊一樣大小空間,某一個時刻,只有一個空間處于活躍的狀態,當活躍的空間滿的時候,GC就會將活 躍的對象復制到未使用的空間中去,原來不活躍的空間就變為了活躍的空間。復制收集器具體過程可以參考下圖:  優點: 1 只掃描可以到達的對象,不需要掃描所有的對象,從而減少了應用暫停的時間 缺點: 1.需要額外的空間消耗,某一個時刻,總是有一塊內存處于未使用狀態 2.復制對象需要一定的開銷 2.2.2.3 Mark-Compact Collector(標記-整理收集器)標記整理收集器汲取了標記清除和復制收集器的優點,它分兩個階段執行,在第一個階段,首先掃描所有活躍的對象,并 標記所有活躍的對象,第二個階段首先清除未標記的對象,然后將活躍的的對象復制到堆得底部。標記整理收集器的過程示意圖請參考下圖:Mark- compact策略極大的減少了內存碎片,并且不需要像Copy Collector一樣需要兩倍的空間。
垃圾收集器策略從20世紀60年代就已經流行起來了,相比于其他編程語言,Java語言是目前使用最多的依賴于垃圾收集器的語言。 AD: JVM內存模型是Java的核心技術之一,之前51CTO曾為大家介紹過JVM分代垃圾回收策略的基礎概念,現在很多編程語言都引入了類似Java JVM的內存模型和垃圾收集器的機制,下面我們將主要針對Java中的JVM內存模型及垃圾收集的具體策略進行綜合的分析。 一 JVM內存模型 1.1 Java棧 Java棧是與每一個線程關聯的,JVM在創建每一個線程的時候,會分配一定的??臻g給線程。它主要用來存儲線程執行過程中的局部變量,方法的返回 值,以及方法調用上下文。??臻g隨著線程的終止而釋放。StackOverflowError:如果在線程執行的過程中,棧空間不夠用,那么JVM就會拋 出此異常,這種情況一般是死遞歸造成的。 1.2 堆 Java中堆是由所有的線程共享的一塊內存區域,堆用來保存各種JAVA對象,比如數組,線程對象等。 1.2.1 Generation JVM堆一般又可以分為以下三部分:  ◆ Perm Perm代主要保存class,method,filed對象,這部門的空間一般不會溢出,除非一次性加載了很多的類,不過在涉及到熱部署的應用服 務器的時候,有時候會遇到java.lang.OutOfMemoryError : PermGen space 的錯誤,造成這個錯誤的很大原因就有可能是每次都重新部署,但是重新部署后,類的class沒有被卸載掉,這樣就造成了大量的class對象保存在了 perm中,這種情況下,一般重新啟動應用服務器可以解決問題。 ◆ Tenured Tenured區主要保存生命周期長的對象,一般是一些老的對象,當一些對象在Young復制轉移一定的次數以后,對象就會被轉移到Tenured區,一般如果系統中用了application級別的緩存,緩存中的對象往往會被轉移到這一區間。 ◆ Young Young區被劃分為三部分,Eden區和兩個大小嚴格相同的Survivor區,其中Survivor區間中,某一時刻只有其中一個是被使用的, 另外一個留做垃圾收集時復制對象用,在Young區間變滿的時候,minor GC就會將存活的對象移到空閑的Survivor區間中,根據JVM的策略,在經過幾次垃圾收集后,任然存活于Survivor的對象將被移動到 Tenured區間。 1.2.2 Sizing the Generations JVM提供了相應的參數來對內存大小進行配置。正如上面描述,JVM中堆被分為了3個大的區間,同時JVM也提供了一些選項對Young,Tenured的大小進行控制。  ◆ Total Heap -Xms :指定了JVM初始啟動以后初始化內存 -Xmx:指定JVM堆得最大內存,在JVM啟動以后,會分配-Xmx參數指定大小的內存給JVM,但是不一定全部使用,JVM會根據-Xms參數來調節真正用于JVM的內存 -Xmx -Xms之差就是三個Virtual空間的大小 ◆ Young Generation -XX:NewRatio=8意味著tenured 和 young的比值8:1,這樣eden+2*survivor=1/9 堆內存 -XX:SurvivorRatio=32意味著eden和一個survivor的比值是32:1,這樣一個Survivor就占Young區的1/34. -Xmn 參數設置了年輕代的大小 ◆ Perm Generation -XX:PermSize=16M -XX:MaxPermSize=64M Thread Stack -XX:Xss=128K 1.3 堆棧分離的好處 呵呵,其它的先不說了,就來說說面向對象的設計吧,當然除了面向對象的設計帶來的維護性,復用性和擴展性方面的好處外,我們看看面向對象如何巧妙的 利用了堆棧分離。如果從JAVA內存模型的角度去理解面向對象的設計,我們就會發現對象它完美的表示了堆和棧,對象的數據放在堆中,而我們編寫的那些方法 一般都是運行在棧中,因此面向對象的設計是一種非常完美的設計方式,它完美的統一了數據存儲和運行。
轉自: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提供了更好的擴充性,其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后主要加強了兩項功能:
(1) 元素不依照順序設定
(2) 更強大的驗證機制
主要體現在:
a.檢查元素的值是否為合法的值
b.檢查元素的值是否為合法的文字字符或者數字字符
c.檢查Servlet,Filter,EJB-ref等等元素的名稱是否唯一
2.新增Filter四種設定:REQUEST、FORWARD、INCLUDE和ERROR。
3.新增Request Listener、Event和Request Attribute Listener、Enent。
4.取消SingleThreadModel接口。當Servlet實現SingleThreadModel接口時,它能確保同時間內,只能有一個thread執行此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月發布Servlet 2.5
Servlet2.5一些變化的介紹:
1) 基于最新的J2SE 5.0開發的。
2) 支持annotations 。
3) web.xml中的幾處配置更加方便。
4) 去除了少數的限制。
5) 優化了一些實例
servlet的各個版本對監聽器的變化有:
(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數據庫的比較 閱讀全文
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.
當你建立好一個WEB服務后,通常有兩個類型的緩存需要配置:
- 設置網站有更新的時候html資源馬上過期,以便正在瀏覽的用戶可以很快地得到更新.
- 設置所有其它資源(例如圖片,CSS,javascript腳本)在一定時間后過期.
這個緩存方案涵蓋Two Simple Rules for HTTP Caching文章中提到關于如何處理更新的一些思想.
現在HttpWatch 6.0支持Firefox了,我們想探討一下Firefox在處理緩存上與IE有些什么不同.設置較長過期時間的使用方式(上面第二條)仍可以直接用于Firefox,但配置1在兩者之間還是存在細微差別的.
在之前的文章 中,我們把第一條劃分為:
- 某些時候動態HTML頁面需要即時從服務器更新以備隨時顯示-甚至是使用后退按鈕的時候.例如,顯示銀行帳號的狀態或在線訂單.
- 靜態HTML頁面,比如聯系,FAQs或者站點地圖等頁面,如果它們設置了
Last-Modified響應頭 ,允許瀏覽器在需要的時候重新校驗,就可以利用到緩存.
一:火狐機制
本文剩下部分探討了Firefox中影響HTML頁面緩存的兩個重要不同點.
1. 使用no-cache防止Firefox緩存無效
你可以簡單地設置如下的響應頭預防IE緩存任何東西:
Cache-Control: no-cache
使用了這個響應頭的頁面不會保存在緩存里,IE總會重新從服務器加載;即使你使用后退按鈕.下面這個例子使用HttpWatch監聽一個網上商店,當我們在提交訂單表單后點擊后退按鈕,結果如下圖:

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

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

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

1:每次都會到服務器檢查最后一次修改時間是否變化 無變化返回304
2:IE啟動時檢查
3:自動檢查
3.1:根據IE的默認在瀏覽器不關閉時,再次訪問同一鏈接都讀本地cache
3.2:IE重啟時候會根據服務器設置的expire值去判斷是否去服務器請求判斷,無變化返回304
4:第一次下載后從不檢查
最好的機制是 3 。根據服務器的設定機制走,會提高訪問速度。
提高訪問速度,最好的辦法就是減少TCP通信。
-------------------------
三:服務器的設置
參考: http://hi.baidu.com/%C8%FD%BE%D6%CE%AA%B6%FE/blog/item/30dae1325363ed92a8018e5c.html
-------------------------
四:服務端設置和客戶端的關聯
服務器的設置告知瀏覽器怎么做緩存,緩存時間是多長。客戶端同時也可以是CDN。
-------------------------
五:工具類分析
IE httpwatch ,FF httpfox YSlow firebug
在Java5.0中對于RMI的使用發生了一些改變, 使得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異常
二. 定義遠程類實現接口
與以前相比, 也沒有發生變化
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. 遠程對象必須實現第一步定義的接口, 在這里是Payment接口.
2. 遠程對象中可以定義接口中不包括的方法, 比如: format, 但是這些方法不能遠程使用.
3. 實現遠程方法時, 可以不拋出java.rmi.RemoteException異常
三. 定義遠程服務
此處與以前相比, 發生了一些變化, 需要注意.
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();
}
}
}
而傳統的做法是這樣的:
public class Server extends java.rmi.server.UnicastRemoteObject implements aRemoteInterface{
public Server(int port) {
super(port);
}
....
Naming.bind(uniqueName, this);
....
}
請自行進行比較.
四. 定義客戶端類
相應地, 這部分也有了變化.
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服務的方法與以前是不同的
五. 測試使用
現在我們有了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的新特性, 現在在不需要在啟動服務之前生成Stub了, stub現在自動生成.
而在Java5.0以前是需要用rmic手工生成的.
4. start rmiregistry, 出現一個窗口, 不要關閉.
5. start java Server, 出現一個窗口, 顯示 Mortgage server is ready to listen...
6. 啟動客戶端java Client, 如果出現
The principal is $150000
The annual interest rate is 6.0%
The term is 15 years
Your monthly payment is $1265.79
那就大功告成了.
注意點: 安全策略機制進行了弱化, 不再需要指定策略文件 授這個權,那個權的了.
我們都知道,IP是由四段數字組成,在此,我們先來了解一下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默認分配的子網掩碼每段只有255或0
A類的默認子網掩碼 255.0.0.0 一個子網最多可以容納1677萬多臺電腦
B類的默認子網掩碼 255.255.0.0 一個子網最多可以容納6萬臺電腦
C類的默認子網掩碼 255.255.255.0 一個子網最多可以容納254臺電腦
我以前認為,要想把一些電腦搞在同一網段,只要IP的前三段一樣就可以了,今天,我才知道我錯了。如果照我這說的話,一個子網就只能容納254臺電腦?真是有點笑話。我們來說詳細看看吧。
要想在同一網段,只要網絡標識相同就可以了,那要怎么看網絡標識呢?首先要做的是把每段的IP轉換為二進制。(有人說,我不會轉換耶,沒關系,我們用
Windows自帶計算器就行。打開計算器,點查看>科學型,輸入十進制的數字,再點一下“二進制”這個單選點,就可以切換至二進制了。)
把子網掩碼切換至二進制,我們會發現,所有的子網掩碼是由一串[red]連續[/red]的1和一串[red]連續[/red]的0組成的(一共4段,每段8位,一共32位數)。
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三類默認子網掩碼的二進制形式,其實,還有好多種子網掩碼,只要是一串連續的1和一串連續的0就可以了(每段都是8位)。如
11111111.11111111.11111000.00000000,這也是一段合法的子網掩碼。子網掩碼決定的是一個子網的計算機數目,計算機公
式是2的m次方,其中,我們可以把m看到是后面的多少顆0。如255.255.255.0轉換成二進制,那就是
11111111.11111111.11111111.00000000,后面有8顆0,那m就是8,255.255.255.0這個子網掩碼可以容納
2的8次方(臺)電腦,也就是256臺,但是有兩個IP是不能用的,那就是最后一段不能為0和255,減去這兩臺,就是254臺。我們再來做一個。
255.255.248.0這個子網掩碼可以最多容納多少臺電腦?
計算方法:
把將其轉換為二進制的四段數字(每段要是8位,如果是0,可以寫成8個0,也就是00000000)
11111111.1111111.11111000.00000000
然后,數數后面有幾顆0,一共是有11顆,那就是2的11次方,等于2048,這個子網掩碼最多可以容納2048臺電腦。
一個子網最多可以容納多少臺電腦你會算了吧,下面我們來個逆向算法的題。
一個公司有530臺電腦,組成一個對等局域網,子網掩碼設多少最合適?
首先,無疑,530臺電腦用B類IP最合適(A類不用說了,太多,C類又不夠,肯定是B類),但是B類默認的子網掩碼是255.255.0.0,可以容納6萬臺電腦,顯然不太合適,那子網掩碼設多少合適呢?我們先來列個公式。
2的m次方=560
首先,我們確定2一定是大于8次方的,因為我們知道2的8次方是256,也就是C類IP的最大容納電腦的數目,我們從9次方一個一個試2的9次方是
512,不到560,2的10次方是1024,看來2的10次方最合適了。子網掩碼一共由32位組成,已確定后面10位是0了,那前面的22位就是1,最
合適的子網掩碼就是:11111111.11111111.11111100.00000000,轉換成10進制,那就是255.255.252.0。
分配和計算子網掩碼你會了吧,下面,我們來看看IP地址的網段。
相信好多人都和偶一樣,認為IP只要前三段相同,就是在同一網段了,其實,不是這樣的,同樣,我樣把IP的每一段轉換為一個二進制數,這里就拿IP:192.168.0.1,子網掩碼:255.255.255.0做實驗吧。
192.168.0.1
11000000.10101000.00000000.00000001
(這里說明一下,和子網掩碼一樣,每段8位,不足8位的,前面加0補齊。)
IP 11000000.10101000.00000000.00000001
子網掩碼 11111111.11111111.11111111.00000000
在這里,向大家說一下到底怎么樣才算同一網段。
要想在同一網段,必需做到網絡標識相同,那網絡標識怎么算呢?各類IP的網絡標識算法都是不一樣的。A類的,只算第一段。B類,只算第一、二段。C類,算第一、二、三段。
算法只要把IP和子網掩碼的每位數AND就可以了。
AND方法:0和1=0 0和0=0 1和1=1
如:And 192.168.0.1,255.255.255.0,先轉換為二進制,然后AND每一位
IP 11000000.10101000.00000000.00000001
子網掩碼 11111111.11111111.11111111.00000000
得出AND結果 11000000.10101000.00000000.00000000
轉換為十進制192.168.0.0,這就是網絡標識,
再將子網掩碼反取,也就是00000000.00000000.00000000.11111111,與IP AND
得出結果00000000.00000000.00000000.00000001,轉換為10進制,即0.0.0.1,
這0.0.0.1就是主機標識。要想在同一網段,必需做到網絡標識一樣。
我們再來看看這個改為默認子網掩碼的B類IP
如IP:188.188.0.111,188.188.5.222,子網掩碼都設為255.255.254.0,在同一網段嗎?
先將這些轉換成二進制
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
網絡標識不一樣,即不在同一網段。
判斷是不是在同一網段,你會了吧,下面,我們來點實際的。
一個公司有530臺電腦,組成一個對等局域網,子網掩碼和IP設多少最合適?
子網掩碼不說了,前面算出結果來了11111111.11111111.11111100.00000000,也就是255.255.252.0
我們現在要確定的是IP如何分配,首先,選一個B類IP段,這里就選188.188.x.x吧
這樣,IP的前兩段確定的,關鍵是要確定第三段,只要網絡標識相同就可以了。我們先來確定網絡號。(我們把子網掩碼中的1和IP中的?對就起來,0和*對應起來,如下:)
255.255.252.0 11111111.11111111.11111100.00000000
188.188.x.x 10111100.10111100.??????**.********
網絡標識 10111100.10111100.??????00.00000000
由此可知,?處隨便填(只能用0和1填,不一定全是0和1),我們就用全填0吧,*處隨便,這樣呢,我們的IP就是
10111100.10111100.000000**.********,一共有530臺電腦,IP的最后一段1~254可以分給254臺計算
機,530/254=2.086,采用進1法,得整數3,這樣,我們確定了IP的第三段要分成三個不同的數字,也就是說,把000000**中的**填三
次數字,只能填1和0,而且每次的數字都不一樣,至于填什么,就隨我們便了,如00000001,00000010,00000011,轉換成二進制,分
別是1,2,3,這樣,第三段也確定了,這樣,就可以把IP分成188.188.1.y,188.188.2.y,188.188.3.y,y處隨便填,
只要在1~254范圍之內,并且這530臺電腦每臺和每臺的IP不一樣,就可以了。
有人也許會說,既然算法這么麻煩,干脆用A類IP和A類默認子網掩碼得了,偶要告訴你的是,由于A類IP和A類默認子網掩碼的主機數目過大,這樣做無疑是大海撈針,如果同時局域網訪問量過頻繁、過大,會影響效率的,所以,最好設置符合自己的IP和子網掩碼^_^
摘要: Eclipse 遠程DEBUG 方法 閱讀全文
|
|
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
25 | 26 | 27 | 28 | 29 | 30 | 31 | 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 | 1 | 2 | 3 | 4 | 5 |
|
常用鏈接
留言簿(12)
隨筆分類
隨筆檔案
文章分類
文章檔案
相冊
收藏夾
好友的blog
技術網站
搜索
積分與排名
最新評論

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