2010年4月1日星期四

Cassandra数据模型简介

Cassandra是一个分布式的key/value store,号称是第二代的分布式数据库,结合了Dynamo的全分布式设计和Bigtable的数据模型。Cassandra今年已经升为apache 的顶级项目,并且被众多技术爱好者频繁提及,确实值得关注一下。先看一下Cassandra官方的overview:

  • Proven: Digg,facebook,Twitter在使用
  • Fault Tolerant:数据自动replicated到多个节点
  • decentralized: 所有节点都是相同的
  • you're in control:可以为每次的update选择同步或异步的复制
  • Rich Data Model:不只是简单的key/value
  • Elastic:线性的扩展
  • Highly available:可调节的一致模型,从"writes never fail" -> "block for all replicates to be readable"

其他特性不必说了,分布式key/value居家必备。我最关注的是Rich Data Model,就像它自己强调的,不再是简单的Key/Value了。

浏览了相关的文档,我可以大致的描述一下。Cassandra的数据 模型可以被简单的认为是4或5维的hash。我们从下往上介绍:

最基础的是Column,由 name,value,timestamp组成,例如
{  
    name: "emailAddress",
    value: "arin@example.com",
    timestamp: 123456789
}

SuperColumn, 顾名思义,SuperColumn可以包含多个column。SuperColumn是可选的,你可以只使用最简单的column。
{   
    name: "homeAddress", // SuperColumn name    
    value: { // value就是多个column的list        
        street: {name: "street", value: "1234 x street", timestamp: 123456789},
        city: {name: "city", value: "san francisco", timestamp: 123456789},
        zip: {name: "zip", value: "94107", timestamp: 123456789},
    }
}

ColumnFamily,column容器,类似数据库中表的概念。
ColumnFamily 中的每一行由key和column list组成。和数据表的最大区别是没有预定义的schema,每一行可以包含任意多个column。

UserProfile = { // this is a ColumnFamily

    phatduckk: {   // key
        // 下面是column list
        username: "phatduckk",
        email: "phatduckk@example.com",
        phone: "(900) 976-6666"
    },
    ieure: {   // key
        // 下面是column list
        username: "ieure",
        email: "ieure@example.com",
        phone: "(888) 555-1212"
        age: "66",
        gender: "undecided"
    },
}

在 Cassandra中,一个key甚至可以关联多个“表”中的同一行:

{
   "mccv":{ // key
      "Users":{ // Columnfamily,用户“表”
     // column list
         "emailAddress":{"name":"emailAddress", "value":"foo@bar.com"},
         "webSite":{"name":"webSite", "value":"http://bar.com"}
      },
      "Stats":{ // Columnfamily,状态“表”
     // column list
         "visits":{"name":"visits", "value":"243"}
      }
   }   
}

另外一个很重要的特性,同一行的column是按照column name排序的,注意,是用column name排序!Cassandra支持几种排序方式,BytesType, UTF8Type, LexicalUUIDType, TimeUUIDType, AsciiType, and LongType.在数据存储的时候已经进行了排序,所以当通过key取出column时是有序的。

key {
    {name: 3, value: "101010101010"},
    {name: 123, value: "hello there"},
    {name: 976, value: "kjjkbcjkcbbd"},
    {name: 832416, value: "kjjkbcjkcbbd"}
}
这个例子使用了LongType排序类型,按照name的升序排序。 这个特性非常有用,可以想象如果使用TimeUUIDType类型,所有的column都会按照时间排序。

Keyspaces,column families容器,对应于数据库概念的database,一般是每个应用配置一个Keyspace。

最上层是Cluster, 代表一个Cassandra实例。Cluster可以包含多个 keysapces。

简单的概括一下:
  1. column: a name,value and a timestamp
  2. SuperColumns:包含多个子columns
  3. ColumnFamilies: “表”,包含column和superColumn
  4. keyspace:“数据库”,包含多个ColumnFamilies,一般是每个 应用一个
  5. cluster: 逻辑实例节点,包含多个 keysapces


介绍完概念我们看一个具 体的例子是如何使用这种数据模型的。我参考的是Digg工程师写的一篇介绍,并进行了简化,感觉这样更能突出Cassandra数据模型的特点。完整的描 述可以参考后面的reference,里面还包括了一篇介绍Twitter是如何使用Cassandra数据模型的。

设想一个简单的 blog系统,可以为每篇blog指定多个tag,并可以按照时间逆序浏览每个tag的blog列表,当然也可以按照时间浏览所有blog。

首先定义一个BlogEntries ColumnFamily,标题作为key,一行代表一篇blog,用逗号分隔表示多个tag,很好理解:

    BlogEntries : { // ColumnFamily

        i-got-a-new-guitar : { // key,blog标题
            title: This is a blog entry about my new, awesome guitar,
            body: this is a cool entry. etc etc yada yada
            author: Arin Sarkissian  
            tags: life,guitar,music  // 逗号分隔的多个tag
            pubDate: 1250558004      
            slug: i-got-a-new-guitar
        },
        // 其他blog列表
        another-cool-guitar : {
            ...
            tags: guitar,
            slug: another-cool-guitar
        },
        scream-is-the-best-movie-ever : {
            ...
            tags: movie,horror,
            slug: scream-is-the-best-movie-ever
        }
    }

再来看看Tag“表”怎么定义,把tag 作为key,每一行包含了所有标记了这个tag的blog list。Cassandra中的column不像在数据库中,不需要预先定义。

    TaggedPosts : { // CF
        // 标记了guitar tag的blog
        guitar : {  // key,tag name
            // 列名是TimeUUIDType类型,value对应的是BlogEntries中的key
            timeuuid_1 : i-got-a-new-guitar,
            timeuuid_2 : another-cool-guitar,
        },
        // 所有blog都列在这个特殊的tag下
        __notag__ : {
            timeuuid_1b : i-got-a-new-guitar,

            timeuuid_2b : another-cool-guitar,

            timeuuid_2b : scream-is-the-best-movie-ever,
        },
        // 标记了movie tag的blog
        movie: {
            timeuuid_1c: scream-is-the-best-movie-ever
        }
    }

我们把列名定义为TimeUUIDType类型。记得前面提 到过,Cassandra是按照列名来排序的,TimeUUIDType就是按照时间来排序的。那么我们通过tag取出的blog列表自然就是按照时间排序的了。Cassandra的api支持根据key和范围条件获取一部分的column list,类似数据库的分页,而且取出来的列都是有序的。定义了两个简单的ColumnFamily,干净漂亮的完成了需求。

可以看 出,Cassandra已经超越了简单的key/value,数据模型是相当的灵活。其核心可以概括为两句话:
1. 由key指明的每一行可以包含任意多的column
2. 每一行内的column是按照name排序的

Reference
cassandra 项目主页
http://cassandra.apache.org/
DataModel
http://wiki.apache.org/cassandra/DataModel
API
http://wiki.apache.org/cassandra/API
Digg 工程师写的DataModel介绍
http://arin.me/blog/wtf-is-a-supercolumn-cassandra-data-model
Twitter 的DataModel介绍
http://blog.evanweaver.com/articles/2009/07/06/up-and-running-with-cassandra/
B2B DBA写的一篇介绍
http://www.hellodba.net/2010/02/cassandra.html
江 枫写的两篇介绍
Cassandra存储机制 http://www.ningoo.net/html/2010/cassandra_storage.html
Cassandra Commitlog http://www.ningoo.net/html/2010/cassandra_commitlog.html
Yahoo针对多个Key/Value系统的比较测试
http://www.brianfrankcooper.net/pubs/ycsb-v4.pdf

2010年3月5日星期五

淘宝年会随感

参加完淘宝的年会,心里产生了一个想法,淘宝和当前中国的状况真有几分神似。都是在高速发展中,成绩被外人所惊叹,但同时又存在很多问题,批评者都会指出,如果这样或那样的问题不解决,将导致最终崩溃。而淘宝提出的解决方案也和国家一样,发展才是硬道理,不是停下来解决问题,而是每年以1倍的速度高速成长,问题在成长中摸索着解决。就像中国的GDP,不敢停下来。我不是想评论公司或国家的策略,而是想到,这种以动态发展的眼光去看待遇到的困难,是我们很多人都不具备的。我们常会停下来抱怨,而不知道迎着困难努力前行,战略上藐视困难,发展中解决问题。

马云说,上网是整体素质偏高的人群,但在网上发表评论的又是素质最低的一群人。你做任何事情,都会有人骂你。他也许是被骂的太多了。老毛大概也是这个原因,不待见知识分子。这个世界不缺有想法的人,但缺少为想法付诸实际行动的人。坐而论道,不如退而结网(韩式语录)。刚上推特时对政治话题感兴趣的不得了,哇塞,民主了,解放了。看多了我就郁闷了。似乎发表悲观的论调更容易吸引听众,股市暴跌,楼市崩盘,中国崩溃。冷嘲热讽看多了都已经变成麻木。而且所谓的民运圈为了一点利益就在推特上吵架,还好我比较厚道又喜欢看八卦。
任何善意的、正面的想法,都被现实击的粉碎。不希望去关注政治,以正面的眼光去看待可能发生的积极变化。但谷歌的离去,还是让我愤怒。

屁股决定脑袋,我从马云的讲话里,听到的都是道理。

2010年2月28日星期日

吞吐量和响应性的思考

系统的性能可以从吞吐量和响应性这两个方面考察。吞吐量衡量的是“有多少”,即限定资源的情况下,能够完成多少工作。响应性衡量的是“有多快”,即单个任务的处理时间。可以说吞吐量越大,响应越快,就表示系统的性能越好。
性能的这两个方面是完全分离的,有时候甚至是矛盾的。有人说软件设计就是权衡,其实我们能在很多地方看到对吞吐量和响应性的取舍。

1. Java gc算法的选择。Java -server缺省的gc算法是Parallel Collector,能够提高吞吐量,但是当你对响应时间更看重的时候,就需要选择Concurent Mark-Sweep(CMS)。

2. 线程上下文的切换。OS在线程上下文切换的时候会有系统开销,调度程序会为每一个可运行的线程分配一个最小执行时间,这样做是为了偿还线程切换的开销,获得更多不中断的执行时间,从整体上提高吞吐量,但这是以牺牲响应性为代价的。

3. Real-Time Java
实时系统,简单的说就是,能够在指定或者确定的时间内完成任务。系统的正确性不仅取决于系统计算结果的正确性.而且取决于正确结果产生的时间。
● “When” is as important as “what”
● “A late answer is a wrong answer”
real-time并不意味着一定比非实时“快”,而是强调执行时间的可预测性。普通Java不能做到实时,GC暂停是一个很重要的原因,因为这是开发人员无法控制和预测的因素。因此在Real-Time Java中,对GC的选择就是完全偏重于响应性。我们可以看一个对比:

对于SIP(Session Initiation Protocol) Server,实时系统的吞吐量比普通系统小,但对请求处理的延迟时间小,延迟大于50ms的只占0.3%。

2010年2月26日星期五

直击心灵的文字

很喜欢李笑来的blog http://www.lixiaolai.com/,并强烈推荐他的书《把时间当作朋友》,正如副标题所说的,“运用心智获得解放”。

阅读他的文字,常有直击心灵的感觉。一层层的拨开事物表面的掩盖,让你直接看到事物的本质。平时太熟悉视而不见的问题,突然放在了眼前,让你真正的正视自己的时候,心弦为之一动。听到这么直接的话评判自身的问题,真的不太好受。例如这篇《效率低下的根源——回避困难》,一句话就点醒了自己,我何尝不是这样呢。只有直面自己的缺点,才有机会改正。做个明白人。

西安网络购物状况感受

过年回西安,从亲戚那儿了解到他们网络购物的使用情况,觉得比较有代表性,因此记录一下:
1. 我姐,中学教师,在西安收入中等偏上,最近刚从京东买了一台4000元的HP笔记本。原来是偶然逛淘宝,后来听我说京东买电器比较专业,尝试着买过耳机,充电电池等小东西,感觉不错,这次就大胆的买了一个笔记本电脑。用我姐夫的话,去电子市场也不太懂,逛的头晕脑胀,不如在网上,即可以仔细比较,还可以了解到真正的热门机型。只要是正品,还是网上买方便。
2. 表弟,室内装修设计。自己只在网上买过书,不太爱逛淘宝,理由是嫌麻烦。他的女同学比较爱逛淘宝,买些便宜的衣服。也有同学在淘宝开店,据说做的比较成功。
3. 表妹,电视台记者。没有网络购物经历,一说到淘宝就想到央视上曝光的在淘宝上卖翻新手机事件。到底是做电视栏目的,对这种题材很敏感。很想从我这了解网店的具体运作,还有网络作为新媒体对她所从事的传统媒体的影响。说实在的,我在这些方面也是小白。我只能告诉她淘宝其实也是个媒体,主要的收入也来自于广告。
4. 淘宝的知名度很大,专业B2C的网站很多人都没有听说过。但B2C的用户体验明显好过淘宝,一旦使用后,忠诚度较高。

英语词典的选择

一般阅读文章使用网易有道词典就够用了,但要想学好英语,需要用到很专业的词典。从李笑来老师的这篇blog可以看出他所选择的词典:
Collins COBUILD Advanced Learner’s English Dictionary
Longman Dictionary of Contemporary English
Oxford Advanced Learner’s English-Chinese Dictionary
Oxford Collocation Dictionary for Students of English
The Merriam-Webster Collegiate Dictionary
WordNet English Dictionary

另外,李笑来老师非常推崇柯林斯(Collins)英语词典和《Collins COBUILD English Grammar》。为啥?Collins词典使用了“if...”的简单句型,最适合第二语言使用者的解释方式;而且它是市面上唯一一个自带可查询语料库的词典。

《Collins Cobuild Dictionary on CD-ROM ver 3.1》
http://www.lixiaolai.com/index.php/archives/7759.html

推荐另一本好语法书:《Collins COBUILD英语语法大全》
http://www.lixiaolai.com/index.php/archives/5610.html

笑来老师还将语法书提取了出来供大家下载
http://www.lixiaolai.com/index.php/archives/4668.html

柯林斯词典可以在verycd下载 http://www.verycd.com/topics/135421/

那到底用什么字典软件学习英语好呢?目前看Lingoes才是王道,搭配上笑来推荐的柯林斯加牛津,无敌了。同样的牛津词典,金山词霸牛津版要卖210。

2010年2月13日星期六

异步数据库driver试用

我们知道,目前的JDBC driver是同步工作方式,也就是发送请求后需要block等待响应。而且connection上的操作都是串行化的,在一个请求处理没有完成之前,后续的请求都必须等待。因此,在处理大并发量访问数据库时,都会应用到连接池技术,实现连接复用,避免连接频繁打开关闭这种不必要的开销,而且复用后不会创建太多的连接。但即便是用了连接池,在客户端越来越多的情况下,对于数据库来说,连接还是太多了。
实际上这种同步串行化的操作风格是对连接的浪费,每个物理连接并没有得到充分的利用,特别是在大并发访问数据库的情况下。面对这种场景很自然的想到,如果有异步的JDBC driver,所有的请求都可以并行发送处理,这样就可以充分利用单个物理连接,避免建立过多连接的情况。


adbcj(http://code.google.com/p/adbcj/)定义了一套异步访问数据库的API,并用两种方式实现了这个API。
1. 使用threadpool包装现有的MySQL connector
2. 基于MINA实现的native driver
第一种方式使用的是现有的MySQL connector,虽然用threadpool包装后达到异步性,但底层的connection对操作还是串行化处理,性能并没有本质的改变,只是用来展示如何实现ADBCJ。
第二种实现方式不仅对调用者表现为异步性,底层的connection对请求也是并行发送,最大限度的利用了物理连接资源。
目前该项目还处在试验阶段,native driver只支持int和varchar类型。

作者的blog列出了他测试对比的结果 http://swamp.homelinux.net/blog/index.php/2009/11/25/performance-comparison-of-apache-mina-and-jboss-netty-revisited/,可以看出adbcj的性能优势。但作者只测试了query,ResultSet是通过callback通知。我比较关心的是insert,而且希望通过feature在调用线程直接得到操作的结果。

callback方式:
                final CountDownLatch latch = new CountDownLatch(count);
        final DbListener listener = new DbListener() {
            public void onCompletion(DbFuture resultSetDbFuture) throws Exception {
                latch.countDown();
                resultSetDbFuture.get();
            }
        };
        for (int i = 0; i < count; i++) {
            connection.executeQuery(query).addListener(listener);
        }

通过feature拿到结果:
            DbSessionFuture resultFuture = session.executeUpdate(sql);
            Result result = resultFuture.get();
            long affected = result.getAffectedRows();
            if (affected != 1) {
                System.out.println("插入数据库失败");
            }

我也做了一个简单的测试,对比adbcj和使用线程池的情况。首先创建一个表,只包含int和varchar:
CREATE TABLE `adbcjtest` (
`id` bigint(64) unsigned NOT NULL auto_increment,
 
`topic` varchar(32) NOT NULL,
 
`body` varchar(4000) NOT NULL,
  PRIMARY KEY  (`id`)
)

测试的场景有:
1. 10个线程,单个adbcj连接 VS. 10个线程,JDBC连接池大小为10。插入1000万条记录
2. 50个线程,单个adbcj连接 VS. 50个线程,JDBC连接池大小为10。插入1000万条记录
3. 100个线程,单个adbcj连接 VS. 100个线程,JDBC连接池大小为10。插入1000万条记录

客户端为Linux ,4核,4G内存,MySQL Server为Linux,8核,16G内存。
测试的对比结果,纵轴为TPS:






可以看出,在并发小的场景下,adbcj并没有优势。并发线程增加后,adbcj的性能就超过了使用JDBC连接池。同时注意到,所有的测试场景adbcj只使用了单个连接。

我们还可以对比测试过程中客户端的load情况:

第一种场景:








第二个测试场景:




第三个测试场景:








所有的测试场景,adbcj的load都低于JDBC连接池。


所以,异步的数据库访问方式值得关注,它能够很大程度上的提高连接的利用率。目前MySQL官方没有提供Java异步方式的驱动,而adbcj发展有些缓慢,也许有必要自己实现一个。

另外,我浏览了一下MySQL的通信协议:http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol,发现MySQL的请求命令都没有sequence number,因此请求并不是完全并行处理。从adbcj的代码也可以看出来,需要用一个activeRequest标识当前请求,请求并行处理程度并没有想象中那么高。

2010年2月10日星期三

《怪诞行为学》书评

传统经济学认为人都是理性的,会根据自己得到的所有信息,做出最合理的决定,以便最大化自己的利益。但本书阐述的是,在很多情况下,人们常常做出非理性的决策,而这种非理性的行为又不是随意做出的,而是有规律可循。正像这本书名的直译:可预期的非理性。这个理论是对传统经济学的补充,是当前很流行的行为经济学。作者因为他的特殊经历而具有非凡的洞察力:在他18岁时由于意外全身烧伤,在医院治疗了3年,在住院期间他因为孤独而常常以旁观者的视角去观察世界,各种人的行为,也包括他自己。这本书不是一本理论著作,读想起相当轻松,每章都描述了一个人们日常生活中常见的非理性行为,然后通过实验揭示这种行为背后的真实原因。因此,行为经济学也称作实验经济学。看着作者构思出一个个巧妙的实验场景,然后一步步得出结论,真的非常有趣。例如为了验证人们是否诚实,他在大学宿舍的公共冰箱中偷偷放了一箱可乐和一些零钱,几天以后去观察,可乐都被拿光了,而钱一分也没有被拿走。从这个现象得出的结论是:离现金一步之遥的作弊最容易发生,因为人们根本就没有意识到自己在作弊。对于非现金的事物作弊,我们擅长于把自己细微的不诚实想法和做法合理化。这大概就是孔乙己说的:窃书不算偷。

读完这本书,不只是了解了行为经济学的知识,更重要的是我们认真的审视了自己的非理性行为,并用理性的眼光看待他们,知道这些行为背后的真实规律。当你下次再在两个功能价钱相似的相机前选择很久而不能下决定的时候,你就会想起那头在两堆谷草之间饿死的驴。心里感叹,人就是这样,竭尽全力的想保持所有选择的可能性,而忘记了真正重要的事情。

2010年2月9日星期二

目标的重要性

一口吃不了个胖子,但很多人就此放弃了吃胖子。


目标对人非常重要,人有了目标,都会尽自己的所能,去完成它,在目标的指引下可以发挥出自己最大的能量。但没有明确目标的时候怎么办呢?需要自己给自己设定目标。
设定目标,然后把它分解,一步步的完成,最终达到目标,是多么一种难得的能力呀。

人很容易因为眼前的诱惑而放弃长远目标,这就是拖沓的原因。人们都希望自己的努力马上能看到成果,这是人的天性使然。所以有些人喜欢赌博,不管输还是赢,马上能看到结果。实际上,通往成功的道路是漫长和艰辛的,成功不可能一蹴而就,推迟满足感,耐得住寂寞,用无比的毅力和耐心去实现自己的目标。认识到人天性上的弱点,就要运用心智力量去改变它。一切的成果都是耐心的成果,而我们需要有一个远大的目标在前面指引,时刻提醒自己,不受眼前的诱惑影响,依靠信念的力量一路前行,最终达到目标。

现在,去设想你十年后成为怎样一个人,把它铭记在心,然后落实到每年、每个季度、每个月、每周和每天的行动中,持之以恒,还有什么做不到的事呢。


说了这么多仿佛都是大道理,但却是我真实的感受,那种经历过失败后沉思的顿悟,那种看着蹉跎岁月无情流逝的刻骨铭心的痛楚。就像别人没办法代替你自己思考,我说出来的感觉别人也体会不到。我们可以看到“过来人”的苦口婆心一般都是白费,只有自己经历了才能深切体会。但我也不想放弃这种“说教”,真的,成功者都是很早就明白这个道理的人。想想如果十年前你就设定了一个你目前都无法企及的目标并持之以恒的去努力,现在的你又是一个什么样子呢?现在也不晚,你能下决心耐得住下个十年的寂寞去完成你的目标吗?共勉吧。

2010年2月2日星期二

如何禁止CNNIC根证书

CNNIC根证书的危害性和如何在Firefox下禁止请参考AutoProxy作者所写的《CNNIC CA:最最最严重安全警告!

下面说一下如何在Windows下IE/Chrome禁用CNNIC根证书:
运行certmgr.msc,第三方根证书颁发机构-CNNIC(及Entrust),详细信息-编辑属性-证书目的-禁用此证书的所有目的。
如果找不到对应的证书,先访问一下:https://tns-fsverify.cnnic.cn/https://www.enum.cn/cn/
设置完成后,请务必用这两个地址验证。

2010年1月29日星期五

Java中无符号数的处理

Java中int,short,byte都是有符号的,如果想处理无符号数,只有一个办法,用容量更大的类型替换,例如unsigned short就用int。唯一的例外是char,它本身就是无符号的,所以可以用char作为unsigned short。

那么从一个byte[]中怎么读取一个有符号的数呢?以unsigned short为例:
int unsignedShort ;
byte[]  buf = getFromSomePlace();
int b1 = buf[0];
int b2 = buf[1];

unsignedShort = (((b1 & 0xff) << 8) | (b2 & 0xff))

那么,((b1 & 0xff) << 8)是什么意思呢?
首先, &  操作符会自动提升byte到int。
然后看b1&0xff的含义。大于2^15-1的无符号short,用两位byte表示,最高位为1。因为Java中都是有符号的数,最高位是符号位,为1表示负数。当byte扩充为int时,扩充位全都会变成1,b1 & 0xff就是将高位的1去掉。
最后是位移8位,和下一个byte拼接成unsigned short。

对于unsigned short,其实还可以用char存储:

char unsignedShort = (char) (b1 << 8 | b2);

我们去查看DataInput的javadoc,readUnsignedShort就是这样的。那位同学说了,我怎么去看DataInputStream的实现不是这样的,
public final int readUnsignedShort() throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (ch1 << 8) + (ch2 << 0);
}

没有什么&0xff。问题就在in.read()。InputStream的read方法返回的是int,从api描述可以看出:
Reads the next byte of data from the input stream. The value byte is returned as an int in the range 0 to 255.
可以看出,实际上返回的是一个无符号的byte,内部已经做了转换。我们可以看一个具体的实现,ByteArrayInputStream.read()
public int read() throws IOException {
        if (closed) {
            throw new IOException("Stream closed");
        } else if (index >= limit) {
            return -1; // EOF
        } else {
            return buffer[index++] & 0xff;
        }
}

也就是说,Java中没有unsigned byte,InputStreamde read方法都统一返回int,所以DataInputStream中就不用再转换了。

2010年1月18日星期一

bash中的位置参数和特殊参数

$1,$2 ... $9
通过位置参数引用命令行中的参数。如果需要引用9个之后的参数,必须用到大括号:${10}。

$0
命令的名字

$$
shell把执行shell进程的PID存储在$$中。

$!
后台运行的进程的PID

$?
上一个命令的返回状态码

$#
除了命令自身,命令行参数的个数

$*
包含所有的命令行参数。只产生一个参数

$@
包含所有的命令行参数。生成的是一串参数,其中每个位置的参数仍然是一个单独的参数。

$*和$@的对比
首先了解一下set的含义。set命令吧set后面的一个或几个参数赋值给位置参数。例如脚本a.sh
set this is it
echo $3 $2 $1
运行脚本./a.sh
it is this
不带任何参数运行set,会显示所有设置好的shell变量

然后看下面两个脚本
a.sh
set "$*"
echo 1: $1
echo 2: $2
echo 3: $3
运行命令./a.sh a b c 的结果是:
1: a b c
2:
3:

b.sh
set "$@"
echo 1: $1
echo 2: $2
echo 3: $3
运行命令./b.sh a b c 的结果是:
1: a
2: b
3: c

因此在shell脚本中$@用的更广泛一些。

2010年1月17日星期日

2>&1是什么意思?

shell脚本中经常看到这样的写法,2>&1,很诡异,反正我第一次看到是一头雾水。但只要明白了数字的含义,一切问题都迎刃而解。
当运行一个程序时,运行该程序的进程会打开3个文件描述符,分别是:0(标准输入),1(标准输出)和2(标准错误输出)。>是重定向符号。那么 2>&1的含义就是将标准错误输出重定向到标准输出。

经常我们还会有这样的写法
command >/dev/null 2>&1 &
哇,整个世界都清净了。

shell的初始化文件

shell启动时会运行初始化文件。但是系统中会同时存在多个初始化文件,/etc/profile,.bash_profile,/ect/bashrc,.bashrc 等等,到底是使用哪一个呢?

首先要了解shell的三种运行模式,登录shell,非登录交互式shell和非交互式shell。针对每一种类型,初始化时用到的文件会不同。
1. 登录shell
/etc/profile,所用用户都会先执行这个文件
然后依次查找 ~/.bash_profile ~/.bash_login, ~/.profile。可以覆盖掉/etc/profile中的默认配置
注销时会执行 ~/.bash_logout

2. 非登录交互式shell(比如通过命令bash)
交互式非登录shell并不执行1中提到的初始化文件,但是它会从登录shell继承由这些初始化文件设置的shell变量。
/etc/bashrc 全局配置,不会被自动调用。
.bashrc 一般在这个文件中会调用/etc/bashrc,好设置全局环境。 .bash_profile通常会运行这个文件,这样,登录shell和非登录shell都可以使用.bashrc中的命令。

3. 非交互式shell(用来运行shell脚本)
非交互式shell并不执行前面描述的初始化文件,但是它会从登录shell那里继承设置好的shell变量。
非交互式shell查找环境变量BASH_ENV,并执行有该变量命名的文件中的命令

.bashrc可能被执行多次,所以最好将那些附加已有变量的命令放到.bash_profile中,例如
PATH=$PASH:$HOME/bin

修改了初始化文件后,可以使用内置命令"."或source使修改生效,这两个命令将脚本作为当前进程的一部分运行。也可以使用它们运行其他脚本,但可能会修改用户依赖的shell变量。如果不是用"."或source运行初始化脚本,那么这些启动脚本中创建的变量将之灾运行该脚本的子shell中起作用,并不影响到启动该脚本的shell。

2010年1月15日星期五

推荐图形化的sar工具 kSar

sar是Linux下最常用的系统信息查看和收集工具,而kSar 是Java开发的一个应用,利用sar输出的文本信息生成图形。kSar有两种使用方式:
1. 从文件装载sar文本数据
2. 利用ssh在远程目标机器上执行sar命令,实时监控,最后再保存为图形

第二种方式非常有用,在压测时可以替换LoadRunner来监控系统负载和其他更多信息,完全根据你设置的sar参数。例如我使用的是sar -q -u -d -n DEV -r 30 10000,每30秒收集一次load,cpu,硬盘,网卡和内存信息。


2010年1月13日星期三

shell脚本中cd `dirname $0` 是什么意思

1. dirname - strip non-directory suffix from file name,翻译成白话就是,从输入的字符串中将目录截取出来,它不会去检查目录是否存在。
例如,dirname /a/b/c/d ,运行结果是/a/b/c

2. `command` ,将命令的执行结果作为字符串返回。也可以写成$(command)

3. $0,命令行的第一个参数,代表命令本身

4. dirname $0,将命令的路径截取出来

5. `dirname $0`,返回命令的路径

6. cd `dirname $0`,进入命令所在的目录

那位兄弟说了,pwd不是很简单,干嘛写的这么复杂dirname $0。

pwd取的当前路径,反应的是你在什么路径下执行命令,和我们想取得命令所在的目录还是有很大区别。

例如我们有个脚本/home/admin/bin/a.sh
echo $(dirname $0) echo $(pwd)

在/home/admin/bin下执行
./a.sh
结果是:
.
/home/admin/bin
这两个结果都是正确的。

在/home/admin目录下执行
bin/a.sh
结果是:
bin
/home/admin
可以看到区别了。不管在什么目录下执行命令,$(dirname $0)总是能命令所在的路径。

如何在SecureCRT中使用Emacs的metakey

Emacs的metakey键是alt,但alt同样是SecureCRT的快捷键,纠结呀

在命令行工作,缺省绑定的就是Emacs模式,很多组合键用不了,杯具呀

解决方法,在session选项中选择Emacs,设置alt为metakey。没有在全局选项中找到这个设置,每个session都要配置,忍了...



那SecureCRT的快捷键怎么办?

如果要访问菜单,例如File,View等,可以“Alt and release” + method 代替Alt+ method。我最常使用的快捷键是在tab中新建连接,原先是Alt+b,现在变成了Alt and release + f + b,还好能接受。

另一种方式就是重新映射快捷键。

在这个帖子中有SecureCRT的技术支持回答了这个问题。