2008年6月12日
http://www.blogjava.net/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 欧洲,等。这些切分分别处于不同的计算机上,且每个切分将存储所有相关数据,如客户喜好或订购历史。 切分的好处(如分区一样)在于它可以压缩大型数据:单独的数据表在每个切分中相对较小,这样就可以支持更快速的读写速度,从而提高性能。切分 还可以改善可靠性,因为即便一个切分意外失效,其他切分仍然可以服务数据。而且因为切分是在应用程序层面进行的,您可以对不支持常规分区的数据库进行切分 处理。资金成本较低同样也是一个潜在优势。 切分和策略 像很多其他技术一样,进行切分时也需要作出部分妥协。因为切分不是一项本地数据库技术 — 也就是说,必须在应用程序中实现 —在开始切分之前需要制定出您的切分策略。进行切分时主键和跨切分查询都扮演重要角色,主要通过定义您不可以做什么实现。 主键 切分利用多个数据库,其中所有数据库都独立起作用,不干涉其他切分。因此,如果您依赖于数据库序列(如自动主键生成),很有可能在一个数据库集中将出现同 一个主键。可以跨分布式数据库协调序列,但是这样会增加系统的复杂程度。避免相同主键最安全的方法就是让应用程序(应用程序将管理切分系统)生成主键。 跨切分查询 大部分切分实现(包括 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 工作所需的三个接口后,我们就可以对切分示例应用程序的第二步进行实现了。现在应该启动 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虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表的值就是数值本身;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。 基本类型包括: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在创建每一个线程的时候,会分配一定的栈空间给线程。它主要用来存储线程执行过程中的局部变量,方法的返回 值,以及方法调用上下文。栈空间随着线程的终止而释放。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"
"http://java.sun.com/j2ee/dtds/web-app_2.3.dtd">
<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="http://java.sun.com/xml/ns/j2ee"
xmlns:workflow="http://www.workflow.com"
xmins:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
.........
</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 方法 阅读全文
部分内容参考:http://www.linuxdiyf.com/viewarticle.php?id=78024
在/etc/init.d下面新建一个文件dbora,内容:
#!/bin/sh
su - $ORA_OWNER -c "lsnrctl start"
su - $ORA_OWNER -c "sqlplus /nolog @$ORACLE_HOME/dbstart.sql"
$ORACLE_HOME/dbstart.sql的内容:
conn / as sysdba
startup
exit
将dbora改成可执行
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用户需要正确设置环境变量上面的自动启动才会生效,具体如下
su - oracle
vi .profile (设置oracle的环境变量文件,这个文件在oracle用户的home目录下)
加入下面变量设置
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
另外如果环境变量设置了NLS_LAN变量注意不要设错,否则oracle会报:ora-12705错误。
用xmanager远程在solaris上安装oracle10g
1.在oracle下载合适的oracle版本
2.用root登陆
3.设置oracle账户信息
1)groupadd dba
2)useradd -d dba -d /opt/oracle -m -s /bin/sh oracle
3)修改/etc/group文件最后一行:dba::100:oracle
4)设置/opt/oracle目录权限 chmod -R 777 oracle
5)vi /etc/system 在最后一行加入(设置用户变量)
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 设置oracle用户密码
必需要先重启才能安装oracle(sytem设置才能生效)
注意安装oracle时需要用oracle用户安装
3.设置display环境变量
csh
setenv DISPLAY xx.xx.xx.xx:1.0
xx.xx.xx.xx是xmanager所在的ip地址
4.su - oracle 运行oracle的./runInstaller开始安装
5.手工设置oracle环境变量(我装的时候自动设置没有生效)
设置oracle环境变量,最好可以设置到.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的进程
ps -ef | grep oracle
7.solaris 命令部分
设置环境变量在/.profile中
#vi .profile
LANG=zh_CN.GBK;export LANG
(必须向上面这样设置才能生效)
重启solais
init 6
查看内存
/usr/sbin/swap -s
看进程的全路径
/usr/ucb/ps -auxww | more | grep java
websphere4 配置过程中遇到的问题
1.admin.config 中配置oracle driver 的classpath
-Dws.ext.dirs=
连接不上oracle数据库,报java.sql.SQLException: ORA-01453: SET TRANSACTION 必须是事务处理的第一个语句错误
[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 实现中抛出。详细信息请参照:
上下文实现:com.ibm.ejs.ns.jndi.CNContextImpl
上下文方法:lookup
上下文名:domainRoots/UnspecifiedDomainName/legacyRoot
目标名:ejsadmin/homes/NodeHome
其它数据:
异常堆栈跟踪: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配置应用和datasource
1)资源配置jdbc 供应商
2)企业应用程序-》右键安装企业应用=》选择一个空的ear文件
webspherejvm设置
adminclient->节点->avgg1->应用服务器->Default Server
-Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Dclient.encoding.override=UTF-8 -Duser.language=zh -Duser.region=CN
3.解析get/post参数时报错:
[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文件
负责获取编码
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
|
|
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
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
技术网站
搜索
积分与排名
最新评论
阅读排行榜
评论排行榜
|
|