昊天

下面是《memcached全面剖析》的第三部分。

前几次的文章在这里:

memcached是缓存,所以数据不会永久保存在服务器上,这是向系统中引入memcached的前提。本次介绍memcached的数据删除机制,以及memcached的最新发展方向——二进制协议(Binary Protocol)和外部引擎支持。

memcached在数据删除方面有效利用资源

数据不会真正从memcached中消失

上次介绍过, memcached不会释放已分配的内存。记录超时后,客户端就无法再看见该记录(invisible,透明),其存储空间即可重复使用。

Lazy Expiration

memcached内部不会监视记录是否过期,而是在get时查看记录的时间戳,检查记录是否过期。这种技术被称为lazy(惰性)expiration。因此,memcached不会在过期监视上耗费CPU时间。

LRU:从缓存中有效删除数据的原理

memcached会优先使用已超时的记录的空间,但即使如此,也会发生追加新记录时空间不足的情况,此时就要使用名为 Least Recently Used(LRU)机制来分配空间。顾名思义,这是删除“最近最少使用”的记录的机制。因此,当memcached的内存空间不足时(无法从slab class 获取到新的空间时),就从最近未被使用的记录中搜索,并将其空间分配给新的记录。从缓存的实用角度来看,该模型十分理想。

不过,有些情况下LRU机制反倒会造成麻烦。memcached启动时通过“-M”参数可以禁止LRU,如下所示:

$ memcached -M -m 1024

启动时必须注意的是,小写的“-m”选项是用来指定最大内存大小的。不指定具体数值则使用默认值64MB。

指定“-M”参数启动后,内存用尽时memcached会返回错误。话说回来,memcached毕竟不是存储器,而是缓存,所以推荐使用LRU。

memcached的最新发展方向

memcached的roadmap上有两个大的目标。一个是二进制协议的策划和实现,另一个是外部引擎的加载功能。

关于二进制协议

使用二进制协议的理由是它不需要文本协议的解析处理,使得原本高速的memcached的性能更上一层楼,还能减少文本协议的漏洞。目前已大部分实现,开发用的代码库中已包含了该功能。 memcached的下载页面上有代码库的链接。

二进制协议的格式

协议的包为24字节的帧,其后面是键和无结构数据(Unstructured Data)。实际的格式如下(引自协议文档):

 Byte/     0       |       1       |       2       |       3       |   
    /              |               |               |               |   
   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
   +---------------+---------------+---------------+---------------+
  0/ HEADER                                                        /   
   /                                                               /   
   /                                                               /   
   /                                                               /   
   +---------------+---------------+---------------+---------------+
 24/ COMMAND-SPECIFIC EXTRAS (as needed)                           /   
  +/  (note length in th extras length header field)               /   
   +---------------+---------------+---------------+---------------+
  m/ Key (as needed)                                               /   
  +/  (note length in key length header field)                     /   
   +---------------+---------------+---------------+---------------+
  n/ Value (as needed)                                             /   
  +/  (note length is total body length header field, minus        /   
  +/   sum of the extras and key length body fields)               /   
   +---------------+---------------+---------------+---------------+
  Total 24 bytes

如上所示,包格式十分简单。需要注意的是,占据了16字节的头部(HEADER)分为请求头(Request Header)和响应头(Response Header)两种。头部中包含了表示包的有效性的Magic字节、命令种类、键长度、值长度等信息,格式如下:

Request Header

 Byte/     0       |       1       |       2       |       3       |
    /              |               |               |               |
   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
   +---------------+---------------+---------------+---------------+
  0| Magic         | Opcode        | Key length                    |
   +---------------+---------------+---------------+---------------+
  4| Extras length | Data type     | Reserved                      |
   +---------------+---------------+---------------+---------------+
  8| Total body length                                             |
   +---------------+---------------+---------------+---------------+
 12| Opaque                                                        |
   +---------------+---------------+---------------+---------------+
 16| CAS                                                           |
   |                                                               |
   +---------------+---------------+---------------+---------------+

Response Header

 Byte/     0       |       1       |       2       |       3       |
    /              |               |               |               |
   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
   +---------------+---------------+---------------+---------------+
  0| Magic         | Opcode        | Key Length                    |
   +---------------+---------------+---------------+---------------+
  4| Extras length | Data type     | Status                        |
   +---------------+---------------+---------------+---------------+
  8| Total body length                                             |
   +---------------+---------------+---------------+---------------+
 12| Opaque                                                        |
   +---------------+---------------+---------------+---------------+
 16| CAS                                                           |
   |                                                               |
   +---------------+---------------+---------------+---------------+

如希望了解各个部分的详细内容,可以checkout出memcached的二进制协议的代码树,参考其中的docs文件夹中的protocol_binary.txt文档。

HEADER中引人注目的地方

看到HEADER格式后我的感想是,键的上限太大了!现在的memcached规格中,键长度最大为250字节,但二进制协议中键的大小用2字节表示。因此,理论上最大可使用65536字节(216)长的键。尽管250字节以上的键并不会太常用,二进制协议发布之后就可以使用巨大的键了。

二进制协议从下一版本1.3系列开始支持。

外部引擎支持

我去年曾经试验性地将memcached的存储层改造成了可扩展的(pluggable)。

MySQL的Brian Aker看到这个改造之后,就将代码发到了memcached的邮件列表。 memcached的开发者也十分感兴趣,就放到了roadmap中。现在由我和 memcached的开发者Trond Norbye协同开发(规格设计、实现和测试)。和国外协同开发时时差是个大问题,但抱着相同的愿景,最后终于可以将可扩展架构的原型公布了。代码库可以从memcached的下载页面 上访问。

外部引擎支持的必要性

世界上有许多memcached的派生软件,其理由是希望永久保存数据、实现数据冗余等,即使牺牲一些性能也在所不惜。我在开发memcached之前,在mixi的研发部也曾经考虑过重新发明memcached。

外部引擎的加载机制能封装memcached的网络功能、事件处理等复杂的处理。因此,现阶段通过强制手段或重新设计等方式使memcached和存储引擎合作的困难就会烟消云散,尝试各种引擎就会变得轻而易举了。

简单API设计的成功的关键

该项目中我们最重视的是API设计。函数过多,会使引擎开发者感到麻烦;过于复杂,实现引擎的门槛就会过高。因此,最初版本的接口函数只有13个。具体内容限于篇幅,这里就省略了,仅说明一下引擎应当完成的操作:

  • 引擎信息(版本等)
  • 引擎初始化
  • 引擎关闭
  • 引擎的统计信息
  • 在容量方面,测试给定记录能否保存
  • 为item(记录)结构分配内存
  • 释放item(记录)的内存
  • 删除记录
  • 保存记录
  • 回收记录
  • 更新记录的时间戳
  • 数学运算处理
  • 数据的flush

对详细规格有兴趣的读者,可以checkout engine项目的代码,阅读器中的engine.h。

重新审视现在的体系

memcached支持外部存储的难点是,网络和事件处理相关的代码(核心服务器)与内存存储的代码紧密关联。这种现象也称为tightly coupled(紧密耦合)。必须将内存存储的代码从核心服务器中独立出来,才能灵活地支持外部引擎。因此,基于我们设计的API,memcached被重构成下面的样子:

memcached-0003-001.png

重构之后,我们与1.2.5版、二进制协议支持版等进行了性能对比,证实了它不会造成性能影响。

在考虑如何支持外部引擎加载时,让memcached进行并行控制(concurrency control)的方案是最为容易的,但是对于引擎而言,并行控制正是性能的真谛,因此我们采用了将多线程支持完全交给引擎的设计方案。

以后的改进,会使得memcached的应用范围更为广泛。

总结

本次介绍了memcached的超时原理、内部如何删除数据等,在此之上又介绍了二进制协议和外部引擎支持等memcached的最新发展方向。这些功能要到1.3版才会支持,敬请期待!

这是我在本连载中的最后一篇。感谢大家阅读我的文章!

下次由长野来介绍memcached的应用知识和应用程序兼容性等内容。

posted @ 2013-01-26 11:54 昊天 阅读(382) | 评论 (0)编辑 收藏

下面是《memcached全面剖析》的第二部分。

我是mixi株式会社研究开发组的前坂徹。 上次的文章介绍了memcached是分布式的高速缓存服务器。本次将介绍memcached的内部构造的实现方式,以及内存的管理方式。另外,memcached的内部构造导致的弱点也将加以说明。

Slab Allocation机制:整理内存以便重复使用

最近的memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存。在该机制出现以前,内存的分配是通过对所有记录简单地进行malloc和free来进行的。但是,这种方式会导致内存碎片,加重操作系统内存管理器的负担,最坏的情况下,会导致操作系统比memcached进程本身还慢。Slab Allocator就是为解决该问题而诞生的。

下面来看看Slab Allocator的原理。下面是memcached文档中的slab allocator的目标:

the primary goal of the slabs subsystem in memcached was to eliminate memory fragmentation issues totally by using fixed-size memory chunks coming from a few predetermined size classes.

也就是说,Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块,以完全解决内存碎片问题。

Slab Allocation的原理相当简单。 将分配的内存分割成各种尺寸的块(chunk),并把尺寸相同的块分成组(chunk的集合)(图1)。

memcached-0002-01.png

图1 Slab Allocation的构造图

而且,slab allocator还有重复使用已分配的内存的目的。也就是说,分配到的内存不会释放,而是重复利用。

Slab Allocation的主要术语

Page

分配给Slab的内存空间,默认是1MB。分配给Slab之后根据slab的大小切分成chunk。

Chunk

用于缓存记录的内存空间。

Slab Class

特定大小的chunk的组。

在Slab中缓存记录的原理

下面说明memcached如何针对客户端发送的数据选择slab并缓存到chunk中。

memcached根据收到的数据的大小,选择最适合数据大小的slab(图2)。 memcached中保存着slab内空闲chunk的列表,根据该列表选择chunk,然后将数据缓存于其中。

memcached-0002-02.png

图2 选择存储记录的组的方法

实际上,Slab Allocator也是有利也有弊。下面介绍一下它的缺点。

Slab Allocator的缺点

Slab Allocator解决了当初的内存碎片问题,但新的机制也给memcached带来了新的问题。

这个问题就是,由于分配的是特定长度的内存,因此无法有效利用分配的内存。例如,将100字节的数据缓存到128字节的chunk中,剩余的28字节就浪费了(图3)。

memcached-0002-03.png

图3 chunk空间的使用

对于该问题目前还没有完美的解决方案,但在文档中记载了比较有效的解决方案。

The most efficient way to reduce the waste is to use a list of size classes that closely matches (if that’s at all possible) common sizes of objects that the clients of this particular installation of memcached are likely to store.

就是说,如果预先知道客户端发送的数据的公用大小,或者仅缓存大小相同的数据的情况下,只要使用适合数据大小的组的列表,就可以减少浪费。

但是很遗憾,现在还不能进行任何调优,只能期待以后的版本了。但是,我们可以调节slab class的大小的差别。接下来说明growth factor选项。

使用Growth Factor进行调优

memcached在启动时指定 Growth Factor因子(通过-f选项),就可以在某种程度上控制slab之间的差异。默认值为1.25。但是,在该选项出现之前,这个因子曾经固定为2,称为“powers of 2”策略。

让我们用以前的设置,以verbose模式启动memcached试试看:

$ memcached -f 2 -vv

下面是启动后的verbose输出:

slab class   1: chunk size    128 perslab  8192
slab class   2: chunk size    256 perslab  4096
slab class   3: chunk size    512 perslab  2048
slab class   4: chunk size   1024 perslab  1024
slab class   5: chunk size   2048 perslab   512
slab class   6: chunk size   4096 perslab   256
slab class   7: chunk size   8192 perslab   128
slab class   8: chunk size  16384 perslab    64
slab class   9: chunk size  32768 perslab    32
slab class  10: chunk size  65536 perslab    16
slab class  11: chunk size 131072 perslab     8
slab class  12: chunk size 262144 perslab     4
slab class  13: chunk size 524288 perslab     2

可见,从128字节的组开始,组的大小依次增大为原来的2倍。这样设置的问题是,slab之间的差别比较大,有些情况下就相当浪费内存。因此,为尽量减少内存浪费,两年前追加了growth factor这个选项。

来看看现在的默认设置(f=1.25)时的输出(篇幅所限,这里只写到第10组):

slab class   1: chunk size     88 perslab 11915
slab class   2: chunk size    112 perslab  9362
slab class   3: chunk size    144 perslab  7281
slab class   4: chunk size    184 perslab  5698
slab class   5: chunk size    232 perslab  4519
slab class   6: chunk size    296 perslab  3542
slab class   7: chunk size    376 perslab  2788
slab class   8: chunk size    472 perslab  2221
slab class   9: chunk size    592 perslab  1771
slab class  10: chunk size    744 perslab  1409

可见,组间差距比因子为2时小得多,更适合缓存几百字节的记录。从上面的输出结果来看,可能会觉得有些计算误差,这些误差是为了保持字节数的对齐而故意设置的。

将memcached引入产品,或是直接使用默认值进行部署时,最好是重新计算一下数据的预期平均长度,调整growth factor,以获得最恰当的设置。内存是珍贵的资源,浪费就太可惜了。

接下来介绍一下如何使用memcached的stats命令查看slabs的利用率等各种各样的信息。

查看memcached的内部状态

memcached有个名为stats的命令,使用它可以获得各种各样的信息。执行命令的方法很多,用telnet最为简单:

$ telnet 主机名 端口号

连接到memcached之后,输入stats再按回车,即可获得包括资源利用率在内的各种信息。此外,输入”stats slabs”或”stats items”还可以获得关于缓存记录的信息。结束程序请输入quit。

这些命令的详细信息可以参考memcached软件包内的protocol.txt文档。

$ telnet localhost 11211
Trying ::1...
Connected to localhost.
Escape character is '^]'.
stats
STAT pid 481
STAT uptime 16574
STAT time 1213687612
STAT version 1.2.5
STAT pointer_size 32
STAT rusage_user 0.102297
STAT rusage_system 0.214317
STAT curr_items 0
STAT total_items 0
STAT bytes 0
STAT curr_connections 6
STAT total_connections 8
STAT connection_structures 7
STAT cmd_get 0
STAT cmd_set 0
STAT get_hits 0
STAT get_misses 0
STAT evictions 0
STAT bytes_read 20
STAT bytes_written 465
STAT limit_maxbytes 67108864
STAT threads 4
END
quit

另外,如果安装了libmemcached这个面向C/C++语言的客户端库,就会安装 memstat 这个命令。使用方法很简单,可以用更少的步骤获得与telnet相同的信息,还能一次性从多台服务器获得信息。

$ memstat --servers=server1,server2,server3,...

libmemcached可以从下面的地址获得:

  • http://tangent.org/552/libmemcached.html

查看slabs的使用状况

使用memcached的创造着Brad写的名为memcached-tool的Perl脚本,可以方便地获得slab的使用情况(它将memcached的返回值整理成容易阅读的格式)。可以从下面的地址获得脚本:

  • http://code.sixapart.com/svn/memcached/trunk/server/scripts/memcached-tool

使用方法也极其简单:

$ memcached-tool 主机名:端口 选项

查看slabs使用状况时无需指定选项,因此用下面的命令即可:

$ memcached-tool 主机名:端口

获得的信息如下所示:

 #  Item_Size   Max_age  1MB_pages Count   Full?
 1     104 B  1394292 s    1215 12249628    yes
 2     136 B  1456795 s      52  400919     yes
 3     176 B  1339587 s      33  196567     yes
 4     224 B  1360926 s     109  510221     yes
 5     280 B  1570071 s      49  183452     yes
 6     352 B  1592051 s      77  229197     yes
 7     440 B  1517732 s      66  157183     yes
 8     552 B  1460821 s      62  117697     yes
 9     696 B  1521917 s     143  215308     yes
10     872 B  1695035 s     205  246162     yes
11     1.1 kB 1681650 s     233  221968     yes
12     1.3 kB 1603363 s     241  183621     yes
13     1.7 kB 1634218 s      94   57197     yes
14     2.1 kB 1695038 s      75   36488     yes
15     2.6 kB 1747075 s      65   25203     yes
16     3.3 kB 1760661 s      78   24167     yes

各列的含义为:

含义
# slab class编号
Item_Size Chunk大小
Max_age LRU内最旧的记录的生存时间
1MB_pages 分配给Slab的页数
Count Slab内的记录数
Full? Slab内是否含有空闲chunk

从这个脚本获得的信息对于调优非常方便,强烈推荐使用。

内存存储的总结

本次简单说明了memcached的缓存机制和调优方法。希望读者能理解memcached的内存管理原理及其优缺点。

下次将继续说明LRU和Expire等原理,以及memcached的最新发展方向—— 可扩充体系(pluggable architecher))。

posted @ 2013-01-26 11:49 昊天 阅读(432) | 评论 (0)编辑 收藏

翻译一篇技术评论社的文章,是讲memcached的连载。fcicq同学说这个东西很有用,希望大家喜欢。

我是mixi株式会社开发部系统运营组的长野。日常负责程序的运营。从今天开始,将分几次针对最近在Web应用的可扩展性领域的热门话题memcached,与我公司开发部研究开发组的前坂一起,说明其内部结构和使用。

memcached是什么?

memcached 是以LiveJournal 旗下Danga Interactive 公司的Brad Fitzpatric 为首开发的一款软件。现在已成为 mixihatenaFacebookVox、LiveJournal等众多服务中提高Web应用扩展性的重要因素。

许多Web应用都将数据保存到RDBMS中,应用服务器从中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、数据库响应恶化、网站显示延迟等重大影响。

这时就该memcached大显身手了。memcached是高性能的分布式内存缓存服务器。一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。

memcached-0001-01.png

图1 一般情况下memcached的用途

memcached的特征

memcached作为高速运行的分布式缓存服务器,具有以下的特点。

  • 协议简单
  • 基于libevent的事件处理
  • 内置内存存储方式
  • memcached不互相通信的分布式

协议简单

memcached的服务器客户端通信并不使用复杂的XML等格式,而使用简单的基于文本行的协议。因此,通过telnet 也能在memcached上保存数据、取得数据。下面是例子。

$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
set foo 0 0 3     (保存命令)
bar               (数据)
STORED            (结果)
get foo           (取得命令)
VALUE foo 0 3     (数据)
bar               (数据)

协议文档位于memcached的源代码内,也可以参考以下的URL。

基于libevent的事件处理

libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能封装成统一的接口。即使对服务器的连接数增加,也能发挥O(1)的性能。 memcached使用这个libevent库,因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。关于事件处理这里就不再详细介绍,可以参考Dan Kegel的The C10K Problem。

内置内存存储方式

为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。由于数据仅存在于内存中,因此重启memcached、重启操作系统会导致全部数据消失。另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。 memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。关于内存存储的详细信息,本连载的第二讲以后前坂会进行介绍,请届时参考。

memcached不互相通信的分布式

memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。各个memcached不会互相通信以共享信息。那么,怎样进行分布式呢?这完全取决于客户端的实现。本连载也将介绍memcached的分布式。

memcached-0001-02.png

图2 memcached的分布式

接下来简单介绍一下memcached的使用方法。

安装memcached

memcached的安装比较简单,这里稍加说明。

memcached支持许多平台。 * Linux * FreeBSD * Solaris (memcached 1.2.5以上版本) * Mac OS X

另外也能安装在Windows上。这里使用Fedora Core 8进行说明。

memcached的安装

运行memcached需要本文开头介绍的libevent库。Fedora 8中有现成的rpm包,通过yum命令安装即可。

$ sudo yum install libevent libevent-devel

memcached的源代码可以从memcached网站上下载。本文执笔时的最新版本为1.2.5。 Fedora 8虽然也包含了memcached的rpm,但版本比较老。因为源代码安装并不困难,这里就不使用rpm了。

memcached安装与一般应用程序相同,configure、make、make install就行了。

$ wget http://www.danga.com/memcached/dist/memcached-1.2.5.tar.gz
$ tar zxf memcached-1.2.5.tar.gz
$ cd memcached-1.2.5
$ ./configure
$ make
$ sudo make install

默认情况下memcached安装到/usr/local/bin下。

memcached的启动

从终端输入以下命令,启动memcached。

$ /usr/local/bin/memcached -p 11211 -m 64m -vv
slab class   1: chunk size     88 perslab 11915
slab class   2: chunk size    112 perslab  9362
slab class   3: chunk size    144 perslab  7281
中间省略
slab class  38: chunk size 391224 perslab     2
slab class  39: chunk size 489032 perslab     2
<23 server listening
<24 send buffer was 110592, now 268435456
<24 server listening (udp)
<24 server listening (udp)
<24 server listening (udp)
<24 server listening (udp)

这里显示了调试信息。这样就在前台启动了memcached,监听TCP端口11211 最大内存使用量为64M。调试信息的内容大部分是关于存储的信息,下次连载时具体说明。

作为daemon后台启动时,只需

$ /usr/local/bin/memcached -p 11211 -m 64m -d

这里使用的memcached启动选项的内容如下。

选项 说明
-p 使用的TCP端口。默认为11211
-m 最大内存大小。默认为64M
-vv 用very verbose模式启动,调试信息和错误输出到控制台
-d 作为daemon在后台启动

上面四个是常用的启动选项,其他还有很多,通过

$ /usr/local/bin/memcached -h

命令可以显示。许多选项可以改变memcached的各种行为,推荐读一读。

用客户端连接

许多语言都实现了连接memcached的客户端,其中以Perl、PHP为主。仅仅memcached网站上列出的语言就有

  • Perl
  • PHP
  • Python
  • Ruby
  • C#
  • C/C++
  • Lua

等等。

这里介绍通过mixi正在使用的Perl库链接memcached的方法。

使用Cache::Memcached

Perl的memcached客户端有

  • Cache::Memcached
  • Cache::Memcached::Fast
  • Cache::Memcached::libmemcached

等几个CPAN模块。这里介绍的Cache::Memcached是memcached的作者Brad Fitzpatric的作品,应该算是memcached的客户端中应用最为广泛的模块了。

使用Cache::Memcached连接memcached

下面的源代码为通过Cache::Memcached连接刚才启动的memcached的例子。

#!/usr/bin/perl

use strict;
use warnings;
use Cache::Memcached;

my $key = "foo";
my $value = "bar";
my $expires = 3600; # 1 hour
my $memcached = Cache::Memcached->new({
    servers => ["127.0.0.1:11211"],
    compress_threshold => 10_000
});

$memcached->add($key, $value, $expires);
my $ret = $memcached->get($key);
print "$ret\n";

在这里,为Cache::Memcached指定了memcached服务器的IP地址和一个选项,以生成实例。 Cache::Memcached常用的选项如下所示。

选项 说明
servers 用数组指定memcached服务器和端口
compress_threshold 数据压缩时使用的值
namespace 指定添加到键的前缀

另外,Cache::Memcached通过Storable模块可以将Perl的复杂数据序列化之后再保存,因此散列、数组、对象等都可以直接保存到memcached中。

保存数据

向memcached保存数据的方法有

  • add
  • replace
  • set

它们的使用方法都相同:

my $add = $memcached->add( '键', '值', '期限' );
my $replace = $memcached->replace( '键', '值', '期限' );
my $set = $memcached->set( '键', '值', '期限' );

向memcached保存数据时可以指定期限(秒)。不指定期限时,memcached按照LRU算法保存数据。这三个方法的区别如下:

选项 说明
add 仅当存储空间中不存在键相同的数据时才保存
replace 仅当存储空间中存在键相同的数据时才保存
set 与add和replace不同,无论何时都保存

获取数据

获取数据可以使用get和get_multi方法。

my $val = $memcached->get('键');
my $val = $memcached->get_multi('键1', '键2', '键3', '键4', '键5');

一次取得多条数据时使用getmulti。getmulti可以非同步地同时取得多个键值,其速度要比循环调用get快数十倍。

删除数据

删除数据使用delete方法,不过它有个独特的功能。

$memcached->delete('键', '阻塞时间(秒)');

删除第一个参数指定的键的数据。第二个参数指定一个时间值,可以禁止使用同样的键保存新数据。此功能可以用于防止缓存数据的不完整。但是要注意,set函数忽视该阻塞,照常保存数据

增一和减一操作

可以将memcached上特定的键值作为计数器使用。

my $ret = $memcached->incr('键');
$memcached->add('键', 0) unless defined $ret;

增一和减一是原子操作,但未设置初始值时,不会自动赋成0。因此,应当进行错误检查,必要时加入初始化操作。而且,服务器端也不会对超过2 SUP(32)时的行为进行检查。

总结

这次简单介绍了memcached,以及它的安装方法、Perl客户端Cache::Memcached的用法。只要知道,memcached的使用方法十分简单就足够了。

下次由前坂来说明memcached的内部结构。了解memcached的内部构造,就能知道如何使用memcached才能使Web应用的速度更上一层楼。欢迎继续阅读下一章。

posted @ 2013-01-26 11:47 昊天 阅读(457) | 评论 (0)编辑 收藏

常常思考一个问题:是不是考虑做2年开发,打2年酱油,然后结婚生子,这样到底行不行?无论你是男是女,人生是一场独自修行的道路。如果在可以选的时候,还是选择靠自己吧。

无论你是要养家还是要实现人生价值,如果你处在迷茫之中,希望这篇文章可以传达一些正能量,可以帮到你。--当然这篇文章是转的呀,但真的很有理啊,假如看了这篇文章,会影响到你的一生,那么我就觉得今天这一小时的时间花在这里值了。

 

你所有不曾料想过的问题,都会随着时间的推移而与你不期而遇;你所有曾经潇洒的随遇而安,同样也会随时间流逝而让你承担那些似乎命中已经注定的代价。在这个世界上,“唯一不可阻挡的是时间,它像一把利刃,无声地切开了坚硬和柔软的一切,恒定地向前推进着,没有任何东西能够使它的行进产生丝毫颠簸,它却改变着一切。”我始终相信一句话:出来混,迟早要还的。虽然你我皆是凡人,只是这芸芸众生中的普通一员,但我依然希望每个人的生命都能够迎着太阳开花结果。不管以你现在的阅历是否能够理解这段话的涵义,请先记下来。我相信总有一天,你会明白。因为,时间能解释一切,时间能证明一切,时间能解决一切。——题记

在最近的一年的职业规划咨询过程中,我明显地感觉到35岁以上人群对于职业生涯规划需求的迫切性。也正是从这些案例中,我们得以清晰地洞察到,时间点的把握对于一个人的成长如何起着决定性的作用。在我们的客户中,遇到的往往是两类较为极端的案例:一类是已经做到一定级别——至少是总监级以上,在公司具有一定地位,年薪不少于20万的人;另一类是工作多年,但依然处于一个相对低的位置,无论是职位层级和物质回报,还是个人的价值感,均无法得到较高认同。这两类案例虽然极端,但却给我们提供可以借鉴的思考。通过对比,我们发现,但凡那些在职业发展上获得一定成功的人,都有一个共同的特征,那就是在自己所熟悉且擅长的领域,至少精耕细作了10年以上。而那些在职场上找不到自己位置的人,往往属于每隔一两年换方向,从来没有在某一个方向上深入积累下去。当然,除了频繁跳槽的因素之外,还有另外一个因素,那就是:已经在一个方向上深入积累,但这种积累属于重复劳动式的,并没有上升,致使职业发展原地踏步。

不管你是否承认,你都必须重视“35岁现象”。很多企业在招募人才时,明确规定年龄在35岁以下。如果你的年龄到了35岁却还在通过招聘网站投递简历不断跳槽的话,你就应该反省一下自己到底哪里做错了。当然,根据我们的实践咨询经验来看,如果你真到了35岁甚至更高的年龄才去思考这个问题的时候,很有可能这个问题你已经无力解决了,很多现实的困难会让你有心无力,束手无策。到了这个时候,很多人会因为当初的选择后悔不迭,但却欲哭无泪。所以,无论是为了避免走更多的弯路,还是迈向更大的成功,你都必须提前思考你未来的谋生之路。已经有太多的案例证明:未雨绸缪会比临时抱佛脚有用得多。

在我们的客户中,有相当一部分属于80后,也是职业规划问题的高发区。年龄最大的一批80后,已经过了“三十而立”的年龄;但还有相当一部分80后,正在迈向三十而立的路上。如果说年轻是上帝给予你的犯错的资本,你还有时间去弥补;但如果你已经到了二十岁的尾巴上,这种资本将不再是你的专利。从大学毕业,到你的而立之年,这个时间已经足够长,让你有足够的机会去了解社会,适应社会,并反思自己的成长。

从30岁到35岁,这其中有5年的时间。假如给你足够犯错的时间,那么,为了不让你35岁以后的职业生涯变得一塌糊涂,你至少应该在30岁就确立明确的目标,并利用5年的时间去追赶。这可能是你成长的最后的最佳时机。错过了这个时机,你已不再年轻,社会也不会再以包容的心态去原谅你的年少轻狂。否则,你多走一步错路,就必定要在以后以十倍的代价补回来。从30岁到35岁,你应该学着为你芸芸众生般的生命,多积累一些厚度,以便让你下半生的职业生涯不要在“假如一切能够重来”的悔恨和遗憾中度过。    

30岁:你必须要面对的三大问题

第一个问题,就是家庭与责任的问题。

不要以为自己还年轻。不要以为有些问题离自己还很远。无论是颓废还是忙碌,你的时间都在飞快地逝去,你感觉自己在加速变老。有些问题,不管你是否愿意去面对,但你的成长阶段决定了你必须要承担与年龄相匹配的责任。大多数的人,到了这个年龄段,都必须要考虑一下家庭问题。如果你已经成家立业,你必须清醒地意识到:你的职业到底该如何发展,才能确保让你支撑整个家庭的负担?这不仅仅包括你的爱人,还包括你的孩子及父母(尤其是双方都是独生子女的时候,你要承担的是双方四位老人的供养)。到了这个阶段,你的压力是成倍增长的,但如果你在职场上的成长无法实现倍增,甚至还在以某种形式进行着倒退,那么,你以后的职业发展就会面临更多的压力。而这种压力,更多的时候,会使你没有余力去谋求更好的发展。很多人在跳槽时,往往会有一个很大的顾虑:如果我跳了,如果收入没有现在的高,那么,现在的工作,我还会轻易再动吗?说得更不客气一点:我还敢轻易再动吗?我还有那个胆量与勇气吗?所以,要想行动,就趁早,趁你还没有背上家庭的包袱的时候,轻装上阵,拼尽全力向前冲,这是你唯一的选择。千万不要在这个时候享受安逸,否则,你的后半生都将永远在碌碌无为中“被安逸”下去。

第二个问题,是能力与年龄的匹配度问题

为什么很多企业在招聘人才的时候,明确规定要5年经验、10年经验等等类似的工作经验要求呢?因为工作时间意味着与之匹配的能力等级。同样的工作,5年经验和10年经验所积累的能力是不一样的,所能承担的责任也是不一样的。你的工作年限越长,往往也意味着你的能力越强,这二者之间是一种正向倍增的关系。但如果你违背了这种关系,那么,你就无法获得用人单位的认可,进而丧失更好的职业发展机会。在我们的很多客户中,其中较为棘手的一种情况,就是能力与年龄不匹配的问题。很多工作3年、5年的人,甚至和工作一年的人在能力方面并没有什么太大的差别,所以当他们的职业想向上突破时,会遇到很多的阻力。导致这种情况的产生有两种原因:一是频繁跳槽,没有在一个方向上积累,无一技之长;二是虽然在一个方向上长期积累,但只有第一年是成长的,剩下的几年都是在做重复劳动,原地踏步。

所以,对于那些年龄30岁的朋友来说,从现在开始,你必须要慎重审视一个问题:从毕业到现在,我工作几年了?我身上所具备的能力是否与我的年龄相匹配?如果不匹配,那么,你一定要及早树立危机感,并跑步前进,以弥补与那些先知先觉者之间的差距,确保自己不会在竞争的过程中被他人挤下马。

第三个问题,是知识结构的构建与提升问题

我们曾经服务过一个客户,做销售做了五六年,也积累了相当丰富的实战操作经验。有一次跳槽去应聘某知名快消企业的区域营销经理的职位。在面试的过程中,所有关于具体操作层面的问题,他都能够对答如流,但上升到系统层面及战略层面的问题时,他的脑子就一片空白。我相信在职场上做过五六年的人都有这种感受:感觉在具体的操作层面,无论是流程,还是技巧与方法,都能够熟练掌握;但如果从更高一层的角度去看待问题,往往又不知道如何下手。

身在职场,不同职位等级的人,所做的事情是不一样的,他们所具备的眼光与思维模式同样也有差别。一个最基础的业务员,想着如何维护好终端,这是战术层面的东西,也是他的职责所在;但如果一个营销总监这样的角色,还和业务员一样天天想着如何去和终端老板打交道,那就是他的失职了。每一个企业的运营,都会由战略与战术所构成。大的战略会细分成小的战略,小的战略会细分成一个一个的执行战术,由基层人员去付诸实施。不同层级的人,分别负责不同高度的工作,各司其职,这也是团队协作的意义所在。同样,每一个不同层级的人,也存在不同的知识结构。层级越高,你看问题的眼光和思路就要越高,你的整体知识结构层次也要向上发展和突破。要不然,你的能力,永远只能停留在具体的基层操作层面上,不会有大的发展,职位上更不可能有上升。       

另外,从沟通的层面来说,你的职位越高,你与老板的距离就越近,你与公司最高领导接触的机会也就越多。如果你看待问题的思路无法与老板们保持在高度上的一致性,那么,你的能力是无法得到认可的。在这样的情况下,你向上晋升的可能性就微乎其微了,职业生涯也会就此止步。

30岁之前:如何打好基础

30 岁是一个承前启后的年龄,也是一个非常重要的时间点。我们在进行职业生涯规划咨询之前,会有一个评估环节,系统评估客户的综合基础,看看是否还适合做职业规划。其中有一个很重要的考察标准,就是年龄问题,重点是30岁左右的年龄。如果你30岁之前几乎没有任何积累,那么,30岁以后的职业生涯要想获得突破是十分困难的,有很多现实的问题无法解决。这是硬伤。但凡遇到有这样硬伤的客户,我们只能对他们说“很抱歉,我们无能为力。”所以,要想让你30岁以后的职业生涯不后悔,顺利渡过30岁这道坎,你就需要在30岁之前对自己负责,未雨绸缪。

你要做的第一件事,就是找准一个可以为之奋斗5年、10年甚至更久的目标。

这是一种最理想的状态。因为一个清晰的目标,可以让你的职业成长围绕着一个点去积累经验,而这种日积月累的经验积累,也是你日后升职加薪的筹码。虽然经验并不一定与能力相关,但如果没有经验的积累,你的能力也基本没有提升的可能。

关于目标与核心竞争力的问题——所谓核心竞争力,一定要有一个核心才行。围绕着这个核心,你才能构建你的竞争力。而这个核心,就是你的职业目标方向——我到底想成为什么样的人?这个问题解决了,方向明确了,哪怕你走得再慢,也可以比那些走弯路的人走得快,因为你明确你的目标,你一直在朝着你的目标前进,你是在走直线。捷径是什么?捷径就是不走弯路,永远走直线。

我们去年曾经咨询过一个客户,7年换了6家公司。按照通常的理解,大家往往会认为这个人的职业发展很糟糕。其实不是。这个人目前的税后月薪是1万元,属于还不错的那种水平。大家也许会纳闷:为什么频繁跳槽的一个人,会有这样的职业发展水平呢?有些人会觉得他很幸运,运气比别人好而已。其实不是。观察他的成长经历,我们发现, 每一次跳槽,他都没有偏离既定的方向。只要方向不错,那么,他的每一步行动,都是在接近这个方向,都是在为以后的职业发展积累更多的筹码。你积累的厚度,将最终决定你未来的发展高度。现在房价、物价这么高,很多人的生存压力也骤然增加,由此也导致很多人迫于生存的压力而变得浮躁和随波逐流。一份工作,往往坚持不了几个月,觉得看不到希望,工资无法提升,往往就选择跳槽。但实际上,跳槽并不能从根本上解决问题。现在的用人单位,都变得非常务实,做多少事,拿多少钱,天经地义(当然,特权阶层不在讨论范围之内)。如果你无法为公司发展贡献更大的价值,却还想着得到更多的回报,这样只赚不赔的买卖是没有人愿意干的。你为公司贡献多少价值,取决于你的能力到底有多强。而能力的积累,则源自于在一个方向上的长期坚持。既便是那些看起来令人很羡慕的成功者们,也少不了日积月累的修炼与成长。那些因为走了弯路却想在极短的时间内把损失补回来的急功近利的做法,只会让你失去更多的东西。      

大家可能听说过10000小时定律:不论你想在任何一个领域成功,你都必须至少付出10000个小时的磨练。Macolm Gladwell在他的新书《异类:成功人士的故事》中说道,无论是最优秀的运动员,企业家,音乐家还是科学家,经调查,你都会发现他们至少都在付出了长达十年,每天不低于三小时的努力之后才崭露头角的。书中有这样一个例子:34岁的世界顶级小提琴家马克西姆•文格罗夫出生在西伯利亚,在4岁的时候接触到第一把小提琴,从此就展现出了过人的天赋。然而他的天赋和敬业度是成正比的。他每天练习7小时,5岁就举办了独奏会,15岁就获得了国际大奖。文格罗夫说,“我的母亲每天晚上8点回到家,吃完晚饭之后就教我小提琴直到凌晨4点。对于一个4岁的小孩来说,这简直就是酷刑,但两年后我变成了小提琴手。”

很多人在抱怨没有机会。其实并非如此。肯特大学的社会学教授弗兰克•弗雷迪认为有效投入很多时间去练习的人自然会拥有自己的运气:“他们近乎苛求地努力,当运气来的时候,他们早就准备好了。”这正应了中国的那句老话——“机会只垂青有准备的头脑。”如果你没有遇到机会,不要抱怨,你首先要反思的是自身,到底哪些方面准备不足?根据我们的经验,只有你在一个方向上坚持至少5年以上,你才有可能得到回报,而且这种回报会对你以后的职业发展产生一个相对长期的影响。同时,也正是这么长时间的积累,让你的基础非常扎实和牢固,你的脚步也会站得更稳。

身在职场,无非要做两件事:一是做事,二是做人。

如果说找准目标为是了积累更多的核心技能,更好地做事的话,那么,学会做人,则是你要职场发展要考虑的第二件大事情。我们甚至认为,会做人比会做事还要重要百倍。找到一个合适的职业目标,对于很多迷茫中的人来说可能并不是容易的事;但学会做人,则是你在任何公司、任何时候都可以修炼的一种职业素养。即便你的职业技能没有多大的突破与成长,但良好的职业素养则会从另外一种层面为你的职业发展增添筹码。我们也遇到不少能力不足、但态度很好、很积极的客户,他们用自己的优秀职业素养成功征服了面试官,得到了不错的发展机会。有句话叫“要做事,先做人。”能力不足,可以慢慢培养;但如果不会做人,在任何组织中,都会成为人人喊打的过街老鼠。

在做人的修炼中,有四项要素,是能够对你以后的职业发展起到致命性影响的。第一是你的人际关系处理能力。这其中包括与你的上司的关系,以及与你的同事的关系。与上司的关系不好,直接决定着你的升迁。所以我经常对我们的客户说这样的一句话:“如果你与上司的关系不好,基本上也就意味着你在这个公司的职业生涯就此终结。” 而与同事的关系不好,即便你哪一天升迁了,成为了他们的领导,你也会因为缺乏“群众基础”而导致权力被架空,你的下属对你不是阳奉阴违,就是故意挑衅,你会发现领导这个位置其实很不好坐。

第二项要素,就是要树立不断学习的能力。我们应注重学习的重要性。我们在去年曾经遇到过一个客户,已经近40岁的人了,做会计做了14年,但仍然只是一个基层主管的职位,在公司中也看不到晋升的机会,也已经好多年没有加薪了。我们问了他一个问题,“在这14年中,你有没有进行过培训、进修或者任何其他形式的充电?”他说,“没有”。“从来没有吗?”“从来没有。”这是一个非常极端的例子,但这种现象在职场中却是非常普遍的。关于自己的学习与成长,你不妨问自己几个问题:与一年前相比,我的专业能力明显进步了吗?无论是理论还是实践,我有自己独特的见解吗?假如遇到职责范围内的事情,我一个人是否能够独立搞定,是否具备独当一面的能力?厚度决定高度。你所以会有厚度的积累,原因在于你不断强化的学习能力。没有学习,你的职业发展就会在原地踏步。你以为你没有退步,但实际上别人在进步,你与别人之间的差距在不知不觉中拉开了,你也就退步了。       

第三项要素,就是要树立职业化精神不管你有没有目标,不管这项工作是不是你愿意做的,只要你在这个岗位上,你就应该把事情做好。在其位谋其职,这是最起码的一种职业素养,也是任何岗位上都不可或缺的一种职业精神。既然在这个岗位上,你的岗位职责要求你把工作做好;如果不想做好,那就走人,别在这里浪费大家的时间。良好的职业素养,有时候比能力本身更重要。

良好的职业化精神,是很多优秀人士身上必备的成功素质。即便这份工作你不喜欢,也不要用消极的的方式去对待,因为你不仅在浪费公司的时间,更是在浪费你自己的时间。身在职场,你能收获的无非是两种东西:一是回报,二是成长。在没有明确的目标之前,把眼前的事情做到极致,把所有你能够在这个职位上得到的东西,统统用自己最大的努力去争取。以消极的态度对待工作,你会两手空空,什么也得不到。

第四项要素,就是要有强大的内心。我所遇到的很多人,其实内心里都很脆弱,无法经受漫长人生道路上所降临的各种苦难。要知道,每个人的成长都不是一帆风顺。即便是耶酥,又何尝不会遭受苦难的洗礼?强大的内心可以让一个人在绝望中看到希望,而消极悲观只会让你在希望面前错机良机。《肖申克的救赎》中有这样一句台词:“懦怯囚禁人的灵魂,希望可以感受自由。强者自救,圣者渡人。”你没有本事成为圣者,但一定要变成强者。“物竞天择,适者生存。”优胜劣汰永远是自然进步的永恒法则。你只有适应这个法则,才能在这样竞争激烈的社会中获得生存之地。

30岁之后:如何实现质的突破

30岁以后的职业发展,不应该再是原地踏步、停滞不前的状态。要想让你35岁以后的职业生涯实现一个比较大的发展,你应该从30岁开始,好好利用5年时间,着手以下3件至关重要的事情的准备。

第一件事就是完成你的专业化品牌构建。如何构建你自己的个人品牌。你到了30岁以后,再去频繁跳槽,肯定是不行的,最大的不利,就是你的专业知识成长,永远只是停留在一个相对低的层次上,而这又会影响你职位的晋升与薪水的增长。我该干什么,适合做什么,此时应该有一个清晰而明确的定位,并利用剩余5年的时间去积累。否则,“35岁现象”一定会在你身上上演。

“日本战略之父”大前研一在其专著《专业主义》中也提出了这样的观点:“你凭什么胜出?未来能够牵动世界大势的,是个人之间的竞争。能否独霸世界舞台,锻造他人无法超越的核心竞争力?你唯一的依恃,就是专业。” 你是一流的商务人士,还是一般的上班族?差别就在这里!给自己一个专业定位,树立自己的品牌形象,会为你以后的职业发展增加更多有价值的筹码。我们曾经遇到过5年跳槽13次、接触过10余个行业的客户。假如你也这样,就毫无专业性可言了。

当然,为了支撑你“专业化”的品牌形象,你还需要在以下两个方面做出努力:第一个方面,就是我们在前面提到过的知识结构的问题,要脱离具体的操作层面的问题,把你看待问题的眼光放得更高一些,更远一些,成为一个具有“远见卓识”而不是“鼠目寸光”的人。你要向这个行业里最优秀的人看齐,并以他们为目标,作为自己修炼成长的榜样,一步一步弥补与优秀之间的差距。这个时候,在职学习、进修、培训等等都需要提上日程,学会用新的知识充实自己的头脑。第二个方面,就是树立自己在行业内的影响力。每一个行业的优秀人才,都有自己聚集的圈子。比如哈佛商业评论网、职业经理人网、业务员网等等,都是相关人才聚集的地方。为什么有些人出去讲一次课就要好几万?为什么有些人能够用自己的力量推动一个行业的发展?原因就是他们的影响力太大了。你要想成为优秀的职场人士,也必须像那些最优秀的人看齐,树立在这个行业内的影响力,用你的思想去影响别人。我们去年遇到一个非常优秀的小伙子,至今印象深刻。他是一个印刷专业的大专生,毕业之后去印刷厂做销售。或许在有些人看来,这个专业、这个职位并没有什么特殊之处。但正是这样一个看起来没有什么含金量的职位,他却用自己独特的方式构建着自己的影响力。他给自己定了一个要求:每月都要写一篇文章,发表在这个行业内最具影响力的杂志上。当然,并不是每篇文章都能够达到发表的质量,但每个月必须要保证质量写一篇。 

这样做的好处,第一是树立了你在这个行业内的影响力;第二,可以让你形成自己的知识结构。我们前面提过知识结构的问题,这里需要再强调一下。你通过学习、培训等方式,得到的知识,终究是别人的,不是你自己的。要想形成你自己的知识结构,你需要将这些外来吸收的知识应用到工作实践中。在实践的过程中,同样的知识理论,在不同的情况下,每个人的感受和心得是不一样的。你写文章的过程,也就是对你自己在实践中的感悟和心得进行总结和反思的过程。在这个过程中形成的知识结构,才能真正称得上是你“自己的”知识。

第二件事情,就是把自己培养成一个优秀的管理者(这里指的是中层管理者,也是你的第一个管理者目标)。除了那些对于技术十分热衷的人,如果你要想在职场上获得更多的空间,职位上的晋升必不可少。有些人会说我不愿意成为一个领导者,我只希望做好自己的份内事,我就满足了。对于抱有这种想法的人,我只能说很抱歉,这不是你能选择的。原因很简单:你25岁的时候,可以做一名基础员工;30岁的时候,也可以做一名基础员工;但你到了35岁以上的时候,你如果还只想着做一名基础员工,那就不太现实了。我们前面提到过 “35岁现象”。如果一个已经35岁的人,他的能力还只是局限在基础岗位上,那么,这样的人基本上是没什么价值的(当然,落后的国企及事业单位除外)。所以,不管你是否愿意,你都必须把职位晋升作为你职业成长道路上的一个重要目标,并为之付出努力。     

走上了管理者岗位,是你职业成长的关键一步。以后的发展空间,都与此次的晋升密不可分。但这个位子并不是每个人都能够坐稳。盖洛普曾经做过一项调查,发现基础员工之所以离职频繁,很多时候问题并不在于公司,而是在于其上司——公司的中层管理者。在我们的职业规划咨询经历中,也经常遇到这样的问题:正因为中层管理的不善,而对于下属员工的发展与成长造成了非常不利的影响,导致他们职业发展无论是在专业能力上,还是在职业修养上,均不能得到有效的成长。
在这里我们暂且不去讨论“中层危机”对于公司的发展多么多么重要这样比较宏观的话题,单纯从小处来说,你必须要意识到:你的管理,将有可能改变你的下属的终身命运,是你错误的管理方式在不知不觉中伤害到你的下属,迫使他们离开公司。管理者在招聘员工的时候,对于频繁跳槽的人是深恶痛绝。但作为一个旁观者,我不得不说的是:当你们在抱怨求职者的不忠、素质低下时,你们是否考虑过建立一种良性的人才成长机制,让每个人都能够得到成长?你们在抱怨人才短缺的同时,是否考虑过如何去认真地培养人才?很多企业会说我们培养了,但留不住,到头来都是为他人做嫁衣,这是一件得不偿失的事情,所以对于人才培养也就不那么热衷了。但问题恰恰出在这里:你为什么留不住人?如果每个公司都不去培养人才,都想吃“现成的”,那么,最终的后果将是:所有的企业,都将面临无人可用的境地!

当你的成长因为你的上司而受到阻碍的时候,这是你的不幸,你可以选择“用脚投票”;当你某一天也成为管理者的时候,我们不希望把这种不幸继续传递下去。唯有如此,人才的培养才能进入正循环,才能够让每个人都能够在职场上找到自己的一席之地。这其中困难多多,但我希望有理想的人能够不断努力,慢慢改变这一切。

第三件事情,就是找一家成长中的公司,在相对长的时间内,伴随其成长。在成长的过程中,找一家具有成长潜力和发展空间的公司,并随着公司一起成长,是一件非常重要的事情。一是你可以亲眼目睹一家公司从小到大、由弱到强的成长历程,对于公司的运营也会有更加深入的理解,你也能够体会到你的角色在公司成长中的位置和作用。二是在公司成长的过程中,你的价值会有更大的发挥余地,会更容易展现出来。很多人在找工作时都倾向于寻找大公司,这也可以理解,但是要想快速的成长,寻找规模不太大的成长中的公司,其实是更好的一种选择。因为在成长的过程中,会对于人才的需求较为迫切,你的职位晋升也会更快。三是你的忠诚度会为你的发展带来更多的回报。企业用人,其中最重要的一条就是忠诚度。没有哪一个企业喜欢朝秦暮楚的员工。尤其是现在跳槽率、流失率在众多企业居高不下的情况下,忠诚就成为一种非常难能可贵的职业精神。

不论你现在已经“奔三”,还是正在“奔三”的路上,请始终告诫自己:不要虚度了大好年华,抓住所有能够抓住的时间,为自己芸芸众生般的生命多积累一些厚度。当你慢慢变老的时候,回想自己的一生,你希望是令人欣慰的一生,还是悔恨和懊恼的一生?

最后,借用我最喜欢的乔布斯语录,作为本文的结尾。“人这一辈子没法做太多的事情,所以每一件都要做得精彩绝伦。你的时间有限,所以不要为别人而活。不要被教条所限,不要活在别人的观念里。不要让别人的意见左右自己内心的声音。最重要的是,勇敢的去追随自己的心灵和直觉,只有自己的心灵和直觉才知道你自己的真实想法,其他一切都是次要。”

当你觉得收入不如别人时,不要随便和别人攀比,因为你永远不可能知道别人在获得收入的背后付出了什么,同理,当我们生活不如别人时,亦无需格外羡慕,因为我们的生活质量是自己通过努力创造来的,如果你觉得自己生活不够好说明你的努力还不够。其实,我们只要坚持就好,坚持3天容易,难的是始终坚持! 

posted @ 2012-12-22 10:52 昊天 阅读(333) | 评论 (0)编辑 收藏

你对JVM调优是否熟悉,这里向大家描述一下,首先要了解几个概念,比如说Perm持久代,用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等。

JVM调优总结

Young(年轻代)

年轻代分三个区。一个Eden区,两个Survivor区。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。

Tenured(年老代)

年老代存放从年轻代存活的对象。一般来说年老代存放的都是生命期较长的对象。

Perm(持久代)

用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设置。

JVM调优之年轻代大小选择

◆响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。

◆吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。

JVM调优之年老代大小选择

◆响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:

1.并发垃圾收集信息

2.持久代并发收集次数

3.传统GC信息

4.花在年轻代和年老代回收上的时间比例

减少年轻代和年老代花费的时间,一般会提高应用的效率

◆吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。

JVM调优之较小堆引起的碎片问题

因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:

◆-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。

◆-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次FullGC后,对年老代进行压缩

posted @ 2012-12-11 11:19 昊天 阅读(523) | 评论 (0)编辑 收藏
在使用Java程序从数据库中查询大量的数据或是应用服务器(如tomcat、jboss,weblogic)加载jar包时会出现java.lang.OutOfMemoryError异常。这主要是由于应用服务器的内存不足引起的。这种异常常有以下几种情况(以下以tomcat环境为例,其它WEB服务器如jboss,weblogic等是同一个道理):
  1. java.lang.OutOfMemoryError: PermGen space
  PermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen space。从文字上看就是内存溢出,解决方法是加大内存。为什么会内存溢出,这是由于这块内存主要是被JVM存放Class和Meta信息的,Class在被Load的时候被放入PermGen space区域,它和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS的话,就很可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。
  解决方法: 手动设置MaxPermSize大小
  a.如果tomcat是以bat方式启动的,则如下设置:
  修改TOMCAT_HOME/bin/catalina.sh
  在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:
  JAVA_OPTS="-server -XX:PermSize=64M -XX:MaxPermSize=128m
  b.如果tomcat是注册成了windows服务,以services方式启动的,则需要修改注册表中的相应键值。
  打开注册表,找到目录HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Procrun 2.0\htfty\Parameters\Java,其中目录地址中红色标注的(如htfty)需要根据不同情况作修改,为tomcat服务注册成windows服务的名称。 可以看到JvmMs和JvmMx项,其中JvmMs设置最小的内存使用参数,JvmMx设置最大的内存使用参数。设置好JvmMs和JvmMx项的值,重启tomcat服务器即可生效。
  建议:将相同的第三方jar文件移置到tomcat/shared/lib目录下,这样可以达到减少jar 文档重复占用内存的目的。
  2. java.lang.OutOfMemoryError: Java heap space
  JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置。JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。在JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候将抛出此异常信息。
  解决方法:手动设置Heap size
  a.如果tomcat是以bat方式启动的,则如下设置:
  修改TOMCAT_HOME/bin/catalina.sh
  在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:
  JAVA_OPTS="-server -Xms800m -Xmx800m -XX:MaxNewSize=256m"
  b.如果tomcat是注册成了windows服务,以services方式启动的,则需要修改注册表中的相应键值。
  打开注册表,找到目录HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Procrun 2.0\htfty\Parameters\Java,其中目录地址中红色标注的(如htfty)需要根据不同情况作修改,为tomcat服务注册成windows服务的名称。 可以看到JvmMs和JvmMx项,其中JvmMs设置最小的内存使用参数,JvmMx设置最大的内存使用参数。设置好JvmMs和JvmMx项的值,重启tomcat服务器即可生效。
  提示:Heap Size 最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。
posted @ 2012-12-11 10:28 昊天 阅读(707) | 评论 (0)编辑 收藏
  1. 堆大小设置
    JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。我在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m。
    典型设置:
    • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
      -
      Xmx3550m:设置JVM最大可用内存为3550M。
      -Xms3550m
      :设置JVM初始内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
      -Xmn2g
      :设置年轻代大小为2G。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
      -Xss128k
      :设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
    • java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
      -XX:NewRatio=4
      :设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
      -XX:SurvivorRatio=4
      :设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
      -XX:MaxPermSize=16m:设置持久代大小为16m。
      -XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
  2. 回收器选择
    JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。
    1. 吞吐量优先的并行收集器
      如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。
      典型配置
      • java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
        -XX:+UseParallelGC
        :选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
        -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
        -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。
      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100
        -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy
        -XX:+UseAdaptiveSizePolicy
        :设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。
    2. 响应时间优先的并发收集器
      如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
      典型配置
      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
        -XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。
        -XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。
      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
        -XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
        -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片
  3. 辅助信息
    JVM提供了大量命令行参数,打印信息,供调试使用。主要有以下一些:
    • -XX:+PrintGC
      输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs]

                      [Full GC 121376K->10414K(130112K), 0.0650971 secs]

    • -XX:+PrintGCDetails
      输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]

                      [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]

    • -XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用
      输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
    • -XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用
      输出形式:Application time: 0.5291524 seconds
    • -XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用
      输出形式:Total time for which application threads were stopped: 0.0468229 seconds
    • -XX:PrintHeapAtGC:打印GC前后的详细堆栈信息
      输出形式:
      34.702: [GC {Heap before gc invocations=7:
       def new generation   total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)
      eden space 49152K,  99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)
      from space 6144K,  55% used [0x221d0000, 0x22527e10, 0x227d0000)
        to   space 6144K,   0% used [0x21bd0000, 0x21bd0000, 0x221d0000)
       tenured generation   total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)
      the space 69632K,   3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)
       compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
         the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
          ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
          rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
      34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:
       def new generation   total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)
      eden space 49152K,   0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)
        from space 6144K,  55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)
        to   space 6144K,   0% used [0x221d0000, 0x221d0000, 0x227d0000)
       tenured generation   total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)
      the space 69632K,   4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)
       compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
         the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
          ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
          rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
      }
      , 0.0757599 secs]
    • -Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析。
  4. 常见配置汇总
    1. 堆设置
      • -Xms:初始堆大小
      • -Xmx:最大堆大小
      • -XX:NewSize=n:设置年轻代大小
      • -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
      • -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
      • -XX:MaxPermSize=n:设置持久代大小
    2. 收集器设置
      • -XX:+UseSerialGC:设置串行收集器
      • -XX:+UseParallelGC:设置并行收集器
      • -XX:+UseParalledlOldGC:设置并行年老代收集器
      • -XX:+UseConcMarkSweepGC:设置并发收集器
    3. 垃圾回收统计信息
      • -XX:+PrintGC
      • -XX:+PrintGCDetails
      • -XX:+PrintGCTimeStamps
      • -Xloggc:filename
    4. 并行收集器设置
      • -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
      • -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
      • -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
    5. 并发收集器设置
      • -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
      • -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。


四、调优总结

  1. 年轻代大小选择
    • 响应时间优先的应用尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
    • 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
  2. 年老代大小选择
    • 响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
      • 并发垃圾收集信息
      • 持久代并发收集次数
      • 传统GC信息
      • 花在年轻代和年老代回收上的时间比例
      减少年轻代和年老代花费的时间,一般会提高应用的效率
    • 吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。
  3. 较小堆引起的碎片问题
    因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
    • -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
    • -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
posted @ 2012-12-11 10:28 昊天 阅读(352) | 评论 (0)编辑 收藏
/*==============================================================*/
/* 需要用user1账号执行下面的授权                                */
/*==============================================================*/ 
grant select any table to user;
grant create materialized view to user;
grant on commit refresh to user;  

/*==============================================================*/
/* 需要用sys账号执行下面的授权                                  */
/*==============================================================*/ 
grant exempt access policy to user;
grant exempt access policy to user;
grant exempt access policy to user;

drop materialized view log on AAA;
create materialized view log on AAA with primary key

drop materialized view AAA;
create materialized view AAA 
refresh fast 
on commit as 
select bindid, merchantid, userid, bindtime, bindtype, username, gsmsn
  
from AAA t;
  
drop materialized view log on DDD;
create materialized view log on DDD with primary key

drop materialized view BBB;
create materialized view  BBB
refresh fast 
on commit as 
select MERCHANTID,
       COMPANYNAME,
       ORGANIZATION
  
from DDD t; 
  
drop materialized view log on BBB;
drop materialized view log on CCC;
drop materialized view log on CCC;
create materialized view log on BBB WITH rowid ,SEQUENCE (userid,timinalid,ACCOUNTNO,IMSI,REGDATE,NICKNAME,SEX,INVALID,ISACTIVE,USERTYPE,STATUS,BIZTYPE,ACTIVEDATE,SIGNATURE,PHOTOID,CITYID,CARID,USERPHOTO,USERCARPHOTO,MOBILENO,EMAIL,LOGINDAYS,LASTLOGINDATE,USERSTATUS,LATELYCITY,LATELYCITYDATE) INCLUDING NEW VALUES;
create materialized view log on CCC WITH rowid ,SEQUENCE (IDENTIFYCARD,CARNUMBER,FDJNUMBER,FOURSSHOP,DELIVERDATE,BIRTHDATE,ADDRESS,MILEAGE,MILEAGETIME) INCLUDING NEW VALUES;
create materialized view log on CCC WITH rowid ,SEQUENCE (DEVICEID,IMSI,ISSUEMONTH,ICCID,SIMPHONE,GSMSN,DEVICESN,CURVER,DEVICETYPE,IS3GNET,IMSITYPE,TERMINALINFO,RATEID,CARDSTATUS) INCLUDING NEW VALUES;

drop materialized view DDD;
create materialized view DDD
refresh fast 
on commit as 
select a.rowid arowid,
       a.USERID,
       a.ACCOUNTNO,
       b.rowid browid,       
       b.USERNAME,
       c.rowid crowid,       
       c.DEVICEID 
  
from BBB a ,CCC b,CCC c 
 
where a.userid = b.userid(+
   
and a.timinalid = c.terminalid(+);
  
 
 
create index INDEX_DDD on DDD(merchantid)
  tablespace SSS
  pctfree 
10
  initrans 
2
  maxtrans 
255
  storage
  (
    initial 5M
    minextents 
5
    maxextents unlimited
  ); 
  
 
create index INDEX_AAA on AAA(userid)
  tablespace SSS
  pctfree 
10
  initrans 
2
  maxtrans 
255
  storage
  (
    initial 5M
    minextents 
5
    maxextents unlimited
  ); 
    
 
create index INDEX_BBB on BBB(merchantid)
  tablespace SSS
  pctfree 
10
  initrans 
2
  maxtrans 
255
  storage
  (
    initial 10M
    minextents 
5
    maxextents unlimited
  ); 
  

 
create index INDEX_CCC on CCC(userid)
  tablespace SSS
  pctfree 
10
  initrans 
2
  maxtrans 
255
  storage
  (
    initial 10M
    minextents 
5
    maxextents unlimited
  ); 
posted @ 2012-06-15 14:51 昊天 阅读(289) | 评论 (0)编辑 收藏

Apache的MINA是一个被用来构建高性能和高可伸缩性应用程序的网络应用框架,它提供了一套统一的建立在Java NIO之上的事件驱动的异步API。

对于MINA框架的了解,MINA官方的几篇文章是必须要看的,如下:

* Application Architecture http://mina.apache.org/mina-based-application-architecture.html
* Server Architecture http://mina.apache.org/server-architecture.html
* Client Architecture http://mina.apache.org/client-architecture.html

其中几个主要的组件如下:

I/O Service - 用来处理I/O流,对于Server端就是IOAcceptor的实现类接受Client端的连接请求,对于Client端就是IoConnector的实现类来建立到Server端的连接。

I/O Filter Chain - 用来过滤或转化数据。对于Server端和Client端来说都是IoFilter接口的实现类,MINA自己内建了很多IoFilter接口的实现类。具体可以参考官方文档。

I/O Handler - 用来处理真正业务逻辑的类。对于Server端和Client端来说都是IoHandler接口的实现类,通常来说需要自己编写。


由于Server端和Client端都是基于以上三个组件的,因此对于Server端和Client端编程来说就都有类似的代码结构。

对于Server端来说:

1. 创建I/O service - 这里就是创建IOAcceptor类监听端口。

2. 创建I/O Filter Chain - 这里就是告诉使用那些IoFilter。
3. 创建I/O Handler - 自己的业务逻辑。 

对于Client端来说:

1. 创建I/O service - 这里就是创建IOConnector类来建立到Server端的连接。

2. 创建I/O Filter Chain - 这里就是告诉使用那些IoFilter。

3. 创建I/O Handler - 自己的业务逻辑。

下面来通过一个例子看看MINA是怎样工作的。由于大多数应用都是基于TCP/IP的应用,所以这里也就不再说UDP/IP了。

这里我使用了Maven来创建了一个简单java应用程序,具体步骤请参Maven的官方手册。这里只是将我用到的maven配置文件pom.xml列出,方便下面及后续文章使用。具体pom.xml文件内容如下:

 

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
  3.     <modelVersion>4.0.0</modelVersion>  
  4.     <groupId>com.google.code.garbagecan.minastudy</groupId>  
  5.     <artifactId>minastudy</artifactId>  
  6.     <packaging>jar</packaging>  
  7.     <version>1.0-SNAPSHOT</version>  
  8.     <name>minastudy</name>  
  9.     <url>http://maven.apache.org</url>  
  10.     <dependencies>  
  11.         <dependency>  
  12.             <groupId>org.apache.mina</groupId>  
  13.             <artifactId>mina-core</artifactId>  
  14.             <version>2.0.4</version>  
  15.         </dependency>  
  16.         <dependency>  
  17.             <groupId>org.apache.mina</groupId>  
  18.             <artifactId>mina-filter-compression</artifactId>  
  19.             <version>2.0.4</version>  
  20.         </dependency>  
  21.         <dependency>  
  22.             <groupId>org.slf4j</groupId>  
  23.             <artifactId>slf4j-api</artifactId>  
  24.             <version>1.3.0</version>  
  25.         </dependency>  
  26.         <dependency>  
  27.             <groupId>org.slf4j</groupId>  
  28.             <artifactId>slf4j-log4j12</artifactId>  
  29.             <version>1.3.0</version>  
  30.         </dependency>  
  31.     </dependencies>  
  32. </project>  
首先来看Server端的代码

 

 

  1. package com.google.code.garbagecan.minastudy.sample1;  
  2.   
  3. import java.io.IOException;  
  4. import java.net.InetSocketAddress;  
  5. import java.nio.charset.Charset;  
  6.   
  7. import org.apache.mina.core.service.IoAcceptor;  
  8. import org.apache.mina.core.service.IoHandlerAdapter;  
  9. import org.apache.mina.core.session.IdleStatus;  
  10. import org.apache.mina.core.session.IoSession;  
  11. import org.apache.mina.filter.codec.ProtocolCodecFilter;  
  12. import org.apache.mina.filter.codec.textline.TextLineCodecFactory;  
  13. import org.apache.mina.filter.logging.LoggingFilter;  
  14. import org.apache.mina.transport.socket.nio.NioSocketAcceptor;  
  15. import org.slf4j.Logger;  
  16. import org.slf4j.LoggerFactory;  
  17.   
  18. public class MyServer {  
  19.       
  20.     private static final Logger logger = LoggerFactory.getLogger(MyServer.class);  
  21.       
  22.     public static void main(String[] args) {  
  23.         IoAcceptor acceptor = new NioSocketAcceptor();  
  24.   
  25.         acceptor.getFilterChain().addLast("logger"new LoggingFilter());  
  26.         acceptor.getFilterChain().addLast("codec"new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));  
  27.   
  28.         acceptor.setHandler(new IoHandlerAdapter() {  
  29.               
  30.             @Override  
  31.             public void sessionCreated(IoSession session) throws Exception {  
  32.             }  
  33.               
  34.             @Override  
  35.             public void sessionOpened(IoSession session) throws Exception {  
  36.             }  
  37.   
  38.             @Override  
  39.             public void sessionClosed(IoSession session) throws Exception {  
  40.             }  
  41.   
  42.             @Override  
  43.             public void sessionIdle(IoSession session, IdleStatus status) throws Exception {  
  44.             }  
  45.   
  46.             @Override  
  47.             public void exceptionCaught(IoSession session, Throwable cause) throws Exception {  
  48.                 logger.error(cause.getMessage(), cause);  
  49.                 session.close(true);  
  50.             }  
  51.   
  52.             @Override  
  53.             public void messageReceived(IoSession session, Object message) throws Exception {  
  54.                 logger.info("Received message " + message);  
  55.                 session.write(message);  
  56.             }  
  57.   
  58.             @Override  
  59.             public void messageSent(IoSession session, Object message) throws Exception {  
  60.                 logger.info("Sent message " + message);  
  61.             }  
  62.         });  
  63.           
  64.         try {  
  65.             acceptor.bind(new InetSocketAddress(10000));  
  66.         } catch (IOException ex) {  
  67.             logger.error(ex.getMessage(), ex);  
  68.         }  
  69.     }  
  70. }  
1. 首先创建I/O Service,这里使用的是NioSocketAcceptor类来创建了一个IoAcceptor实例。

 

2. 创建I/O Filter Chain,这里使用了两个IoFilter,一个是LoggingFilter用来记录日志和打印事件消息,另一个是ProtocolCodecFilter实例用来编码数据,这里其实就是将传递的数据编码成文本。

3. 创建I/O Handler,不要害怕,看起来代码多,其实就是一个实现了IoHandler接口的子类,自己需要实现其中的一些方法,这里方法比较多,但是我在这里只实现了messageSent,messageReceived和exceptionCaught方法。

4. 最后就是让IoAcceptor类实例绑定端口实现监听。

下面看看Client端的代码

  1. package com.google.code.garbagecan.minastudy.sample1;  
  2.   
  3. import java.net.InetSocketAddress;  
  4. import java.nio.charset.Charset;  
  5.   
  6. import org.apache.mina.core.RuntimeIoException;  
  7. import org.apache.mina.core.future.ConnectFuture;  
  8. import org.apache.mina.core.service.IoConnector;  
  9. import org.apache.mina.core.service.IoHandlerAdapter;  
  10. import org.apache.mina.core.session.IdleStatus;  
  11. import org.apache.mina.core.session.IoSession;  
  12. import org.apache.mina.filter.codec.ProtocolCodecFilter;  
  13. import org.apache.mina.filter.codec.textline.TextLineCodecFactory;  
  14. import org.apache.mina.filter.logging.LoggingFilter;  
  15. import org.apache.mina.transport.socket.nio.NioSocketConnector;  
  16. import org.slf4j.Logger;  
  17. import org.slf4j.LoggerFactory;  
  18.   
  19. public class MyClient {  
  20.       
  21.     private static final Logger logger = LoggerFactory.getLogger(MyClient.class);  
  22.       
  23.     public static void main(String[] args) {  
  24.         IoConnector connector = new NioSocketConnector();  
  25.         connector.setConnectTimeoutMillis(10 * 1000);  
  26.           
  27.         connector.getFilterChain().addLast("logger"new LoggingFilter());  
  28.         connector.getFilterChain().addLast("codec"new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));  
  29.           
  30.         connector.setHandler(new IoHandlerAdapter() {  
  31.               
  32.             @Override  
  33.             public void sessionCreated(IoSession session) throws Exception {  
  34.             }  
  35.   
  36.             @Override  
  37.             public void sessionOpened(IoSession session) throws Exception {  
  38.                 for (int i = 0; i < 10; i++) {  
  39.                     session.write("Hello user_" + i);  
  40.                 }  
  41.                 session.write("Bye");  
  42.             }  
  43.   
  44.             @Override  
  45.             public void sessionClosed(IoSession session) throws Exception {  
  46.             }  
  47.   
  48.             @Override  
  49.             public void sessionIdle(IoSession session, IdleStatus status) throws Exception {  
  50.             }  
  51.   
  52.             @Override  
  53.             public void exceptionCaught(IoSession session, Throwable cause) throws Exception {  
  54.                 logger.error(cause.getMessage(), cause);  
  55.                 session.close(true);  
  56.             }  
  57.   
  58.             @Override  
  59.             public void messageReceived(IoSession session, Object message) throws Exception {  
  60.                 logger.info("Received message " + message);  
  61.                 if (message.toString().equalsIgnoreCase("Bye")) {  
  62.                     session.close(true);  
  63.                 }  
  64.             }  
  65.   
  66.             @Override  
  67.             public void messageSent(IoSession session, Object message) throws Exception {  
  68.                 logger.info("Sent message " + message);  
  69.             }  
  70.         });  
  71.   
  72.         IoSession session = null;  
  73.         try {  
  74.             ConnectFuture future = connector.connect(new InetSocketAddress("localhost"10000));  
  75.             future.awaitUninterruptibly();  
  76.             session = future.getSession();  
  77.         } catch (RuntimeIoException e) {  
  78.             logger.error(e.getMessage(), e);  
  79.         }  
  80.   
  81.         session.getCloseFuture().awaitUninterruptibly();  
  82.         connector.dispose();  
  83.     }  
  84. }  

1. 首先创建I/O Service,这里使用的是NioSocketConnector类来创建了一个IoConnector实例,并设置连接超时为10秒。

2. 创建I/O Filter Chain,和服务器端同样设置了两个IoFilter,一个是LoggingFilter用来记录日志和打印事件消息,另一个是ProtocolCodecFilter实例用来编码数据,这里其实就是将传递的数据编码成文本。

3. 创建I/O Handler,也不要害怕,看起来代码多,其实也是一个实现了IoHandler接口的子类,并且自己实现了sessionOpened,messageSent,messageReceived和exceptionCaught方法。实现sessionOpened方法是为了在建立连接后向Server端发送消息。另外看一下messageReceived方法实现,在接收到服务器端的消息后关闭会话。从而可以使Client程序最终能够退出。

4. 最后就是IoConnector实例类连接远端的Server。

 

下面测试一下上面的程序,首先运行MyServer类,然后运行MyClient类,就可以分别在各自的终端上看到事件日志以及发送/接收的消息了。

posted @ 2012-06-12 16:30 昊天 阅读(803) | 评论 (0)编辑 收藏
ReentrantLock是jdk5引入的新的锁机制,它与内部锁(synchronize) 相同的并发性和内存语义,比如可重入加锁语义。在中等或者更高负荷下,ReentrantLock有更好的性能,并且拥有可轮询和可定时的请求锁等高级功能。这个程序简单对比了ReentrantLock公平锁、ReentrantLock非公平锁以及内部锁的性能,从结果上看,非公平的ReentrantLock表现最好。内部锁也仅仅是实现统计意义上的公平,结果也比公平的ReentrantLock好上很多。这个程序仅仅是计数,启动N个线程,对同一个Counter进行递增,显然,这个递增操作需要同步以保证原子性,采用不同的锁来实现同步,然后查看结果。
Counter接口:
package net.rubyeye.concurrency.chapter13;

public interface Counter {
    
public long getValue();

    
public void increment();

}

然后,首先使用我们熟悉的synchronize来实现同步:
package net.rubyeye.concurrency.chapter13;

public class SynchronizeBenchmark implements Counter {
    
private long count = 0;

    
public long getValue() {
        
return count;
    }

    
public synchronized void increment() {
        count
++;
    }
}

采用ReentrantLock的版本,切记要在finally中释放锁,这是与synchronize使用方式最大的不同,内部锁jvm会自动帮你释放锁,而ReentrantLock需要你自己来处理。
package net.rubyeye.concurrency.chapter13;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockBeanchmark implements Counter {

    
private volatile long count = 0;

    
private Lock lock;

    
public ReentrantLockBeanchmark() {
        
// 使用非公平锁,true就是公平锁
        lock = new ReentrantLock(false);
    }

    
public long getValue() {
        
// TODO Auto-generated method stub
        return count;
    }

    
public void increment() {
        lock.lock();
        
try {
            count
++;
        } 
finally {
            lock.unlock();
        }
    }

}

    写一个测试程序,使用CyclicBarrier来等待所有任务线程创建完毕以及所有任务线程计算完成,清单如下:
package net.rubyeye.concurrency.chapter13;

import java.util.concurrent.CyclicBarrier;

public class BenchmarkTest {
    
private Counter counter;

    
private CyclicBarrier barrier;

    
private int threadNum;

    
public BenchmarkTest(Counter counter, int threadNum) {
        
this.counter = counter;
        barrier 
= new CyclicBarrier(threadNum + 1); //关卡计数=线程数+1
        this.threadNum = threadNum;
    }

    
public static void main(String args[]) {
        
new BenchmarkTest(new SynchronizeBenchmark(), 5000).test();
       
//new BenchmarkTest(new ReentrantLockBeanchmark(), 5000).test();
        //new BenchmarkTest(new ReentrantLockBeanchmark(), 5000).test();   
    }

    
public void test() {
        
try {
            
for (int i = 0; i < threadNum; i++) {
                
new TestThread(counter).start();
            }
           
long start = System.currentTimeMillis();
            barrier.await(); // 等待所有任务线程创建
            barrier.await(); // 等待所有任务计算完成
            long end = System.currentTimeMillis();
            System.out.println(
"count value:" + counter.getValue());
            System.out.println(
"花费时间:" + (end - start) + "毫秒");
        } 
catch (Exception e) {
            
throw new RuntimeException(e);
        }
    }

    
class TestThread extends Thread {
        
private Counter counter;

        
public TestThread(final Counter counter) {
            
this.counter = counter;
        }

        
public void run() {
            
try {
                barrier.await();
                
for (int i = 0; i < 100; i++)
                    counter.increment();
                barrier.await();
            } 
catch (Exception e) {
                
throw new RuntimeException(e);
            }
        }
    }
}

分别测试一下,

将启动的线程数限定为500,结果为:
公平ReentrantLock:      210 毫秒
非公平ReentrantLock :   39  毫秒
内部锁:                          39 毫秒

将启动的线程数限定为1000,结果为:
公平ReentrantLock:      640 毫秒
非公平ReentrantLock :   81 毫秒
内部锁:                           60 毫秒

线程数不变,test方法中的循环增加到1000次,结果为:
公平ReentrantLock:      16715 毫秒
非公平ReentrantLock :   168 毫秒
内部锁:                           639  毫秒

将启动的线程数增加到2000,结果为:
公平ReentrantLock:      1100 毫秒
非公平ReentrantLock:   125 毫秒
内部锁:                           130 毫秒

将启动的线程数增加到3000,结果为:
公平ReentrantLock:      2461 毫秒
非公平ReentrantLock:   254 毫秒
内部锁:                           307 毫秒

启动5000个线程,结果如下:
公平ReentrantLock:      6154  毫秒
非公平ReentrantLock:   623   毫秒
内部锁:                           720 毫秒

非公平ReentrantLock和内部锁的差距,在jdk6上应该缩小了,据说jdk6的内部锁机制进行了调整。
posted @ 2012-04-12 10:31 昊天 阅读(521) | 评论 (1)编辑 收藏
仅列出标题
共7页: 上一页 1 2 3 4 5 6 7 下一页 

导航

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

统计

留言簿(1)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜