使用Python的Pexpect模块执行SSH登陆

Pexpect 的使用范围很广,可以用来实现与 ssh, ftp , telnet 等程序的自动交互;可以用来自动复制软件安装包并在不同机器自动安装;还可以用来实现软件测试中与命令行交互的自动化。

下载安装:

download pexpect-2.3.tar.gz
tar zxvf pexpect-2.3.tar.gz
cd pexpect-2.3
python setup.py install (do this as root)

其实这个过程一点都不难,唯一的问题就是登陆之后终端尺寸的大小(我理解是buffer),因为Pexpect在代码里面有hardcode,把终端尺寸默认设成(24, 80)了。所以你连上后打开emacs会发现,emacs只占据了整个终端的一小部分。
Continue reading

优化 MySQL 语句的十个建议

原文链接:20bits.com   编译:伯乐在线?- 唐小娟

Jaslabs的Justin Silverton列出了十条有关优化MySQL查询的语句,我不得不对此发表言论,因为这个清单非常非常糟糕。另外一个Mike也同样意识到了。所以在这个博客中,我要做两件事情,第一,指出为什么这个清单很糟糕,第二,列出我的清单,希望我的比较好些。继续看吧,无畏的读者们!

(译者注:作者借这个题目反讽另一篇同名的文章)

为什么那个清单很糟糕

1.他的力气没使对地方

我们要遵循的一个准则就是如果你要优化代码时,应该先找出瓶颈在哪。然而Silverton先生的力气没有用对地方。我认为60%的优化是基于清楚 理解SQL和数据库基础的。你需要知道join和子查询的区别,列索引,以及如何将数据规范化等等。另外的35%的优化是需要清楚数据库选择时的性能表 现,例如COUNT(*)可能很快也可能很慢,要看你选用什么数据库引擎。还有一些其他要考虑的因素,例如数据库在什么时候不用缓存,什么时候存在硬盘上 而不存在内存中,什么时候数据库创建临时表等等。剩下的5%就很少会有人碰到了,但Silverton先生恰好在这上面花了大量的时间。我从来就没用过 SQL_SAMLL_RESULT。

2.很好的问题,但是很糟糕的解决方法

Silverton先生提出了一些很好的问题。MySQL针对长度可变的列如TEXT或BLOB,将会使用动态行格式(dynamic row format),这意味着排序将在硬盘上进行。我们的方法不是要回避这些数据类型,而是将这些数据类型从原来的表中分离开,放入另外一个表中。下面的 schema可以说明这个想法:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE posts (
     id int UNSIGNED NOT NULL AUTO_INCREMENT,
     author_id int UNSIGNED NOT NULL,
     created timestamp NOT NULL,
     PRIMARY KEY(id)
);
CREATE TABLE posts_data (
     post_id int UNSIGNED NOT NULL.
     body text,
     PRIMARY KEY(post_id)
);

3. 有点匪夷所思……

他的许多建议都是让人非常吃惊的,譬如“移除不必要的括号”。你这样写SELECT * FROM posts WHERE (author_id = 5 AND published = 1),还是这样写SELECT * FROM posts WHERE author_id = 5 AND published = 1 ,都不重要。任何比较好的DBMS都会自动进行识别做出处理。这种细节就好像C语言中是i++快些还是++i快些。真的,如果你把精力都花在这上面了,那就不用写代码了。

mysql

我的列表

看看我的列表是不是更好吧。我先从最普遍的开始。

1.建立基准,建立基准,建立基准!

如果需要做决定的话,我们需要数据说话。什么样的查询是最糟的?瓶颈在哪?我什么情况下会写出糟糕的查询?基准测试可以让你模拟高压情况,然后借助 性能测评工具,可以让你发现数据库配置中的错误。这样的工具有supersmack, ab, SysBench。这些工具可以直接测试你的数据库(譬如supersmack),或者模拟网络流量(譬如ab)。

2.性能测试,性能测试,性能测试!

那么,当你能够建立一些高压情况之后,你需要找出配置中的错误。这就是性能测评工具可以帮你做的了。它可以帮你发现配置中的瓶颈,不论是在内存中,CPU中,网络中,硬盘I/O,或者是以上皆有。

你要做的第一件事就是开启慢查询日志(slow query log),装上mtop。这样你就能获取那些恶意的入侵者的信息了。有需要运行10秒的查询语句正在破坏你的应用程序吗?这些家伙会展示给你看他的查询语句是怎么写的。

在你发现那些很慢的查询语句后,你需要用MySQL自带的工具,如EXPLAIN,SHOW STATUS,SHOW PROCESSLIST。它们会告诉你资源都消耗在哪了,查询语句的缺陷在哪,譬如一个有三次join子查询的查询语句是否在内存中进行排序,还是在硬盘 上进行。当然你也应该使用测评工具如top,procinfo,vmstat等等获取更多系统性能信息。

3.减小你的schema

在你开始写查询语句之前,你需要设计schema。记住将一个表装入内存所需要的空间大概是行数*一行的大小。除非你觉得世界上的每个人都会在你的 网站注册2兆8000亿次的话,否则你不需要采用BITINT作为你的user_id。同样的,如果一个文本列是固定大小的话(譬如US邮编,通常 是”XXXXX-XXXX”的形式),采用VARCHAR的话会给每行增加多余的字节。

有些人对数据库规范化不以为意,他们说这样会形成相当复杂的schema。然而适当的规范化会减少化冗余数据。(适当的规范化)就意味着牺牲少许性 能,换取整体上更少的footprint,这种性能换取内存在计算机科学中是很常见的。最好的方法是IMO,就是开始先规范化,之后如果性能需要的话,再 反规范化。你的数据库将会更逻辑化,你也不用过早的进行优化。(译者注,这一段我不是很理解,可能翻译错了,欢迎纠正。)

4.拆分你的表

通常有些表只有一些列你是经常需要更新的。例如对于一个博客,你需要在许多不同地方显示标题(如最近的文章列表),只在某个特定页显示概要或者全文。水平垂直拆分是很有帮助的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE TABLE posts (
     id int UNSIGNED NOT NULL AUTO_INCREMENT,
     author_id int UNSIGNED NOT NULL,
     title var char( 128 ),
     created timestamp NOT NULL,
     PRIMARY KEY(id)
);
CREATE TABLE posts_data (
     post_id int UNSIGNED NOT NULL,
     teaser text,
     body text,
     PRIMARY KEY(post_id)
);

上面的schema是对读数据进行的优化。经常要访问的数据存在一个表中,那些不经常访问的数据放在另一个。被拆分后,不经常访问的数据占据更少的 内存。你也可以优化写数据,经常更新的数据放在一个表,不经常更新的放在另一个表。这可以使缓存更高效,因为MySQL不需要让没有更新过的数据移出缓 存。

5.不要过度使用artificial primary key

artificial primary key非常棒,因为他们使得schema更少的变化。如果我们将地理信息存在以美国邮编为基础的表中,如果邮编系统突然改变了,那我们就会有大麻烦了。另 一方面,采用natural key有时候也很棒,譬如我们需要join多对多的关系表时,我们不应该这样:

1
2
3
4
5
6
7
CREATE TABLE posts_tags (
     relation_id int UNSIGNED NOT NULL AUTO_INCREMENT,
     post_id int UNSIGNED NOT NULL,
     tag_id int UNSIGNED NOT NULL,
     PRIMARY KEY(relation_id),
     UNIQUE INDEX(post_id, tag_id)
);

artificial key完全是多余的,而且post-tag关系的数量将会受到整形数据的系统最大值的限制。

1
2
3
4
5
CREATE TABLE posts_tags (
     post_id int UNSIGNED NOT NULL,
     tag_id int UNSIGNED NOT NULL,
     PRIMARY KEY(post_id, tag_id)
);

6.学习索引

你选择的索引的好坏很重要,不好的话可能破坏数据库。对那些还没有在数据库学习很深入的人来说,索引可以看作是就是hash排序。例如如果我们用查询语句SELECT * FROM users WHERE last_name = ‘Goldstein’,而last_name没有索引的话,那么DBMS将会查询每一行,看看是否等于“Goldstein”。索引通常是B-tree(还有其他的类型),可以加快比较的速度。

你需要给你要select,group,order,join的列加上索引。显然每个索引所需的空间正比于表的行数,所以越多的索引将会占用更多的 内存。而且写数据时,索引也会有影响,因为每次写数据时都会更新对应的索引。你需要取一个平衡点,取决每个系统和实施代码的需要。

7.SQL不是C

C是经典的过程语言,对于一个程序员来说,C语言也是个陷阱,使你错误的以为SQL也是一种过程语言(当然SQL也不是功能语言也不是面向对象的)。你不要想象对数据进行操作,而是要想象有一组数据,以及它们之间的关系。经常使用子查询时会出现错误的用法。

1
2
3
4
5
6
SELECT a.id,
     (SELECT MAX(created)
     FROM posts
     WHERE author_id = a.id)
AS latest_post
FROM authors a

因为这个子查询是耦合的,子查询要使用外部查询的信息,我们应该使用join来代替。

1
2
3
4
5
SELECT a.id, MAX(p.created) AS latest_post
FROM authors a
INNER JOIN posts p
     ON (a.id = p.author_id)
GROUP BY a.id

8.理解你的引擎

MySQL有两种存储引擎:MyISAM和InnoDB。它们分别有自己的性能特点和考虑因素。总体来讲,MyISAM适合读数据很多的情况,InnoDB适合写数据很多的情况,但也有很多情况下正好相反。最大的区别是它们如何处理COUNT函数。

MyISAM缓存有表meta-data,如行数。这就意味着,COUNT(*)对于一个结构很好的查询是不需要消耗多少资源的。然后对于 InnoDB来说,就没有这种缓存。举个例子,我们要对一个查询来分页,假设你有这样一个语句SELECT * FROM users LIMIT 5,10,而运行SELECT COUNT(*) FROM users LIMIT 5,10 时,对于MyISAM很快完成,而对InnoDB就需要和第一个语句相同的时间。MySQL有个SQL_CALC_FOUND_ROWS选项,可以告诉 InnoDB运行查询语句时就计算行数,之后再从SELECT FOUND_ROWS()来获取。这是MySQL特有的。但使用InnoDB有时候是非常必要的,你可以获得一些功能(如行锁定,stord procedure等)。

9.MySQL特定的快捷键

MySQL提供了许多扩展,方便使用。譬如INSERT … SELECT, INSERT … ON DUPLICATE KEY UPDATE, 以及REPLACE。

我能用到它们时是毫不犹豫的,因为它们很方便,能在许多情况下发挥不错的效果。但是MySQL也有一些危险的关键字,应该少用。例如INSERT DELAYED,它告诉MySQL不需要立即插入数据(例如在写日志的时候)。但问题是如果在很高数据量的情况下,插入可能会被无限期延迟,导致插入队列 爆满。你也可以使用MySQL的索引提示来指出哪些索引是需要使用的。MySQL大部分时间运行是不错的,但如果schema设计不好的话或语句写得不好 的话,MySQL的表现可能很糟糕。

10.到这里为止吧

最后,如果你关心MySQL性能优化的话,请阅读Peter Zaitsev的关于MySQL性能的博客,他写了许多关于数据库管理和优化的博客。

MySQL主从配置

在开始配置之前,我们先说说为什么要用数据库主从复制吧。
1. 分散负载:用主从复制最大的好处之一就是由主库执行所有插入操作,由从库承担起大部分的查询操作。简单地通过主从就能极大地提速你的应用。
2. 用从库备份数据:这个优点常常被遗忘。使用从库备份不会影响你的网站工作,特别是当数据量大到G级别的时候,由备份引起的锁表引导致网站延迟响应。更严重地可能会导致网站瞬间瘫痪。如果在从库上做这些就无关紧要了,你甚至可以停掉某个从库,并且暴力的复制var目录。

假设你有两台数据库服务器,计划如下部署:

Master server ip: 10.0.0.1
Slave server ip: 10.0.0.2
Slave username: slaver
Slave pw: 123456
Your data directory is: /usr/local/mysql/var/

主库配置文件my.cnf [mysqld]节下面加上:


# changes made to do master
server-id = 1
relay-log = /usr/local/mysql/var/mysql-relay-bin
relay-log-index = /usr/local/mysql/var/mysql-relay-bin.index
log-error = /usr/local/mysql/var/mysql.err
master-info-file = /usr/local/mysql/var/mysql-master.info
relay-log-info-file = /usr/local/mysql/var/mysql-relay-log.info
datadir = /usr/local/mysql/var
log-bin = /usr/local/mysql/var/mysql-bin
# end master

从库配置文件my.cnf [mysqld]节下面加上
# changes made to do slave
server-id = 2
relay-log = /usr/local/mysql/var/mysql-relay-bin
relay-log-index = /usr/local/mysql/var/mysql-relay-bin.index
log-error = /usr/local/mysql/var/mysql.err
master-info-file = /usr/local/mysql/var/mysql-master.info
relay-log-info-file = /usr/local/mysql/var/mysql-relay-log.info
datadir = /usr/local/mysql/var
# end slave setup

在主库上创建用户:


mysql> grant replication slave on *.* to ‘slaver’@’10.0.0.2′ identified by ’123456′;

值得注意的是,grant replication slave是全局操作,不能对单个库操作。

使用mysqldump工具,导出主库数据,配好之前先手动同步主从库的数据:

mysqldump -u root –all-databases –single-transaction –master-data=1 > masterdump.sql

然后在从库中导入:

mysql < masterdump.sql

接下来要告库从库去复制哪个主库,以及连接主库的信息:

mysql> CHANGE MASTER TO MASTER_HOST=’10.0.0.1′, MASTER_USER=’slaver’, MASTER_PASSWORD=’123456′;

紧接着开启从库同步:

mysql> start slave;

可以通过下命的命令查看同步状态:

mysql> show slave status\G

最后执行完成之后,刚添加的用户出现怪异现像,任何帐号都能登陆而且不用密码,最后百思不得其解,大量搜索之后发现“重启MySQL即可”,然后和然后的然后,就可以了。。。

MySQL:在一次查询中进行批量更新

为了提高工作效率以及节省人力时间,批量操作在现在的应用中越来越常见。

对于数据库来说,无非是“插删改查”,“查询”和“删除“自然不在话下,但是”删“的话我们一般遵循单查询批量1000这个上限,再多可能会影响数据库同步延迟。

而批量”插入“则可以通过拼SQL方式实现,即:


INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

当然,我们要限制单条SQL的大小,一般来说10M吧。

但是对于批量“更新”呢?能不能单查询批量更新(这里的批量更新指的是Where之后只有单条结果的情况)呢? 下面来看看批量“更新”的几种方案:

  1. 使用INSERT ... ON DUPLICATE KEY UPDATE

    INSERT INTO table (id,Col1,Col2) VALUES (1,1,1),(2,2,3),(3,9,3),(4,10,12)
    ON DUPLICATE KEY UPDATE Col1=VALUES(Col1),Col2=VALUES(Col2);

    这种方法倒是简单,但是官方文档有提示在Multi Unique Index的时候慎用,官方文档原话如下:

    In general, you should try to avoid using an ON DUPLICATE KEY clause on tables with multiple unique indexes.

  2. 使用MySQL的Case语句,原型如下:

    CASE case_value
    WHEN when_value THEN statement_list
    [WHEN when_value THEN statement_list] …
    [ELSE statement_list]
    END CASE

    在更新时则这么用:


    UPDATE table SET Col1 = CASE id
    WHEN 1 THEN 1
    WHEN 2 THEN 2
    WHEN 4 THEN 10
    ELSE Col1
    END CASE,
    Col2 = CASE id
    WHEN 3 THEN 3
    WHEN 4 THEN 12
    ELSE Col2
    END CASE
    WHERE id IN (1, 2, 3, 4);

    这种方式太过复杂,我想谁也不会在代码中这么用,所以不推荐使用。。。
  3. 新建临时表,把新数据放在临时表中,然后再逐条更新,示例代码(PHP)如下:

    // Reorder items
    function update_items_tempdb(&$items)
    {
    shuffle($items);
    $table_name = uniqid(‘tmp_test_’);
    $sql = “CREATE TEMPORARY TABLE `$table_name` (”
    .” `id` int(10) unsigned NOT NULL AUTO_INCREMENT”
    .”, `position` int(10) unsigned NOT NULL”
    .”, PRIMARY KEY (`id`)”
    .”) ENGINE = MEMORY”;
    query($sql);
    $i = 0;
    $sql = ”;
    foreach ($items as &$item)
    {
    $item->position = $i++;
    $sql .= ($sql ? ‘, ‘ : ”).”({$item->id}, {$item->position})”;
    }
    if ($sql)
    {
    query(“INSERT INTO `$table_name` (id, position) VALUES $sql”);
    $sql = “UPDATE `test`, `$table_name` SET `test`.position = `$table_name`.position”
    .” WHERE `$table_name`.id = `test`.id”;
    query($sql);
    }
    query(“DROP TABLE `$table_name`”);
    }

总结一下:对于少量的更新,我们仍将使用单查询更新一条记录的方式(指的是根据Unique字段查询的单条结果)。如果更新数据大到某张表(百万数据量)的一半时,我们将使用临时表方案,也就是这里说的第三种方案。

Install virtualbox 4.1.8 OSE on Slackware 13.37 X86_64

在Linux系统上编译安装软件总是记忆深刻的,因为这个过程往往是繁杂而痛苦的。我有两台电脑,一台式机一本本,台式机系统是Slackware 13.37 X86_64 ,本本系统是Windows 7,因为工作原因要用到Baidu Hi,Outlook,Lync等Windows专有软件,所以一直在两台电脑之前切来切去,一直感觉很麻烦,一直想在台式机上装一个虚拟机运行Win 7。今天下午终于腾出时间在我的Slackware上装好virtualbox了。现在把过程记录下来,在Slackware下面装软件可以说是幸福的,因为它有像freebsd一样的包管理系统。

1. 先到这里下载virtualbox http://slackbuilds.org/repository/13.37/system/virtualbox/ ,别急着动手且看下一步
2. 依赖iasl,所以先安装ACPICA http://slackbuilds.org/repository/13.37/development/acpica/
3. virtualbox需要32位的库文件支持,而我装的是纯64位版的,所以需要安装32位的库文件:http://connie.slackware.com/~alien/multilib/13.37/
把相应的*.txz下载下来并用upgradepkg –install-new *.txz安装
4. 依赖jdk,所以还要安装jdk,我装的是jdk-6u25
5. 最后安装virtualbox,成功后别急着运行
6. virtualbox运行还依赖virtualbox-kernel http://slackbuilds.org/repository/13.37/system/virtualbox-kernel/ ,装完这个重启便可

注意:slackbuilds.org站上找到包后,要下载Source Download列表中的每一个文件和Slack Build文件,否则会编译失败,编译成功后会生成/tmp/foo.tgz文件(slackware的包文件),接着用installpkg foo.tgz便可成功安装。以上每步我都有生成包文件,如果环境与我一致,应该可以通用。

VirtualBox在安装了增强包之后就像打了鸡血一样,拖动窗口也不卡了,还可以和宿主机共享数据空间,还可以和宿主机共享粘贴板,并且分辩率随窗口大小而变化,真心感觉不错,赞一个!!!

php multi-thread

今天突然想看看PHP多线程的问题,发现很多人讲PHP的多线程其实是多进程,唉,破灭了!!!网上给PHP的模拟多线程提出了几种方案,下面仅摘录:

There are a few solutions, varying from “Hmmm, just about OK” through to “Poke out your eyes”.

1. Write your multithreaded code as a PHP extension. Probably the most supported, but you need to write in C (or another language supported for extensions).
2. Spawn child processes onto the underlying OS, and read/write to their input/output with standard file handles. See popen for one route in, or PCNTRL. Reasonable, and you can use PHP from the command line.
3. Make other HTTP requests to yourself via CURL or similar, thus piggybacking on your web servers multi-processing capacity. Keeps all your code “web like”, but runs the risk of irate support developers tracking you down and breaking thumbs.