jimmy's profileJDlogPhotosBlogLists Tools Help
    March 20

    MySQL: 诡异的MySQL server has gone away及其解决

    MySQL: 诡异的MySQL server has gone away及其解决

    jimmy | 15 三月, 2007 20:32

    在Mysql执行show status,通常更关注缓存效果、进程数等,往往忽略了两个值:

    Variable_name Value
    Aborted_clients 3792
    Aborted_connects 376

    通常只占query的0.0x%,所以并不为人所重视。而且在传统Web应用上,query错误对用户而言影响并不大,只是重新刷新一下页面就OK了。最近的基础改造中,把很多应用作为service运行,无法提示用户重新刷新,这种情况下,可能就会影响到服务的品质。

    通过程序脚本的日志跟踪,主要报错信息为“MySQL server has gone away”。官方的解释是:

    The most common reason for the MySQL server has gone away error is that the server timed out and closed the connection.

    Some other common reasons for the MySQL server has gone away error are:

    • You (or the db administrator) has killed the running thread with a KILL statement or a mysqladmin kill command.

    • You tried to run a query after closing the connection to the server. This indicates a logic error in the application that should be corrected.

    • A client application running on a different host does not have the necessary privileges to connect to the MySQL server from that host.

    • You got a timeout from the TCP/IP connection on the client side. This may happen if you have been using the commands: mysql_options(..., MYSQL_OPT_READ_TIMEOUT,...) or mysql_options(..., MYSQL_OPT_WRITE_TIMEOUT,...). In this case increasing the timeout may help solve the problem.

    • You have encountered a timeout on the server side and the automatic reconnection in the client is disabled (the reconnect flag in the MYSQL structure is equal to 0).

    • You are using a Windows client and the server had dropped the connection (probably because wait_timeout expired) before the command was issued.

      The problem on Windows is that in some cases MySQL doesn't get an error from the OS when writing to the TCP/IP connection to the server, but instead gets the error when trying to read the answer from the connection.

      In this case, even if the reconnect flag in the MYSQL structure is equal to 1, MySQL does not automatically reconnect and re-issue the query as it doesn't know if the server did get the original query or not.

      The solution to this is to either do a mysql_ping on the connection if there has been a long time since the last query (this is what MyODBC does) or set wait_timeout on the mysqld server so high that it in practice never times out.

    • You can also get these errors if you send a query to the server that is incorrect or too large. If mysqld receives a packet that is too large or out of order, it assumes that something has gone wrong with the client and closes the connection. If you need big queries (for example, if you are working with big BLOB columns), you can increase the query limit by setting the server's max_allowed_packet variable, which has a default value of 1MB. You may also need to increase the maximum packet size on the client end. More information on setting the packet size is given in Section A.1.2.9, “Packet too large.

      An INSERT or REPLACE statement that inserts a great many rows can also cause these sorts of errors. Either one of these statements sends a single request to the server irrespective of the number of rows to be inserted; thus, you can often avoid the error by reducing the number of rows sent per INSERT or REPLACE.

    • You also get a lost connection if you are sending a packet 16MB or larger if your client is older than 4.0.8 and your server is 4.0.8 and above, or the other way around.

    • It is also possible to see this error if hostname lookups fail (for example, if the DNS server on which your server or network relies goes down). This is because MySQL is dependent on the host system for name resolution, but has no way of knowing whether it is working — from MySQL's point of view the problem is indistinguishable from any other network timeout.

      You may also see the MySQL server has gone away error if MySQL is started with the --skip-networking option.

      Another networking issue that can cause this error occurs if the MySQL port (default 3306) is blocked by your firewall, thus preventing any connections at all to the MySQL server.

    • You can also encounter this error with applications that fork child processes, all of which try to use the same connection to the MySQL server. This can be avoided by using a separate connection for each child process.

    • You have encountered a bug where the server died while executing the query.


    据此分析,可能原因有3:

    1,Mysql服务端与客户端版本不匹配。

    2,Mysql服务端配置有缺陷或者优化不足

    3,需要改进程序脚本

    通过更换多个服务端与客户端版本,发现只能部分减少报错,并不能完全解决。排除1。

    对服务端进行了彻底的优化,也未能达到理想效果。在timeout的取值设置上,从经验值的10,到PHP默认的60,进行了多次尝试。而Mysql官方默认值(8小时)明显是不可能的。从而对2也进行了排除。(更多优化的经验分享,将在以后整理提供)

    针对3对程序代码进行分析,发现程序中大量应用了类似如下的代码(为便于理解,用原始api描述):

    $conn=mysql_connect( ... ... );

    ... ... ... ...

    if(!$conn){ //reconnect

    $conn=mysql_connect( ... ... );

    }

    mysql_query($sql, $conn);

    这段代码的含义,与Mysql官方建议的方法思路相符[ If you have a script, you just have to issue the query again for the client to do an automatic reconnection. ]。在实际分析中发现,if(!$conn)并不是可靠的,程序通过了if(!$conn)的检验后,仍然会返回上述错误。

    对程序进行了改写:

    if(!conn){ // connect ...}

    elseif(!mysql_ping($conn)){ // reconnect ... }

    mysql_query($sql, $conn);

    经实际观测,MySQL server has gone away的报错基本解决。

    BTW: 附带一个关于 reconnect 的疑问,

    在php4x+client3x+mysql4x的旧环境下,reconnet的代码:

    $conn=mysql_connect(...) 可以正常工作。

    但是,在php5x+client4x+mysql4x的新环境下,$conn=mysql_connect(...)返回的$conn有部分情况下不可用。需要书写为:

    mysql_close($conn);

    $conn=mysql_connect(...);

    返回的$conn才可以正常使用。原因未明。未做深入研究,也未见相关讨论。或许mysql官方的BUG汇报中会有吧。

    ~~呵呵~~

    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/123

    MySQL: 4.X 版本数据库向 5.15 以上迁移

    MySQL: 4.X 版本数据库向 5.15 以上迁移

    jimmy | 15 三月, 2007 13:53

    会有两个问题:

    1,表结构变化

    2,权限表及辅助表变化

    解决问题1,官方的建议是全部导出,然后在新的库里面导入。

    实际操作中,旧表直接搬迁后是可以继续使用的,只是效率和一些新功能可能会受到影响。保持线上服务的同时,运行mysql_repaire,或者是在phpmyadmnin里面修复一下,就可以了。

    注意:要先进行备份。升级后的表,如果需要退回到mysql4x版本,是不能够使用的。

    解决问题2,如果不升级也是可以继续使用的,但是在日志中会看到:

    070315 13:36:35 [ERROR] mysql.user has no `Event_priv` column at position 29
    070315 13:36:35 [ERROR] SCHEDULER: The system tables are damaged. The scheduler subsystem will be unusable during this run.

    同时会导致 SHOW VARIABLES; 命令不可用,无法查看系统变量情况。

    官方的解决方案是: mysql/bin/mysql_fix_privilege_tables,但实际操作中发现并不能解决(Beta版的bug?)。手工处理会稍微复杂一些,因为表结构变化,直接导出、导入是不行的。

    A,导出user表内容到user.sql

    B,用scripts/mysql_install_db生成新结构的表,覆盖旧表。

    C,修改user.sql,删除create段,修改INSERT段,将SQL语句补全为:

    INSERT INTO `user` (`Host`,`User`,`Password`,`Select_priv`,`Inser ... ...) VALUES (.. ... ...)

    D,删除user表内容,将user.sql导入。

    E,重启数据库。

    Done。

    ~~呵呵~~


    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/122

    MySQL: Linux机上运行多个版本的MySQL

    MySQL: Linux机上运行多个版本的MySQL

    jimmy | 14 三月, 2007 10:15

    在同一台服务器上部署多个MySQL服务,可以有效提高机器利用率。而且,将不同内容分属不同服务上,也相对更安全些。

    MySQL提供了mysqld_multi,但实际应用中没有人用它。弊端:

    1,暂停、重启,会导致所有服务都停下来。

    2,配置文件冗长,拆分、迁移时麻烦。

    业内比较常用的方法是,将数据库数据文件和配置文件,以及开启关闭脚本,共同放在一个目录下。启动服务时,作为一个单独的服务;关闭时对其他服务没有影响。搬迁时,只要将这个目录迁移到目标机器上就OK了。

    一个典型的例子:


    10.10.82.83 [server2]$ cat start.sh
    #!/bin/sh
    rundir="/opt/server2";
    echo $rundir;
    /usr/local/mysql/bin/mysqld_safe --defaults-file="$rundir"/my.cnf --pid-file="$rundir"/mysql.pid --datadir="$rundir"/var --socket="$rundir"/mysql.sock --log-error="$rundir"/err.log --log-slow-queries="$rundir"/slow_query.log&

    上例中使用的MySQL是按照官方建议安装在/usr/local/mysql下的。

    有时候需要在同一台服务器上使用多个不同版本,比如,测试新版本等。按照以上逻辑,将新MySQLD安装在 /usr/local/mysql-new下,修改 start.sh 为 /usr/local/mysql-new/bin/mysqld_safe。数据库可以正常启动,但是,观察发现并不是新版本。原因何在?

    官方的解释是:需要下载源代码版本,编译时指定运行目录。其实并非必须如此。

    研究mysqld_safe文件,发现其逻辑为:

    A,从命令行取配置参数

    B,从配置文件取配置参数

    C,对于mysqld的目录(basedir)和数据文件目录(datadir),如果以上都没有在配置中指定,则先检查当前目录是否可用,否则设置/usr/local/mysql为默认。

    所以,虽然使用的是mysql-new下的mysqld_safe,但运行的还是/usr/local/mysql/bin/mysqld,也就是旧版本。需要相应修改启动脚本,或者是配置文件:

    #!/bin/sh
    rundir="/opt/server0";
    echo $rundir;
    /usr/local/mysql-new/bin/mysqld_safe --defaults-file="$rundir"/my.cnf --ledir=/usr/local/mysql-new/bin --basedir=/usr/local/mysql-new --pid-file="$rundir"/mysql.pid --datadir="$rundir"/var --socket="$rundir"/mysql.sock --log-error="$rundir"/err.log --log-slow-queries="$rundir"/slow_query.log&

    这样就可以在同一台机器上同时运行多个版本的Mysql了。


    ~~呵呵~~

    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/121

    MySQL: time_format格式

    MySQL: time_format格式

    jimmy | 13 三月, 2007 14:51

    TIME_FORMAT(time,format)

    eg: time_format(the_time, '%Y-%m-%d %T')

    说明符

    说明

    %a

    工作日的缩写名称 (Sun..Sat)

    %b

    月份的缩写名称 (Jan..Dec)

    %c

    月份,数字形式(0..12)

    %D

    带有英语后缀的该月日期 (0th, 1st, 2nd, 3rd, ...)

    %d

    该月日期, 数字形式 (00..31)

    %e

    该月日期, 数字形式(0..31)

    %f

    微秒 (000000..999999)

    %H

    小时(00..23)

    %h

    小时(01..12)

    %I

    小时 (01..12)

    %i

    分钟,数字形式 (00..59)

    %j

    一年中的天数 (001..366)

    %k

    小时 (0..23)

    %l

    小时 (1..12)

    %M

    月份名称 (January..December)

    %m

    月份, 数字形式 (00..12)

    %p

    上午(AM)或下午( PM

    %r

    时间 , 12小时制 (小时hh:分钟mm:秒数ss 后加 AMPM)

    %S

    (00..59)

    %s

    (00..59)

    %T

    时间 , 24小时制 (小时hh:分钟mm:秒数ss)

    %U

    (00..53), 其中周日为每周的第一天

    %u

    (00..53), 其中周一为每周的第一天

    %V

    (01..53), 其中周日为每周的第一天 ; %X同时使用

    %v

    (01..53), 其中周一为每周的第一天 ; %x同时使用

    %W

    工作日名称 (周日..周六)

    %w

    一周中的每日 (0=周日..6=周六)

    %X

    该周的年份,其中周日为每周的第一天, 数字形式,4位数;%V同时使用

    %x

    该周的年份,其中周一为每周的第一天, 数字形式,4位数;%v同时使用

    %Y

    年份, 数字形式,4位数

    %y

    年份, 数字形式 (2位数)

    %%

    %’文字字符

    ~~呵呵~~


    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/120

    转: apache2 MPM的选择与配置

    转: apache2 MPM的选择与配置

    jimmy | 06 三月, 2007 20:27

     谈到Apache,大多数系统管理员对其稳定版1.3印象颇深。虽然Apache 2.0的系列开发版早已由Alpha、Beta发展到现在的GA(General Availability)版,但是一些人潜意识里还认为开发版并非可用于生产环境的稳定版本。尤其是1.3版的API与2.0版不兼容,使得大量模块必须要重写才能在2.0版上使用。Apache 1.3和2.0之间的内部变化的确较大,用Apache创始人Brian Behlendorf自己的话来说:“这个版本包括了数百个新的特性,所以这个产品应该具有3.1或8i这样的产品编号,而不是2.0。”Apache 2.0中加入了很多的核心改进和新功能,如Unix线程、多协议支持、新的构建系统、对非Unix平台的更佳支持、IPv6支持、新的Apache API、过滤器、多语言错误响应、原生的Windows NT Unicode支持、更简单化的配置,以及升级的正则表达式库等。它当然还包括对许多模块的重要改进,同时也加入了一些新的模块。

      为了使Apache更加平滑地从1.3版升级到2.0版,Apache开发团队做了很多工作。目前很多重要的模块已经可以很好地支持2.0版,如 PHP、FastCGI、Mod_perl、Mod_python等。在httpd.conf的指令配置语法上,目前的2.0版(2.0.45)与1.3 版的兼容性已做得相当好。比如,以前的2.0版如果要使用PHP,一般用过滤器实现;现在的PHP官方文档中已经使用1.3版中的LoadModule语句做为加载PHP的推荐方式。只要略微了解一下Apache 2.0的新特性,从1.3版升级到2.0版将是一件非常容易的事情。使用Apache 2.0是大势所趋,因为Apache的开发团队已经把开发重心转移到2.0版上。1.3版自2002年10月发布了1.3.27后一直没有新版本推出,而 2.0版在与1.3.27同时发布2.0.43后,在今年1月发布了2.0.44,并于今年3月末发布了2.0.45,并包含了很多改进和修正。

      MPM的引入带来性能改善

      Apache 2.0在性能上的改善最吸引人。在支持POSIX线程的Unix系统上,Apache可以通过不同的MPM运行在一种多进程与多线程相混合的模式下,增强部分配置的可扩充性能。相比于Apache 1.3,2.0版本做了大量的优化来提升处理能力和可伸缩性,并且大多数改进在默认状态下即可生效。但是在编译和运行时刻,2.0也有许多可以显著提高性能的选择。本文不想叙述那些以功能换取速度的指令,如HostnameLookups等,而只是说明在2.0中影响性能的最核心特性:MPM(Multi -Processing Modules,多道处理模块)的基本工作原理和配置指令。

      毫不夸张地说,MPM的引入是Apache 2.0最重要的变化。大家知道,Apache是基于模块化的设计,而Apache 2.0更扩展了模块化设计到Web服务器的最基本功能。服务器装载了一种多道处理模块,负责绑定本机网络端口、接受请求,并调度子进程来处理请求。扩展模块化设计有两个重要好处:

      ◆ Apache可以更简洁、有效地支持多种操作系统;

      ◆ 服务器可以按站点的特殊需要进行自定制。

      在用户级,MPM看起来和其它Apache模块非常类似。主要区别是在任意时刻只能有一种MPM被装载到服务器中。

      指定MPM的方法

      下面以Red Hat Linux 9为平台,说明在Apache 2.0中如何指定MPM (Apache采用2.0.45)。先解压缩源代码包httpd-2.0.45.tar.gz,生成httpd-2.0.45目录(Apache 1.3源代码包的命名规则是apache_1.3.NN.tar.gz,而2.0版则是httpd-2.0.NN.tar.gz,其中NN是次版本号)。

      进入httpd-2.0.45目录,运行以下代码:

    $ ./configure --help|grep mpm


      显示如下:

    --with-mpm=MPM
    Choose the process model for Apache to use.
    MPM={beos|worker|prefork|mpmt_os2| perchild|leader|threadpool}


      上述操作用来选择要使用的进程模型,即哪种MPM模块。Beos、mpmt_os2分别是BeOS和OS/2上缺省的MPM,perchild主要设计目的是以不同的用户和组的身份来运行不同的子进程。这在运行多个需要CGI的虚拟主机时特别有用,会比1.3版中的SuExec机制做得更好。 leader和threadpool都是基于worker的变体,还处于实验性阶段,某些情况下并不会按照预期设想的那样工作,所以Apache官方也并不推荐使用。因此,我们主要阐述prefork和worker这两种和性能关系最大的产品级MPM ( 有关其它的MPM详细说明,请参见Apache官方文档:http://httpd.apache.org/docs-2.0/mod/)。

      prefork的工作原理及配置

      如果不用“--with-mpm”显式指定某种MPM,prefork就是Unix平台上缺省的MPM。它所采用的预派生子进程方式也是Apache 1.3中采用的模式。prefork本身并没有使用到线程,2.0版使用它是为了与1.3版保持兼容性;另一方面,prefork用单独的子进程来处理不同的请求,进程之间是彼此独立的,这也使其成为最稳定的MPM之一。

      若使用prefork,在make编译和make install安装后,使用“httpd -l”来确定当前使用的MPM,应该会看到prefork.c(如果看到worker.c说明使用的是worker MPM,依此类推)。再查看缺省生成的httpd.conf配置文件,里面包含如下配置段:


    StartServers 5
    MinSpareServers 5
    MaxSpareServers 10
    MaxClients 150
    MaxRequestsPerChild 0


      prefork的工作原理是,控制进程在最初建立“StartServers”个子进程后,为了满足MinSpareServers设置的需要创建一个进程,等待一秒钟,继续创建两个,再等待一秒钟,继续创建四个……如此按指数级增加创建的进程数,最多达到每秒32个,直到满足 MinSpareServers设置的值为止。这就是预派生(prefork)的由来。这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销以增加性能。

      MaxSpareServers设置了最大的空闲进程数,如果空闲进程数大于这个值,Apache会自动kill掉一些多余进程。这个值不要设得过大,但如果设的值比MinSpareServers小,Apache会自动把其调整为MinSpareServers+ 1。如果站点负载较大,可考虑同时加大MinSpareServers和MaxSpareServers。

       MaxRequestsPerChild设置的是每个子进程可处理的请求数。每个子进程在处理了“MaxRequestsPerChild”个请求后将自动销毁。0意味着无限,即子进程永不销毁。虽然缺省设为0可以使每个子进程处理更多的请求,但如果设成非零值也有两点重要的好处:

      ◆ 可防止意外的内存泄漏;

      ◆ 在服务器负载下降的时侯会自动减少子进程数。

      因此,可根据服务器的负载来调整这个值。笔者认为10000左右比较合适。

      MaxClients是这些指令中最为重要的一个,设定的是Apache可以同时处理的请求,是对Apache性能影响最大的参数。其缺省值150是远远不够的,如果请求总数已达到这个值(可通过ps -ef|grep http|wc -l来确认),那么后面的请求就要排队,直到某个已处理请求完毕。这就是系统资源还剩下很多而HTTP访问却很慢的主要原因。系统管理员可以根据硬件配置和负载情况来动态调整这个值。虽然理论上这个值越大,可以处理的请求就越多,但Apache默认的限制不能大于256。如果把这个值设为大于256,那么 Apache将无法起动。事实上,256对于负载稍重的站点也是不够的。在Apache 1.3中,这是个硬限制。如果要加大这个值,必须在“configure”前手工修改的源代码树下的src/include/httpd.h中查找 256,就会发现“#define HARD_SERVER_LIMIT 256”这行。把256改为要增大的值(如4000),然后重新编译Apache即可。在Apache 2.0中新加入了ServerLimit指令,使得无须重编译Apache就可以加大MaxClients。下面是笔者的prefork配置段:


    StartServers 10
    MinSpareServers 10
    MaxSpareServers 15
    ServerLimit 2000
    MaxClients 1000
    MaxRequestsPerChild 10000


      上述配置中,ServerLimit的最大值是20000,对于大多数站点已经足够。如果一定要再加大这个数值,对位于源代码树下server/mpm/prefork/prefork.c中以下两行做相应修改即可:

    #define DEFAULT_SERVER_LIMIT 256
    #define MAX_SERVER_LIMIT 20000


      worker的工作原理及配置

      相对于prefork,worker是2.0 版中全新的支持多线程和多进程混合模型的MPM。由于使用线程来处理,所以可以处理相对海量的请求,而系统资源的开销要小于基于进程的服务器。但是, worker也使用了多进程,每个进程又生成多个线程,以获得基于进程服务器的稳定性。这种MPM的工作方式将是Apache 2.0的发展趋势。

      在configure -with-mpm=worker后,进行make编译、make install安装。在缺省生成的httpd.conf中有以下配置段:


    StartServers 2
    MaxClients 150
    MinSpareThreads 25
    MaxSpareThreads 75
    ThreadsPerChild 25
    MaxRequestsPerChild 0


      worker的工作原理是,由主控制进程生成“StartServers”个子进程,每个子进程中包含固定的ThreadsPerChild线程数,各个线程独立地处理请求。同样,为了不在请求到来时再生成线程,MinSpareThreads和MaxSpareThreads设置了最少和最多的空闲线程数;而MaxClients设置了所有子进程中的线程总数。如果现有子进程中的线程总数不能满足负载,控制进程将派生新的子进程。

      MinSpareThreads和MaxSpareThreads的最大缺省值分别是75和250。这两个参数对Apache的性能影响并不大,可以按照实际情况相应调节。

      ThreadsPerChild是worker MPM中与性能相关最密切的指令。ThreadsPerChild的最大缺省值是64,如果负载较大,64也是不够的。这时要显式使用 ThreadLimit指令,它的最大缺省值是20000。上述两个值位于源码树server/mpm/worker/worker.c中的以下两行:

    #define DEFAULT_THREAD_LIMIT 64
    #define MAX_THREAD_LIMIT 20000


      这两行对应着ThreadsPerChild和ThreadLimit的限制数。最好在configure之前就把64改成所希望的值。注意,不要把这两个值设得太高,超过系统的处理能力,从而因Apache不起动使系统很不稳定。

      Worker模式下所能同时处理的请求总数是由子进程总数乘以ThreadsPerChild值决定的,应该大于等于MaxClients。如果负载很大,现有的子进程数不能满足时,控制进程会派生新的子进程。默认最大的子进程总数是16,加大时也需要显式声明ServerLimit(最大值是 20000)。这两个值位于源码树server/mpm/worker/worker.c中的以下两行:

    #define DEFAULT_SERVER_LIMIT 16
    #define MAX_SERVER_LIMIT 20000


      需要注意的是,如果显式声明了ServerLimit,那么它乘以ThreadsPerChild的值必须大于等于MaxClients,而且 MaxClients必须是ThreadsPerChild的整数倍,否则Apache将会自动调节到一个相应值(可能是个非期望值)。下面是笔者的 worker配置段:


    StartServers 3
    MaxClients 2000
    ServerLimit 25
    MinSpareThreads 50
    MaxSpareThreads 200
    ThreadLimit 200
    ThreadsPerChild 100
    MaxRequestsPerChild 0


      通过上面的叙述,可以了解到Apache 2.0中prefork和worker这两个重要MPM的工作原理,并可根据实际情况来配置Apache相关的核心参数,以获得最大的性能和稳定性。


    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/118

    MySQL5的分区研究

    MySQL5的分区研究

    jimmy | 06 二月, 2007 11:14

    MySQL5.1提供的分区技术,令人对其充满希望。

    根据官方文档,分区技术能够极大的帮助DBA人员。支持的分区模式包括:

    Range

    List

    Hash

    Key

    ----

    安装配置记录:

    wget http://dev.mysql.com/get/Downloads/MySQL-5.1/mysql-5.1.16-beta-linux-i686-icc-glibc23.tar.gz/from/http://mysql.cdpa.nsysu.edu.tw/

    tar -xzvf mysql-5.1.16-beta-linux-i686-icc-glibc23.tar.gz

    mv mysql-5.1.16-beta-linux-i686-icc-glibc23 /usr/local/mysql-5.1

    修改启动脚本,启动


    检查当前版本是否支持分区:

    SHOW VARIABLES LIKE '%partition%';

    测试带分区的表:(From 手册)

    CREATE TABLE tr (id INT, name VARCHAR(50), purchased DATE)
    PARTITION BY RANGE(YEAR(purchased))
    (
    PARTITION p0 VALUES LESS THAN (1990),
    PARTITION p1 VALUES LESS THAN (1995),
    PARTITION p2 VALUES LESS THAN (2000),
    PARTITION p3 VALUES LESS THAN (2005)
    );


    INSERT INTO tr VALUES
    (1, 'desk organiser', '2003-10-15'),
    (2, 'CD player', '1993-11-05'),
    (3, 'TV set', '1996-03-10'),
    (4, 'bookcase', '1982-01-10'),
    (5, 'exercise bike', '2004-05-09'),
    (6, 'sofa', '1987-06-05'),
    (7, 'popcorn maker', '2001-11-22'),
    (8, 'aquarium', '1992-08-04'),
    (9, 'study desk', '1984-09-16'),
    (10, 'lava lamp', '1998-12-25');

    从服务器上可以看到文件结构为:

    10.10.82.83 [testuser]$ l
    total 404M
    -rw-rw---- 1 mysql mysql 8.5K 2007-03-14 15:26:29 tr.frm
    -rw-rw---- 1 mysql mysql 32 2007-03-14 15:26:29 tr.par
    -rw-rw---- 1 mysql mysql 68 2007-03-14 15:26:48 tr#P#p0.MYD
    -rw-rw---- 1 mysql mysql 1.0K 2007-03-14 15:26:48 tr#P#p0.MYI
    -rw-rw---- 1 mysql mysql 48 2007-03-14 15:26:48 tr#P#p1.MYD
    -rw-rw---- 1 mysql mysql 1.0K 2007-03-14 15:26:48 tr#P#p1.MYI
    -rw-rw---- 1 mysql mysql 44 2007-03-14 15:26:48 tr#P#p2.MYD
    -rw-rw---- 1 mysql mysql 1.0K 2007-03-14 15:26:48 tr#P#p2.MYI
    -rw-rw---- 1 mysql mysql 84 2007-03-14 15:26:48 tr#P#p3.MYD
    -rw-rw---- 1 mysql mysql 1.0K 2007-03-14 15:26:48 tr#P#p3.MYI

    说明创建分区成功。

    测试分区管理,删除:

    ALTER TABLE tr DROP PARTITION p2;

    添加:

    ALTER TABLE tr ADD PARTITION (PARTITION p2 VALUES LESS THAN (2000));

    注意:

    1,删除分区后,分区内的数据也不可恢复的删除了。

    2,只有range和list分区可以进行无数据影响的添加操作。其他种类的分区,不要进行分区管理。或者是,先把数据全导出,改变分区结构后再导入。

    3,作为分区依据的列值是可以修改的,修改后会转移到对应分区中。(这点很有意义哟~)

    下面是实战:

    CREATE TABLE `user` (
    `id` int(11) not null auto_increment,
    `cn` varchar(22) not null default '',
    `nickname` varchar(32) not null default '',
    `genderid` tinyint(1) unsigned default null,
    `artnum` int(8) unsigned default '0',
    `elitenum` int(8) unsigned default '0',
    `commnum` int(11) not null default '0',
    `delnum` int(8) unsigned default '0',
    `loginnum` int(8) unsigned default '1',
    `linetime` int(8) unsigned default '0',
    `lastdate` datetime default null,
    `power` int(10) not null default '0',
    `score` int(8) default '0',
    `inputdate` datetime default null,
    `finallydate` datetime default null,
    `ad3` tinyint(4) not null default '0',
    primary key (`id`),
    key `nickname` (`nickname`),
    key `finallydate` (`finallydate`)
    ) ENGINE=MyISAM DEFAULT CHARSET=gb2312 AUTO_INCREMENT=4589157
    PARTITION BY RANGE(id)
    (
    PARTITION p5 VALUES LESS THAN (5000000),
    PARTITION p6 VALUES LESS THAN (6000000),
    PARTITION p7 VALUES LESS THAN (7000000),
    PARTITION p8 VALUES LESS THAN (8000000),
    PARTITION p9 VALUES LESS THAN (9000000),
    PARTITION p10 VALUES LESS THAN (10000000),
    PARTITION p11 VALUES LESS THAN (11000000),
    PARTITION p12 VALUES LESS THAN (12000000),
    PARTITION p13 VALUES LESS THAN (13000000),
    PARTITION p14 VALUES LESS THAN (14000000),
    PARTITION p15 VALUES LESS THAN (15000000),
    PARTITION p16 VALUES LESS THAN (16000000),
    PARTITION p0 VALUES LESS THAN MAXVALUE
    );

    关入400万数据,性能对比:

    (待补充)

    方案二,使用Key进行划分

    create table `user2` (
    `cn` varchar(22) not null default '',
    `nickname` varchar(32) not null default '',
    `genderid` tinyint(1) unsigned default null,
    `artnum` int(8) unsigned default '0',
    `elitenum` int(8) unsigned default '0',
    `commnum` int(11) not null default '0',
    `delnum` int(8) unsigned default '0',
    `loginnum` int(8) unsigned default '1',
    `linetime` int(8) unsigned default '0',
    `lastdate` datetime default null,
    `power` int(10) not null default '0',
    `score` int(8) default '0',
    `inputdate` datetime default null,
    `finallydate` datetime default null,
    `ad3` tinyint(4) not null default '0',
    primary key (`cn`),
    key `nickname` (`nickname`),
    key `finallydate` (`finallydate`)
    ) engine=myisam default charset=gb2312
    partition by key(cn)
    partitions 3;

    增加分区数:ALTER TABLE user2 ADD PARTITION PARTITIONS 10;

    减少分区数:ALTER TABLE user2 COALESCE PARTITION 6;

    增加和减少分区时,数据并不丢失。

    一个不很方便的地方在于,当划分分区时,有unique的列时可能会拒绝操作。

    另外,关于性能方面。range,list划分会对提高性能有帮助,但是作用多大,还是跟具体应用有关的。可以肯定的是,不会比我们传统上用程序逻辑将数据分散到多个表的方法效率更高,只是更方便而已。对于hash,key的划分,需要消耗大量计算时间,如果设计不当,可能还会造成性能的下降。所以,这也是我没有立即写性能对比数据的原因。

    以后在实际应用中,采用分区技术确实能够有效提高性能得到验证后,再回来补充性能部分的内容。

    ~~呵呵~~



    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/119

    OpenVPN快速应用

    OpenVPN快速应用

    jimmy | 31 一月, 2007 21:01

    本文要点:

    如何快速搭建一个单Client-Server的VPN应用,适用Windows的客户端,Linux的服务器端。目的是能够快速连入机房环境,更有效的维护和管理服务系统。

    对于如何用OpenVPN构架一个企业的通用VPN应用,本文并不适宜。

    参考:

    OpenVPNTM Static Key Mini-HOWTO


    首先,下载安装OpenVPN。

    (假定: 当前服务器外网IP为 www.myserver.com ,内网IP为 192.168.0.3)

    生成公共Key:

    openvpn --genkey --secret openvpn_static.key

    将Key放到C:下,然后复制一份到服务器/etc下。


    服务器端编辑配置文件:

    #vi /etc/openvpn.cfg

    dev tun
    ifconfig 10.8.0.1 10.8.0.2
    secret /etc/openvpn_static.key
    keepalive 10 60
    ping-timer-rem
    persist-tun
    persist-key
    user root
    group root
    daemon

    启动服务:

    /usr/local/sbin/openvpn /etc/openvpn.cfg

    开启IP转发:

    echo 1 > /proc/sys/net/ipv4/ip_forward

    编辑客户端配置文件: c:openvpn.ovpn


    remote www.myserver.com
    />dev tun
    ifconfig 10.8.0.2 10.8.0.1
    secret C:openvpn_static.key
    keepalive 10 60
    ping-timer-rem
    persist-tun
    persist-key
    route 192.168.0.0 255.255.0.0


    配置文件上点右键,选择“start openvpn on this config file”,即可连入服务器。

    同时,可以访问到与服务器同一局网的机器。对于需要连入服务器环境管理多台机器是非常方便的。

    ~~呵呵~~



    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/117

    关于团队精神的探索

    关于团队精神的探索

    jimmy | 21 一月, 2007 22:55

    其实谈不上什么探索,只是一些自己的困惑和思考。上周本来使安排了时间进行组员的谈话的,中间意外插入了跟另一个Team的Leader一席交谈,有很多的感触。于是决定花时间先反思一下,整理清自己的思路。

    当然,对自己不足的思考是很多的,就不在这里敲了,免得变成检讨书。这些反思继续向深处挖掘,便有一大堆的问题。这些个问题缠绕在一起,似乎每一个的答案都依赖着其它的问题,像是一个死循环。比起写程序难的多。

    团队精神依赖团队目标——团队精神如果没有统一目标来支撑,就会流于空洞和不切实际;

    团队目标依赖个人努力——统一的目标没有每个人的努力推动,就不可能得到贯彻和实现;

    个人努力依赖个人目标——最强的个人动力的根源,不是来自管理手段或者是逼迫,而是来自于对个人目标或者个人理想的不懈追求;

    个人目标依赖团队精神——每个人的个人目标,很难是一样的,甚至常常会产生冲突或造成不利的内耗。多样的个人目标,如何统一到团队的共同目标上去?这就需要团队精神。

    按程序的求解方式,就陷入循环,似乎得不到解决了。每一个环节的优化都对下一级提出更多依赖,从而使如果确定方式方法变得异常艰难。但是如果反过来看,每一个环节的改善都是为其上的环节构筑更好的支撑,整个循环就变得更顺畅。在处理每个环节时,不是着眼在环节本身依赖的条件,而是如何能够更有利于其上层的环节,很多问题就变得清晰起来。

    1,个体的差异是必须承认和接受的。

    作为团队的Leader,需要关心团队中的每一个人,从了解到理解,从理解到尊重。每个人要的都是不同的,有的人是怀抱着长远的理想,有的人是渴望自身成功,有的人是为了有稳定愉快的工作环境,有的人是为了感激,有的人是为了物质收益…… 所有这些,没有绝对的标准去衡量哪个比哪个更好,更没有什么对或者错。每个人的个人选择,都是需要尊重的。只有基于理解和尊重之上的帮助,才能真正帮助别人。这是调动每个人充分发挥个人才能的基础。

    2,每个人都应该为自己的目标或者理想去努力。

    时间,是真正属于自己的东西,正是时间构成了我们的生命。我们每一天的时间,流逝了就不再了。每天,投入到工作中的时间也是如此。既然每天把时间放在这里,就应该对其负责,不能任其白白过去,应该不断努力使时间更有意义。作为团队的Leader,同时作为朋友,负有责任去不断提醒,更重要是不断帮助大家客服困难。在这里,方式和方法就很重要,我还很欠缺,不知道如何去做,也需要大家来帮助我。比如,如何改进交流和沟通?如何鼓励和激励?如何坦然去面对缺点和不足?……如果我能够在这方面有进步,会另文与大家分享。

    可以确定的是,只有每个人的积极性得到调动,才能更好的工作,从而推动整体工作顺利向前,去达成团队的共同目标。

    3,团队的共同目标。

    每个人都渴望是一个优秀团队的一员,因团队的成功而骄傲,因自己的贡献而使团队自豪。只有团队的进步,取得成绩,个人才能得到更多的回报。团队目标必须明确,而且需要有明确的规则和制度去保证。在这方面有很多经验可以借鉴,我们也做了很多的尝试和努力。

    去年看了不少关于体育的影片,《Remember The Titans》、《Glory Road》、《Invincible》、《Gridiron Gang》、《Facing The Giants》,有很多感触,真的很想能够跟团队在一起观看一起分享影片中那种团队获得成功的感动。在向共同目标努力的过程中,大家一起克服困难,互相帮助,团队的精神才会深入每个人的内心,感召和改变着每一个人。

    4,我们需要的团队精神。

    我还没有完全想好。从我的感觉上,应该具备这些要素:积极向上,愉快坦诚,注重沟通,互相帮助,敢挑重担,勇于负责。应该可以影响到个体,更积极自觉的挖掘和发现个人目标与团队目标的一致性,更主动自愿的将自己的个人目标与团队中其他人的个人目标保持和谐和一致。在这样的团队精神下,能够协助个人建立现实环境更合理更可实现的个人目标。

    单纯用业绩来衡量一个团队是否优秀的团队,常常是片面的,因为里面有很多环境、时间的因素。我觉得能够在以上所说四个环节上不断改进优化,从而达成各环节互相推动的正循环,才算是一个优秀的团队。因为只有这样的团队,才能保证在客观条件下达成最好的成果,才能保证团队中的成员更顺利更健康的发展。

    为自己,为大家,让我们一起积极努力吧。

    ~~呵呵~~

    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/116

    SQLite函数速查

    SQLite函数速查

    jimmy | 17 一月, 2007 11:15

    常用操作DEMO:

    <?
    /**
     *
     * SQLite ���������
     *
     * by jimmy 20060911
     */
    include("../include/config.inc.php");
    echo "<hr>PDO��<hr>";
    try { 
        $conn = new PDO('sqlite2:jimmy.sqlite'); 
        $string = 'working...<br>';
        echo $conn->quote($string);    echo 'PDO Connection  Ok','<br>'; 
        $conn->beginTransaction(); 
        $conn->exec("INSERT INTO test values(null,'pdo','pdo_test...')"); 
     echo $conn->commit();
        echo 'A Transaction: Insert Data ok','<br>'; 
    } catch (PDOException $e) { 
       echo 'Connection failed: ' . $e->getMessage(); 
    } echo 'get all data','<br>';
    $query=$conn->query('SELECT * FROM test'); 
    $result=$query->fetchAll();
    trace( $result); echo 'get data line by line', '<br>n';
    $query = $conn->prepare("SELECT * FROM test where id > ?");
    if ($query->execute(array('1'))) {
      while ($row = $query->fetch()) {
        trace($row);
      }
    }
    echo "<hr>SQLiteç±»<hr>";
    $db = new SQLiteDatabase('jimmy2');
    $db->query("INSERT INTO test VALUES(null,'sqlite','sqlite class')");
    echo 'get data all','<br>';
    $result = $db->arrayQuery('SELECT * FROM test LIMIT 25', SQLITE_ASSOC);
    foreach ($result as $entry) {
        echo 'title: ' . $entry['title'] . '  content: ' . $entry['content'];
    }
    echo '<br>get data line by line','<br>';
    $result = $db->query("SELECT * FROM test LIMIT 25");
    while($row=$result->fetch()){
     print_r($row);
    }
    echo "<hr>pear MDB2<hr>";
    echo "wait...";echo "<hr>Zendç±»<hr>";
    require_once 'Zend.php';
    Zend::loadClass('Zend_Db');
    $params = array ('dbname' => 'jimmy.sqlite','sqlite2' => TRUE);
    $db = Zend_Db::factory('PDO_SQLITE', $params);try {    
        $db->beginTransaction();
        $db->query("INSERT INTO test VALUES(null,'sqlite','zend sqlite')");   
        echo "<br>Insert OK ".$db->lastInsertId();; 
        $db->commit();
    } 
    catch (Exception $e) {    
        $db->rollBack();    
        echo $e->getMessage();
    }
    $result = $db->fetchAll("SELECT * FROM test");
    trace($result);$query = $db->query("SELECT * FROM test");
    while($row=$query->fetch()){
        trace($row);
    }
    ?>

    常用语法:

    结构定义
    CREATE TABLE

    创建新表。

    语法:

    sql-command ::= CREATE [TEMP | TEMPORARY] TABLE table-name ( column-def [, column-def]*
    [, constraint]*
    )
    sql-command ::= CREATE [TEMP | TEMPORARY] TABLE [database-name.] table-name AS select-statement
    column-def ::= name [type] [[CONSTRAINT name] column-constraint]*
    type ::= typename |typename ( number ) |typename ( number , number )
    column-constraint ::= NOT NULL [ conflict-clause ] |
    PRIMARY KEY
    [sort-order] [ conflict-clause ] |
    UNIQUE
    [ conflict-clause ] |
    CHECK (
    expr ) [ conflict-clause ] |
    DEFAULT
    value |
    COLLATE
    collation-name
    constraint ::= PRIMARY KEY ( column-list ) [ conflict-clause ] |
    UNIQUE (
    column-list ) [ conflict-clause ] |
    CHECK (
    expr ) [ conflict-clause ]
    conflict-clause ::= ON CONFLICT conflict-algorithm
    CREATE VIEW

    创建一个视图(虚拟表),该表以另一种方式表示一个或多个表中的数据。

    语法:

    sql-command ::= CREATE [TEMP | TEMPORARY] VIEW [database-name.] view-name AS select-statement

    例子:CREATE VIEW master_view AS
    SELECT * FROM sqlite_master WHERE type='view';
    说明:
    创建一个名为master_view的视图,其中包括sqlite_master这个表中的所有视图表。
    CREATE TRIGGER

    创建触发器,触发器是一种特殊的存储过程,在用户试图对指定的表执行指定的数据修改语句时自动执行。

    语法:

    sql-statement ::= CREATE [TEMP | TEMPORARY] TRIGGER trigger-name [ BEFORE | AFTER ]database-event ON [database-name .] table-nametrigger-action
    sql-statement ::= CREATE [TEMP | TEMPORARY] TRIGGER trigger-name INSTEAD OFdatabase-event ON [database-name .] view-nametrigger-action
    database-event ::= DELETE |
    INSERT
    |
    UPDATE
    |
    UPDATE OF
    column-list
    trigger-action ::= [ FOR EACH ROW | FOR EACH STATEMENT ] [ WHEN expression ]
    BEGIN
    trigger-step ; [ trigger-step ; ]*
    END
    trigger-step ::= update-statement | insert-statement | delete-statement | select-statement

    例子:
    CREATE TRIGGER update_customer_address UPDATE OF address ON customers
    BEGIN
    UPDATE orders SET address = new.address WHERE customer_name = old.name;
    END;
    说明:创建了一个名为update_customer_address的触发器,当用户更新customers表中的address字段时,将触发并更新orders表中的address字段为新的值。
    比如执行如下一条语句:
    UPDATE customers SET address = '1 Main St.' WHERE name = 'Jack Jones';
    数据库将自动执行如下语句:
    UPDATE orders SET address = '1 Main St.' WHERE customer_name = 'Jack Jones';

    CREATE INDEX

    为给定表或视图创建索引。

    语法:

    sql-statement ::= CREATE [UNIQUE] INDEX index-name
    ON
    [database-name .] table-name ( column-name [, column-name]* )[ ON CONFLICT conflict-algorithm ]
    column-name ::= name [ COLLATE collation-name] [ ASC | DESC ]

    例子:CREATE INDEX idx_email ON customers (email);
    说明:
    为customers表中的email创建一个名为idx_email的字段。

    结构删除
    DROP TABLE

    删除表定义及该表的所有索引。

    语法:

    sql-command ::= DROP TABLE [database-name.] table-name
    例子:
    DROP TABLE customers;
    DROP VIEW

    删除一个视图。

    语法:

    sql-command ::= DROP VIEW view-name

    例子:
    DROP VIEW master_view;

    DROP TRIGGER

    删除一个触发器。

    语法:

    sql-statement ::= DROP TRIGGER [database-name .] trigger-name

    例子:DROP TRIGGER update_customer_address;

    DROP INDEX

    删除一个索引。

    语法:

    sql-command ::= DROP INDEX [database-name .] index-name

    例子:DROP INDEX idx_email;

    数据操作
    INSERT

    将新行插入到表。

    语法:

    sql-statement ::= INSERT [OR conflict-algorithm] INTO [database-name .] table-name [(column-list)] VALUES(value-list) |
    INSERT
    [OR conflict-algorithm] INTO [database-name .] table-name [(column-list)] select-statement
    UPDATE

    更新表中的现有数据。

    语法:

    sql-statement ::= UPDATE [ OR conflict-algorithm ] [database-name .] table-name
    SET
    assignment [, assignment]*[WHERE expr]
    assignment ::= column-name = expr
    DELETE

    从表中删除行。

    语法:

    sql-statement ::= DELETE FROM [database-name .] table-name [WHERE expr]
    SELECT

    从表中检索数据。

    语法:

    sql-statement ::= SELECT [ALL | DISTINCT] result [FROM table-list][WHERE expr][GROUP BY expr-list][HAVING expr][compound-op select]*[ORDER BY sort-expr-list][LIMIT integer [( OFFSET | , ) integer]]
    result ::= result-column [, result-column]*
    result-column ::= * | table-name . * | expr [ [AS] string ]
    table-list ::= table [join-op table join-args]*
    table ::= table-name [AS alias] |
    (
    select ) [AS alias]
    join-op ::= , | [NATURAL] [LEFT | RIGHT | FULL] [OUTER | INNER | CROSS] JOIN
    join-args ::= [ON expr] [USING ( id-list )]
    sort-expr-list ::= expr [sort-order] [, expr [sort-order]]*
    sort-order ::= [ COLLATE collation-name ] [ ASC | DESC ]
    compound_op ::= UNION | UNION ALL | INTERSECT | EXCEPT
    REPLACE

    类似INSERT

    语法:

    sql-statement ::= REPLACE INTO [database-name .] table-name [( column-list )] VALUES ( value-list ) |
    REPLACE INTO
    [database-name .] table-name [( column-list )] select-statement
    事务处理
    BEGIN TRANSACTION

    标记一个事务的起始点。

    语法:

    sql-statement ::= BEGIN [TRANSACTION [name]]
    END TRANSACTION

    标记一个事务的终止。

    语法:

    sql-statement ::= END [TRANSACTION [name]]
    COMMIT TRANSACTION

    标志一个事务的结束。

    语法:

    sql-statement ::= COMMIT [TRANSACTION [name]]
    ROLLBACK TRANSACTION

    将事务回滚到事务的起点。

    语法:

    sql-statement ::= ROLLBACK [TRANSACTION [name]]
    其他操作
    COPY

    主要用于导入大量的数据。

    语法:

    sql-statement ::= COPY [ OR conflict-algorithm ] [database-name .] table-name FROM filename[ USING DELIMITERS delim ]
    例子:
    COPY customers FROM customers.csv;
    EXPLAIN

    语法:

    sql-statement ::= EXPLAIN sql-statement
    PRAGMA

    语法:

    sql-statement ::= PRAGMA name [= value] |
    PRAGMA
    function(arg)
    VACUUM

    语法:

    sql-statement ::= VACUUM [index-or-table-name]
    ATTACH DATABASE

    附加一个数据库到当前的数据库连接。

    语法:

    sql-statement ::= ATTACH [DATABASE] database-filename AS database-name
    DETTACH DATABASE

    从当前的数据库分离一个使用ATTACH DATABASE附加的数据库。

    语法:

    sql-command ::= DETACH [DATABASE] database-name

    SQLite内建函数表

    算术函数
    abs(X) 返回给定数字表达式的绝对值。
    max(X,Y[,...]) 返回表达式的最大值。
    min(X,Y[,...]) 返回表达式的最小值。
    random(*) 返回随机数。
    round(X[,Y]) 返回数字表达式并四舍五入为指定的长度或精度。
    字符处理函数
    length(X) 返回给定字符串表达式的字符个数。
    lower(X) 将大写字符数据转换为小写字符数据后返回字符表达式。
    upper(X) 返回将小写字符数据转换为大写的字符表达式。
    substr(X,Y,Z) 返回表达式的一部分。
    randstr()
    quote(A)
    like(A,B) 确定给定的字符串是否与指定的模式匹配。
    glob(A,B)
    条件判断函数
    coalesce(X,Y[,...])
    ifnull(X,Y)
    nullif(X,Y)
    集合函数
    avg(X) 返回组中值的平均值。
    count(X) 返回组中项目的数量。
    max(X) 返回组中值的最大值。
    min(X) 返回组中值的最小值。
    sum(X) 返回表达式中所有值的和。
    其他函数
    typeof(X) 返回数据的类型。
    last_insert_rowid() 返回最后插入的数据的ID。
    sqlite_version(*) 返回SQLite的版本。
    change_count() 返回受上一语句影响的行数。
    last_statement_change_count()

    ~~呵呵~~

    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/115

    SQLite函数速查

    SQLite函数速查

    jimmy | 17 一月, 2007 11:15

    常用操作DEMO:

    <?
    /**
    *
    * SQLite ���������
    *
    * by jimmy 20060911
    */

    include("../include/config.inc.php");
    echo "<hr>PDO��<hr>";
    try {
    $conn = new PDO('sqlite2:jimmy.sqlite');
    $string = 'working...<br>';
    echo $conn->quote($string); echo 'PDO Connection Ok','<br>';
    $conn->beginTransaction();
    $conn->exec("INSERT INTO test values(null,'pdo','pdo_test...')");
    echo $conn->commit();
    echo 'A Transaction: Insert Data ok','<br>';
    } catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
    } echo 'get all data','<br>';
    $query=$conn->query('SELECT * FROM test');
    $result=$query->fetchAll();
    trace( $result); echo 'get data line by line', '<br>n';
    $query = $conn->prepare("SELECT * FROM test where id > ?");
    if ($query->execute(array('1'))) {
    while ($row = $query->fetch()) {
    trace($row);
    }
    }
    echo "<hr>SQLiteç±»<hr>";
    $db = new SQLiteDatabase('jimmy2');
    $db->query("INSERT INTO test VALUES(null,'sqlite','sqlite class')");
    echo 'get data all','<br>';
    $result = $db->arrayQuery('SELECT * FROM test LIMIT 25', SQLITE_ASSOC);
    foreach ($result as $entry) {
    echo 'title: ' . $entry['title'] . ' content: ' . $entry['content'];
    }
    echo '<br>get data line by line','<br>';
    $result = $db->query("SELECT * FROM test LIMIT 25");
    while($row=$result->fetch()){
    print_r($row);
    }
    echo "<hr>pear MDB2<hr>";
    echo "wait...";echo "<hr>Zendç±»<hr>";
    require_once 'Zend.php';
    Zend::loadClass('Zend_Db');
    $params = array ('dbname' => 'jimmy.sqlite','sqlite2' => TRUE);
    $db = Zend_Db::factory('PDO_SQLITE', $params);try {
    $db->beginTransaction();
    $db->query("INSERT INTO test VALUES(null,'sqlite','zend sqlite')");
    echo "<br>Insert OK ".$db->lastInsertId();;
    $db->commit();
    }
    catch (Exception $e) {
    $db->rollBack();
    echo $e->getMessage();
    }
    $result = $db->fetchAll("SELECT * FROM test");
    trace($result);$query = $db->query("SELECT * FROM test");
    while($row=$query->fetch()){
    trace($row);
    }
    ?>

    常用语法:

    结构定义
    CREATE TABLE

    创建新表。

    语法:

    sql-command ::= CREATE [TEMP | TEMPORARY] TABLE table-name ( column-def [, column-def]*
    [, constraint]*
    )
    sql-command ::= CREATE [TEMP | TEMPORARY] TABLE [database-name.] table-name AS select-statement
    column-def ::= name [type] [[CONSTRAINT name] column-constraint]*
    type ::= typename |typename ( number ) |typename ( number , number )
    column-constraint ::= NOT NULL [ conflict-clause ] |
    PRIMARY KEY
    [sort-order] [ conflict-clause ] |
    UNIQUE
    [ conflict-clause ] |
    CHECK (
    expr ) [ conflict-clause ] |
    DEFAULT
    value |
    COLLATE
    collation-name
    constraint ::= PRIMARY KEY ( column-list ) [ conflict-clause ] |
    UNIQUE (
    column-list ) [ conflict-clause ] |
    CHECK (
    expr ) [ conflict-clause ]
    conflict-clause ::= ON CONFLICT conflict-algorithm
    CREATE VIEW

    创建一个视图(虚拟表),该表以另一种方式表示一个或多个表中的数据。

    语法:

    sql-command ::= CREATE [TEMP | TEMPORARY] VIEW [database-name.] view-name AS select-statement

    例子:CREATE VIEW master_view AS
    SELECT * FROM sqlite_master WHERE type='view';
    说明:
    创建一个名为master_view的视图,其中包括sqlite_master这个表中的所有视图表。
    CREATE TRIGGER

    创建触发器,触发器是一种特殊的存储过程,在用户试图对指定的表执行指定的数据修改语句时自动执行。

    语法:

    sql-statement ::= CREATE [TEMP | TEMPORARY] TRIGGER trigger-name [ BEFORE | AFTER ]database-event ON [database-name .] table-nametrigger-action
    sql-statement ::= CREATE [TEMP | TEMPORARY] TRIGGER trigger-name INSTEAD OFdatabase-event ON [database-name .] view-nametrigger-action
    database-event ::= DELETE |
    INSERT
    |
    UPDATE
    |
    UPDATE OF
    column-list
    trigger-action ::= [ FOR EACH ROW | FOR EACH STATEMENT ] [ WHEN expression ]
    BEGIN
    trigger-step ; [ trigger-step ; ]*
    END
    trigger-step ::= update-statement | insert-statement | delete-statement | select-statement

    例子:
    CREATE TRIGGER update_customer_address UPDATE OF address ON customers
    BEGIN
    UPDATE orders SET address = new.address WHERE customer_name = old.name;
    END;
    说明:创建了一个名为update_customer_address的触发器,当用户更新customers表中的address字段时,将触发并更新orders表中的address字段为新的值。
    比如执行如下一条语句:
    UPDATE customers SET address = '1 Main St.' WHERE name = 'Jack Jones';
    数据库将自动执行如下语句:
    UPDATE orders SET address = '1 Main St.' WHERE customer_name = 'Jack Jones';

    CREATE INDEX

    为给定表或视图创建索引。

    语法:

    sql-statement ::= CREATE [UNIQUE] INDEX index-name
    ON
    [database-name .] table-name ( column-name [, column-name]* )[ ON CONFLICT conflict-algorithm ]
    column-name ::= name [ COLLATE collation-name] [ ASC | DESC ]

    例子:CREATE INDEX idx_email ON customers (email);
    说明:
    为customers表中的email创建一个名为idx_email的字段。

    结构删除
    DROP TABLE

    删除表定义及该表的所有索引。

    语法:

    sql-command ::= DROP TABLE [database-name.] table-name
    例子:
    DROP TABLE customers;
    DROP VIEW

    删除一个视图。

    语法:

    sql-command ::= DROP VIEW view-name

    例子:
    DROP VIEW master_view;

    DROP TRIGGER

    删除一个触发器。

    语法:

    sql-statement ::= DROP TRIGGER [database-name .] trigger-name

    例子:DROP TRIGGER update_customer_address;

    DROP INDEX

    删除一个索引。

    语法:

    sql-command ::= DROP INDEX [database-name .] index-name

    例子:DROP INDEX idx_email;

    数据操作
    INSERT

    将新行插入到表。

    语法:

    sql-statement ::= INSERT [OR conflict-algorithm] INTO [database-name .] table-name [(column-list)] VALUES(value-list) |
    INSERT
    [OR conflict-algorithm] INTO [database-name .] table-name [(column-list)] select-statement
    UPDATE

    更新表中的现有数据。

    语法:

    sql-statement ::= UPDATE [ OR conflict-algorithm ] [database-name .] table-name
    SET
    assignment [, assignment]*[WHERE expr]
    assignment ::= column-name = expr
    DELETE

    从表中删除行。

    语法:

    sql-statement ::= DELETE FROM [database-name .] table-name [WHERE expr]
    SELECT

    从表中检索数据。

    语法:

    sql-statement ::= SELECT [ALL | DISTINCT] result [FROM table-list][WHERE expr][GROUP BY expr-list][HAVING expr][compound-op select]*[ORDER BY sort-expr-list][LIMIT integer [( OFFSET | , ) integer]]
    result ::= result-column [, result-column]*
    result-column ::= * | table-name . * | expr [ [AS] string ]
    table-list ::= table [join-op table join-args]*
    table ::= table-name [AS alias] |
    (
    select ) [AS alias]
    join-op ::= , | [NATURAL] [LEFT | RIGHT | FULL] [OUTER | INNER | CROSS] JOIN
    join-args ::= [ON expr] [USING ( id-list )]
    sort-expr-list ::= expr [sort-order] [, expr [sort-order]]*
    sort-order ::= [ COLLATE collation-name ] [ ASC | DESC ]
    compound_op ::= UNION | UNION ALL | INTERSECT | EXCEPT
    REPLACE

    类似INSERT

    语法:

    sql-statement ::= REPLACE INTO [database-name .] table-name [( column-list )] VALUES ( value-list ) |
    REPLACE INTO
    [database-name .] table-name [( column-list )] select-statement
    事务处理
    BEGIN TRANSACTION

    标记一个事务的起始点。

    语法:

    sql-statement ::= BEGIN [TRANSACTION [name]]
    END TRANSACTION

    标记一个事务的终止。

    语法:

    sql-statement ::= END [TRANSACTION [name]]
    COMMIT TRANSACTION

    标志一个事务的结束。

    语法:

    sql-statement ::= COMMIT [TRANSACTION [name]]
    ROLLBACK TRANSACTION

    将事务回滚到事务的起点。

    语法:

    sql-statement ::= ROLLBACK [TRANSACTION [name]]
    其他操作
    COPY

    主要用于导入大量的数据。

    语法:

    sql-statement ::= COPY [ OR conflict-algorithm ] [database-name .] table-name FROM filename[ USING DELIMITERS delim ]
    例子:
    COPY customers FROM customers.csv;
    EXPLAIN

    语法:

    sql-statement ::= EXPLAIN sql-statement
    PRAGMA

    语法:

    sql-statement ::= PRAGMA name [= value] |
    PRAGMA
    function(arg)
    VACUUM

    语法:

    sql-statement ::= VACUUM [index-or-table-name]
    ATTACH DATABASE

    附加一个数据库到当前的数据库连接。

    语法:

    sql-statement ::= ATTACH [DATABASE] database-filename AS database-name
    DETTACH DATABASE

    从当前的数据库分离一个使用ATTACH DATABASE附加的数据库。

    语法:

    sql-command ::= DETACH [DATABASE] database-name

    SQLite内建函数表

    算术函数
    abs(X) 返回给定数字表达式的绝对值。
    max(X,Y[,...]) 返回表达式的最大值。
    min(X,Y[,...]) 返回表达式的最小值。
    random(*) 返回随机数。
    round(X[,Y]) 返回数字表达式并四舍五入为指定的长度或精度。
    字符处理函数
    length(X) 返回给定字符串表达式的字符个数。
    lower(X) 将大写字符数据转换为小写字符数据后返回字符表达式。
    upper(X) 返回将小写字符数据转换为大写的字符表达式。
    substr(X,Y,Z) 返回表达式的一部分。
    randstr()
    quote(A)
    like(A,B) 确定给定的字符串是否与指定的模式匹配。
    glob(A,B)
    条件判断函数
    coalesce(X,Y[,...])
    ifnull(X,Y)
    nullif(X,Y)
    集合函数
    avg(X) 返回组中值的平均值。
    count(X) 返回组中项目的数量。
    max(X) 返回组中值的最大值。
    min(X) 返回组中值的最小值。
    sum(X) 返回表达式中所有值的和。
    其他函数
    typeof(X) 返回数据的类型。
    last_insert_rowid() 返回最后插入的数据的ID。
    sqlite_version(*) 返回SQLite的版本。
    change_count() 返回受上一语句影响的行数。
    last_statement_change_count()

    ~~呵呵~~

    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/115

    内存数据库及其效率

    内存数据库及其效率

    jimmy | 16 一月, 2007 21:22

    将MySQL放置到Ramdisk中,考核其效率变化。

    数据准备:

    从真实论坛中抽取8万条数据,数据库大小为18M

    测试程序:

    读取——随即选择版面、标签、页数,从数据库中排序取出数据。

    写入——在读取测试基础上,每50次读取进行一次Update操作。

    正常效率:

    进行1000次读取:第一遍15.60413312912 ,第二遍11.677510023117 seconds

    进行1000次读取+写入:第一遍16.505848169327,第二遍16.751178026199 seconds


    创建内存盘:

    查看可用于Ramdisk的内存大小:

    dmesg | grep RAMDISK

    16M,太小。修改启动文件,改变大小:

    vi /etc/grub.conf

    # grub.conf generated by anaconda
    #
    # Note that you do not have to rerun grub after making changes to this file
    # NOTICE: You do not have a /boot partition. This means that
    # all kernel and initrd paths are relative to /, eg.
    # root (hd0,0)
    # kernel /boot/vmlinuz-version ro root=/dev/cciss/c0d0p1
    # initrd /boot/initrd-version.img
    #boot=/dev/cciss/c0d0
    default=0
    timeout=5
    splashimage=(hd0,0)/boot/grub/splash.xpm.gz
    hiddenmenu
    title Red Hat Enterprise Linux AS (2.6.9-42.ELsmp)
    root (hd0,0)
    kernel /boot/vmlinuz-2.6.9-42.ELsmp ro root=LABEL=/ ramdisk_size=1000000
    initrd /boot/initrd-2.6.9-42.ELsmp.img
    title Red Hat Enterprise Linux AS-up (2.6.9-42.EL)
    root (hd0,0)
    kernel /boot/vmlinuz-2.6.9-42.EL ro root=LABEL=/
    initrd /boot/initrd-2.6.9-42.EL.img

    注意,是以K为单位的。

    重启,查看是否有效。

    # dmesg | grep RAMDISK
    RAMDISK driver initialized: 16 RAM disks of 1000000K size 1024 blocksize

    绑定内存盘到一个目录:

    # mount /dev/ram2 /opt/ram_db -t ramfs

    最好不要用ram0,留下给其它临时应用。

    (注:

    1,如果目录所在盘是ext3的,那么就不要用mk2ef格式内存盘,否则会导致无法mount。

    2,通过mount -tmpfs -o size=10M none /mnt/tmp的方法可以动态加载内存,并且灵活限制大小,是更时髦的方法。但是,在启动时指定内存可能会更有保障些。

    然后,把数据库复制到内存盘中:

    tar -cf - * | tar -C ../ram_db -xf -

    测试效率:

    进行1000次读取:第一遍13.552532196045 seconds ,第二遍12.20893907547 seconds

    进行1000次读取+写入:16.122632026672 seconds

    在SQLite中做类似测试,结论相似。

    【对较大数据表的测试】

    构建200万条主贴数据,表大小为350M。(注意,查询范围与上例测试有所不同,与上例无任何对比关系。仅代表内存与非内存的对比)

    常规:7.1019299030304 seconds

    内存:6.9220759868622 seconds

    结论:

    对于不是非常巨大的表,MySQL进行了较好的缓冲处理,性能瓶颈在算法上而不是磁盘操作上,用内存盘性能提升效率有限。

    对于过G的巨型表(千万数据 或 有TEXT字段),则其IO瓶颈更明显,是否采用内存能有效加速有待进一步测试。

    ~~呵呵~~

    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/114

    SQLite研究——内置函数

    SQLite研究——内置函数

    jimmy | 26 十二月, 2006 09:37

    Core Functions

    The core functions shown below are available by default. Additional functions may be written in C and added to the database engine using the sqlite3_create_function() API.

    abs(X) Return the absolute value of argument X.
    coalesce(X,Y,...) Return a copy of the first non-NULL argument. If all arguments are NULL then NULL is returned. There must be at least 2 arguments.
    glob(X,Y) This function is used to implement the "X GLOB Y" syntax of SQLite. The sqlite3_create_function() interface can be used to override this function and thereby change the operation of the GLOB operator.
    ifnull(X,Y) Return a copy of the first non-NULL argument. If both arguments are NULL then NULL is returned. This behaves the same as coalesce() above.
    last_insert_rowid() Return the ROWID of the last row insert from this connection to the database. This is the same value that would be returned from the sqlite_last_insert_rowid() API function.
    length(X) Return the string length of X in characters. If SQLite is configured to support UTF-8, then the number of UTF-8 characters is returned, not the number of bytes.
    like(X,Y [,Z]) This function is used to implement the "X LIKE Y [ESCAPE Z]" syntax of SQL. If the optional ESCAPE clause is present, then the user-function is invoked with three arguments. Otherwise, it is invoked with two arguments only. The sqlite_create_function() interface can be used to override this function and thereby change the operation of the LIKE operator. When doing this, it may be important to override both the two and three argument versions of the like() function. Otherwise, different code may be called to implement the LIKE operator depending on whether or not an ESCAPE clause was specified.
    load_extension(X)
    load_extension(X,Y)
    Load SQLite extensions out of the shared library file named X using the entry point Y. The result is a NULL. If Y is omitted then the default entry point of sqlite3_extension_init is used. This function raises an exception if the extension fails to load or initialize correctly.
    lower(X) Return a copy of string X will all characters converted to lower case. The C library tolower() routine is used for the conversion, which means that this function might not work correctly on UTF-8 characters.
    max(X,Y,...) Return the argument with the maximum value. Arguments may be strings in addition to numbers. The maximum value is determined by the usual sort order. Note that max() is a simple function when it has 2 or more arguments but converts to an aggregate function if given only a single argument.
    min(X,Y,...) Return the argument with the minimum value. Arguments may be strings in addition to numbers. The minimum value is determined by the usual sort order. Note that min() is a simple function when it has 2 or more arguments but converts to an aggregate function if given only a single argument.
    nullif(X,Y) Return the first argument if the arguments are different, otherwise return NULL.
    quote(X) This routine returns a string which is the value of its argument suitable for inclusion into another SQL statement. Strings are surrounded by single-quotes with escapes on interior quotes as needed. BLOBs are encoded as hexadecimal literals. The current implementation of VACUUM uses this function. The function is also useful when writing triggers to implement undo/redo functionality.
    random(*) Return a pseudo-random integer between -9223372036854775808 and +9223372036854775807.
    round(X)
    round(X,Y)
    Round off the number X to Y digits to the right of the decimal point. If the Y argument is omitted, 0 is assumed.
    soundex(X) Compute the soundex encoding of the string X. The string "?000" is returned if the argument is NULL. This function is omitted from SQLite by default. It is only available the -DSQLITE_SOUNDEX=1 compiler option is used when SQLite is built.
    sqlite_version(*) Return the version string for the SQLite library that is running. Example: "2.8.0"
    substr(X,Y,Z) Return a substring of input string X that begins with the Y-th character and which is Z characters long. The left-most character of X is number 1. If Y is negative the the first character of the substring is found by counting from the right rather than the left. If SQLite is configured to support UTF-8, then characters indices refer to actual UTF-8 characters, not bytes.
    typeof(X) Return the type of the expression X. The only return values are "null", "integer", "real", "text", and "blob". SQLite's type handling is explained in Datatypes in SQLite Version 3.
    upper(X) Return a copy of input string X converted to all upper-case letters. The implementation of this function uses the C library routine toupper() which means it may not work correctly on UTF-8 strings.

    Aggregate Functions

    The aggregate functions shown below are available by default. Additional aggregate functions written in C may be added using the sqlite3_create_function() API.

    In any aggregate function that takes a single argument, that argument can be preceeded by the keyword DISTINCT. In such cases, duplicate elements are filtered before being passed into the aggregate function. For example, the function "count(distinct X)" will return the number of distinct values of column X instead of the total number of non-null values in column X.

    avg(X) Return the average value of all non-NULL X within a group. String and BLOB values that do not look like numbers are interpreted as 0. The result of avg() is always a floating point value even if all inputs are integers.

    count(X)
    count(*)
    The first form return a count of the number of times that X is not NULL in a group. The second form (with no argument) returns the total number of rows in the group.
    max(X) Return the maximum value of all values in the group. The usual sort order is used to determine the maximum.
    min(X) Return the minimum non-NULL value of all values in the group. The usual sort order is used to determine the minimum. NULL is only returned if all values in the group are NULL.
    sum(X)
    total(X)
    Return the numeric sum of all non-NULL values in the group. If there are no non-NULL input rows then sum() returns NULL but total() returns 0.0. NULL is not normally a helpful result for the sum of no rows but the SQL standard requires it and most other SQL database engines implement sum() that way so SQLite does it in the same way in order to be compatible. The non-standard total() function is provided as a convenient way to work around this design problem in the SQL language.

    The result of total() is always a floating point value. The result of sum() is an integer value if all non-NULL inputs are integers. If any input to sum() is neither an integer or a NULL then sum() returns a floating point value which might be an approximation to the true sum.

    Sum() will throw an "integer overflow" exception if all inputs are integers or NULL and an integer overflow occurs at any point during the computation. Total() never throws an exception.

    Date and Time Functions Overview

    Five date and time functions are available, as follows:

    1. date( timestring, modifier, modifier, ...)
    2. time( timestring, modifier, modifier, ...)
    3. datetime( timestring, modifier, modifier, ...)
    4. julianday( timestring, modifier, modifier, ...)
    5. strftime( format, timestring, modifier, modifier, ...)

    All five functions take a time string as an argument. This time string may be followed by zero or more modifiers. The strftime() function also takes a format string as its first argument.

    The date() function returns the date in this format: YYYY-MM-DD. The time() function returns the time as HH:MM:SS. The datetime() function returns "YYYY-MM-DD HH:MM:SS". The julianday() function returns the number of days since noon in Greenwich on November 24, 4714 B.C. The julian day number is the preferred internal representation of dates. The strftime() routine returns the date formatted according to the format string specified as the first argument. The format string supports most, but not all, of the more common substitutions found in the strftime() function from the standard C library:

       %d  day of month
       %f  ** fractional seconds  SS.SSS
       %H  hour 00-24
       %j  day of year 001-366
       %J  ** Julian day number
       %m  month 01-12
       %M  minute 00-59
       %s  seconds since 1970-01-01
       %S  seconds 00-59
       %w  day of week 0-6  sunday==0
       %W  week of year 00-53
       %Y  year 0000-9999
       %%  %
    

    The %f and %J conversions are new. Notice that all of the other four functions could be expressed in terms of strftime().

       date(...)      ->  strftime("%Y-%m-%d", ...)
       time(...)      ->  strftime("%H:%M:%S", ...)
       datetime(...)  ->  strftime("%Y-%m-%d %H:%M:%S", ...)
       julianday(...) ->  strftime("%J", ...)
    

    The only reasons for providing functions other than strftime() is for convenience and for efficiency.

    Time Strings

    A time string can be in any of the following formats:

    1. YYYY-MM-DD
    2. YYYY-MM-DD HH:MM
    3. YYYY-MM-DD HH:MM:SS
    4. YYYY-MM-DD HH:MM:SS.SSS
    5. YYYY-MM-DDTHH:MM
    6. YYYY-MM-DDTHH:MM:SS
    7. YYYY-MM-DDTHH:MM:SS.SSS
    8. HH:MM
    9. HH:MM:SS
    10. HH:MM:SS.SSS
    11. now
    12. DDDD.DDDD

    In formats 5 through 7, the "T" is a literal character separating the date and the time, as required by the ISO-8601 standard. These formats are supported in SQLite 3.2.0 and later. Formats 8 through 10 that specify only a time assume a date of 2000-01-01. Format 11, the string 'now', is converted into the current date and time. Universal Coordinated Time (UTC) is used. Format 12 is the julian day number expressed as a floating point value.

    Modifiers

    The time string can be followed by zero or more modifiers that alter the date or alter the interpretation of the date. The available modifiers are as follows.

    1. NNN days
    2. NNN hours
    3. NNN minutes
    4. NNN.NNNN seconds
    5. NNN months (see #551 and [1163])
    6. NNN years (see #551 and [1163])
    7. start of month
    8. start of year
    9. start of week (withdrawn -- will not be implemented)
    10. start of day
    11. weekday N (see #551 and [1163])
    12. unixepoch
    13. localtime
    14. utc

    The first six modifiers (1 through 6) simply add the specified amount of time to the date specified by the preceding timestring.

    The "start of" modifiers (7 through 10) shift the date backwards to the beginning of the current month, year or day.

    The "weekday" modifier advances the date forward to the next date where the weekday number is N. Sunday is 0, Monday is 1, and so forth.

    The "unixepoch" modifier (12) only works if it immediately follows a timestring in the DDDDDDDDDD format. This modifier causes the DDDDDDDDDD to be interpreted not as a julian day number as it normally would be, but as the number of seconds since 1970. This modifier allows unix-based times to be converted to julian day numbers easily.

    The "localtime" modifier (13) adjusts the previous time string so that it displays the correct local time. "utc" undoes this.

    Examples

    Compute the current date.

      SELECT date('now');
    

    Compute the last day of the current month.

      SELECT date('now','start of month','+1 month','-1 day');
    

    Compute the date and time given a unix timestamp 1092941466.

      SELECT datetime(1092941466, 'unixepoch');
    

    Compute the date and time given a unix timestamp 1092941466, and compensate for your local timezone.

      SELECT datetime(1092941466, 'unixepoch', 'localtime');
    

    Compute the current unix timestamp.

      SELECT strftime('%s','now');
    

    Compute the number of days since the battle of Hastings.

      SELECT julianday('now') - julianday('1066-10-14','gregorian');
    

    Compute the number of seconds between two dates:

      SELECT julianday('now')*86400 - julianday('2004-01-01 02:34:56')*86400;
    

    Compute the date of the first Tuesday in October (January + 9) for the current year.

      SELECT date('now','start of year','+9 months','weekday 2');
    

    Caveats And Bugs

    The computation of local time depends heavily on the whim of local politicians and is thus difficult to get correct for all locales. In this implementation, the standard C library function localtime() is used to assist in the calculation of local time. Note that localtime() is not threadsafe, so use of the "localtime" modifier is not threadsafe. Also, the localtime() C function normally only works for years between 1970 and 2037. For dates outside this range, SQLite attempts to map the year into an equivalent year within this range, do the calculation, then map the year back.

    Please surround uses of localtime() with sqliteOsEnterMutex() and sqliteOsLeaveMutex() so threads using SQLite are protected, at least! -- e It is so. --drh

    [Consider instead, using localtime_r which is reentrant and may be used *without* expensive mutex locking. Although non-standard it's available on most Unixes --hauk] But it is not available on windows, as far as I am aware. --drh On windows localtime() is thread-safe if the MT C runtime is used. The MT runtime uses thread-local storage for the static variables, the kind functions use.--gr [What about using localtime_r, and on systems where it is unavailable defining it as sqliteOsEnterMutext() ; locatime() ; sqliteOsLeaveMutex() so that non-windows systems get the maximum advantage, with almost zero code impact?] The autoconfigury and patch for localtime_r is here: ¤http://www.sqlite.org/cvstrac/tktview?tn=1906 . I'm curious why this obvious fix is not applied. gmtime() also suffers from this same threadsafety problem.

    Date computations do not give correct results for dates before Julian day number 0 (-4713-11-24 12:00:00).

    All internal computations assume the Gregorian calendar system.


    An anonymous user adds:
    For my use I added new functions and functionalities to the date functions that come with the sqlite 3.3.0 (can be used in older versions as well with small effort).

    In main lines they are as follows:

    1. NNN days
    2. NNN hours
    3. NNN minutes
    4. NNN.NNNN seconds
    5. NNN months (see #551 and [1163])
    6. NNN years (see #551 and [1163])
    7. start of month
    8. start of year
    9. start of week (!!! implemented)
    10. start of day
    11. weekday N (see #551 and [1163])
    12. unixepoch
    13. localtime
    14. utc
    15. julian (not implemented as of 2004-01-05)
    16. gregorian (not implemented as of 2004-01-05)
    17. start of minute
    18. start of hour
    19. end of minute
    20. end of hour
    21. end of day
    22. end of week
    23. end of month
    24. end of year
    25. group seconds by
    26. group minutes by
    27. group hours by
    28. group days by
    29. group weeks by
    30. group months by
    31. group years by

    The "start of" modifiers (7 through 10 and 17 through 18) shift the date backwards to the beginning of the current minute, hour, week, month, year or day.

    The "end of" modifiers (19 through 24) shift the date forwards to the end of the current minute, hour, week, month, year or day.

    The "group * by" modifiers (25 through 31) round the date to the closest backward multiple supplied, with some limitations, to the current seconds (1 through 30), minutes (1 through 30), hours (1 through 12), days (1 through 15), weeks (1 through 26), months (1 through 6), years (1 through 100), these limitations are due to dont complicate the calculations when a multiple can span beyound the unit modified.

    Ex:

    SELECT datetime('2006-02-04 20:09:23','group hours by 3'); => '2006-02-04 18:00:00'

    SELECT datetime('2006-02-05 20:09:23','group days by 3'); => '2006-02-04 00:00:00'

    New functions "week_number(date)" returns the week number of the year on the supplied date parameter, "datetime2seconds(datetime)" return the number of seconds from the supplied datetime parameter.

    The diff file ready to be applied to the file "date.c" in the src directory of sqlite 3.3.0 is at ¤http://dad-it.com:8080/date.c.diff, I hope it's considered valuable to be merged in the official distribution.

    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/113

    实用的Linux命令技巧(字串处理)

    实用的Linux命令技巧(字串处理)

    jimmy | 22 十二月, 2006 10:45

    grep、tail这些就不说了,自己看Help。

    awk 参数 '命令' 文件名

    -F, ——用,做分隔符。缺省是空格

    '命令' ——一般正则、输出使用较多

    例子:

    awk '/"GET .*.gif"/ {print $7}' logs/access_log

    从logs/access_log中,找出匹配 *.gif 的记录

    sar -u 1 3 | grep Average | awk '{print $7}'

    查看CPU空闲率


    wc 参数 文件

    -l 计行

    -c 计字节

    例子:

    wc -l logs/access_log

    统计日志总行数

    grep "logo.gif" logs/access_log | wc -l

    统计日志中logo.gif被访问的次数

    bc 参数 表达式文件

    计算器。可以进行复杂数学计算

    echo 100+100 | bc

    echo 0.12343556/21321445 | bc -l

    sed 参数 文件

    sed是一个单行的文本编辑器,功能非常强。这里只介绍几个常用的技巧。

    例子:

    cat /proc/cpuinfo | grep 'model name' | sed -e 's/^(.*):(.*)$/2/'

    查看CPU信息

    uptime | sed -e 's/^.*average.*: (.*)$/1/' -e 's/ //g'

    查看负载

    uptime | sed -e 's/^.*average.*: (.*)$/1/' -e 's/ //g' | awk -F, '{ printf("%.2fn",$2); printf("%.2fn",$3) }'

    增强版

    ~~呵呵~~

    转贴一个sed的教程:

    使用 sed 编辑器
    作者:Emmett Dulaney

    sed 编辑器是 Linux 系统管理员的工具包中最有用的资产之一,
    因此,有必要彻底地了解其应用

    Linux 操作系统最大的一个好处是它带有各种各样的实用工具。存在如此之多不同的实用工具,几乎不可能知道并了解所有这些工具。可以简化关键情况下操作的一个实用工具是 sed。它是任何管理员的工具包中最强大的工具之一,并且可以证明它自己在关键情况下非常有价值。

    sed 实用工具是一个“编辑器”,但它与其它大多数编辑器不同。除了不面向屏幕之外,它还是非交互式的。这意味着您必须将要对数据执行的命令插入到命令行或要处理的脚本中。当显示它时,请忘记您在使用 Microsoft Word 或其它大多数编辑器时拥有的交互式编辑文件功能。sed 在一个文件(或文件集)中非交互式、并且不加询问地接收一系列的命令并执行它们。因而,它流经文本就如同水流经溪流一样,因而 sed 恰当地代表了流编辑器。它可以用来将所有出现的 "Mr. Smyth" 修改为 "Mr. Smith",或将 "tiger cub" 修改为 "wolf cub"。流编辑器非常适合于执行重复的编辑,这种重复编辑如果由人工完成将花费大量的时间。其参数可能和一次性使用一个简单的操作所需的参数一样有限,或者和一个具有成千上万行要进行编辑修改的脚本文件一样复杂。sed 是 Linux 和 UNIX 工具箱中最有用的工具之一,且使用的参数非常少。

    sed 的工作方式

    sed 实用工具按顺序逐行将文件读入到内存中。然后,它执行为该行指定的所有操作,并在完成请求的修改之后将该行放回到内存中,以将其转储至终端。完成了这一行上的所有操作之后,它读取文件的下一行,然后重复该过程直到它完成该文件。如同前面所提到的,默认输出是将每一行的内容输出到屏幕上。在这里,开始涉及到两个重要的因素—首先,输出可以被重定向到另一文件中,以保存变化;第二,源文件(默认地)保持不被修改。sed 默认读取整个文件并对其中的每一行进行修改。不过,可以按需要将操作限制在指定的行上。

    该实用工具的语法为:

    sed [options] '{command}' [filename]
    

    在这篇文章中,我们将浏览最常用的命令和选项,并演示它们如何工作,以及它们适于在何处使用。

    替换命令

    sed 实用工具以及其它任何类似的编辑器的最常用的命令之一是用一个值替换另一个值。用来实现这一目的的操作的命令部分语法是:

    's/{old value}/{new value}/'
    

    因而,下面演示了如何非常简单地将 "tiger" 修改为 "wolf":

    $ echo The tiger cubs will meet on Tuesday after school | sed 
    's/tiger/wolf/'
    The wolf cubs will meet on Tuesday after school
    $
    

    注意如果输入是源自之前的命令输出,则不需要指定文件名—同样的原则也适用于 awk、sort 和其它大多数 LinuxUNIX 命令行实用工具程序。

    多次修改

    如果需要对同一文件或行作多次修改,可以有三种方法来实现它。第一种是使用 "-e" 选项,它通知程序使用了多条编辑命令。例如:

    $ echo The tiger cubs will meet on Tuesday after school | sed -e '
    s/tiger/wolf/' -e 's/after/before/'
    The wolf cubs will meet on Tuesday before school
    $
    

    这是实现它的非常复杂的方法,因此 "-e" 选项不常被大范围使用。更好的方法是用分号来分隔命令:

    $ echo The tiger cubs will meet on Tuesday after school | sed '
    s/tiger/wolf/; s/after/before/'
    The wolf cubs will meet on Tuesday before school 
    $
    

    注意分号必须是紧跟斜线之后的下一个字符。如果两者之间有一个空格,操作将不能成功完成,并返回一条错误消息。这两种方法都很好,但许多管理员更喜欢另一种方法。要注意的一个关键问题是,两个撇号 (' ') 之间的全部内容都被解释为 sed 命令。直到您输入了第二个撇号,读入这些命令的 shell 程序才会认为您完成了输入。这意味着可以在多行上输入命令—同时 Linux 将提示符从 PS1 变为一个延续提示符(通常为 ">")—直到输入了第二个撇号。一旦输入了第二个撇号,并且按下了 Enter 键,则处理就进行并产生相同的结果,如下所示:

    $ echo The tiger cubs will meet on Tuesday after school | sed '
    > s/tiger/wolf/
    > s/after/before/'
    The wolf cubs will meet on Tuesday before school
    $
    

    全局修改

    让我们开始一次看似简单的编辑。假定在要修改的消息中出现了多次要修改的项目。默认方式下,结果可能和预期的有所不同,如下所示:

    $ echo The tiger cubs will meet this Tuesday at the same time
    as the meeting last Tuesday | sed 's/Tuesday/Thursday/'
    The tiger cubs will meet this Thursday at the same time
    as the meeting last Tuesday 
    $
    

    与将出现的每个 "Tuesday" 修改为 "Thursday" 相反,sed 编辑器在找到一个要修改的项目并作了修改之后继续处理下一行,而不读整行。sed 命令功能大体上类似于替换命令,这意味着它们都处理每一行中出现的第一个选定序列。为了替换出现的每一个项目,在同一行中出现多个要替换的项目的情况下,您必须指定在全局进行该操作:

    $ echo The tiger cubs will meet this Tuesday at the same time
    as the meeting last Tuesday | sed 's/Tuesday/Thursday/g'
    The tiger cubs will meet this Thursday at the same time
    as the meeting last Thursday
    $
    

    请记住不管您要查找的序列是否仅包含一个字符或词组,这种对全局化的要求都是必需的。

    sed 还可以用来修改记录字段分隔符。例如,以下命令将把所有的 tab 修改为空格:

    sed 's/	/ /g' 
    

    其中,第一组斜线之间的项目是一个 tab,而第二组斜线之间的项目是一个空格。作为一条通用的规则,sed 可以用来将任意的可打印字符修改为任意其它的可打印字符。如果您想将不可打印字符修改为可打印字符—例如,铃铛修改为单词 "bell"—sed 不是适于完成这项工作的工具(但 tr 是)。

    有时,您不想修改在一个文件中出现的所有指定项目。有时,您只想在满足某些条件时才作修改—例如,在与其它一些数据匹配之后才作修改。为了说明这一点,请考虑以下文本文件:

    $ cat sample_one
    one     1
    two     1
    three   1
    one     1
    two     1
    two     1
    three   1
    $
    

    假定希望用 "2" 来替换 "1",但仅在单词 "two" 之后才作替换,而不是每一行的所有位置。通过指定在给出替换命令之前必须存在一次匹配,可以实现这一点:

    $ sed '/two/ s/1/2/' sample_one
    one     1
    two     2
    three   1
    one     1
    two     2
    two     2
    three   1
    $
    

    现在,使其更加准确:

    $ sed '
    > /two/ s/1/2/
    > /three/ s/1/3/' sample_one
    one     1
    two     2
    three   3
    one     1
    two     2
    two     2
    three   3
    $
    

    请再次记住唯一改变了的是显示。如果您查看源文件,您将发现它始终保持不变。您必须将输出保存至另一个文件,以实现永久保存。值得重复的是,不对源文件作修改实际是祸中有福—它让您能够对文件进行试验而不会造成任何实际的损害,直到让正确命令以您预期和希望的方式进行工作。

    以下命令将修改后的输出保存至一个新的文件:

    $ sed '
    > /two/ s/1/2/
    > /three/ s/1/3/' sample_one > sample_two
    

    该输出文件将所有修改合并在其中,并且这些修改通常将在屏幕上显示。现在可以用 head、cat 或任意其它类似的实用工具来进行查看。

    脚本文件

    sed 工具允许您创建一个脚本文件,其中包含从该文件而不是在命令行进行处理的命令,并且 sed 工具通过 "-f" 选项来引用。通过创建一个脚本文件,您能够一次又一次地重复运行相同的操作,并指定比每次希望从命令行进行处理的操作详细得多的操作。

    考虑以下脚本文件:

    $ cat sedlist
    /two/ s/1/2/
    /three/ s/1/3/
    $
    

    现在可以在数据文件上使用脚本文件,获得和我们之前看到的相同的结果:

    $ sed -f sedlist sample_one
    one     1
    two     2
    three   3
    one     1
    two     2
    two     2
    three   3
    $
    

    注意当调用 "-f" 选项时,在源文件内或命令行中不使用撇号。脚本文件,也称为源文件,对于想重复多次的操作和从命令行运行可能出错的复杂命令很有价值。编辑源文件并修改一个字符比在命令行中重新输入一条多行的项目要容易得多。

    限制行

    编辑器默认查看输入到流编辑器中的每一行,且默认在输入到流编辑器中的每一行上进行编辑。这可以通过在发出命令之前指定约束条件来进行修改。例如,只在此示例文件的输出的第 5 和第 6 行中用 "2" 来替换 "1",命令将为:

    $ sed '5,6 s/1/2/' sample_one
    one     1
    two     1
    three   1
    one     1
    two     2
    two     2
    three   1
    $
    

    在这种情况下,因为要修改的行是专门指定的,所以不需要替换命令。因此,您可以灵活地根据匹配准则(可以是行号或一种匹配模式)来选择要修改哪些行(从根本上限制修改)。

    禁止显示

    sed 默认将来自源文件的每一行显示到屏幕上(或重定向到一个文件中),而无论该行是否受到编辑操作的影响,"-n" 参数覆盖了这一操作。"-n" 覆盖了所有的显示,并且不显示任何一行,而无论它们是否被编辑操作修改。例如:

    $ sed -n -f sedlist sample_one
    $
    
    $ sed -n -f sedlist sample_one > sample_two
    $ cat sample_two
    $
    

    在第一个示例中,屏幕上不显示任何东西。在第二个示例中,不修改任何东西,因此不将任何东西写到新的文件中—它最后是空的。这不是否定了编辑的全部目的吗?为什么这是有用的?它是有用的仅因为 "-n" 选项能够被一条显示命令 (-p) 覆盖。为了说明这一点,假定现在像下面这样对脚本文件进行了修改:

    $ cat sedlist
    /two/ s/1/2/p
    /three/ s/1/3/p
    $
    

    然后下面是运行它的结果:

    $ sed -n -f sedlist sample_one
    two     2
    three   3
    two     2
    two     2
    three   3
    $
    

    保持不变的行全部不被显示。只有受到编辑操作影响的行被显示了。在这种方式下,可以仅取出这些行,进行修改,然后把它们放到一个单独的文件中:

    $ sed -n -f sedlist sample_one > sample_two
    $
    
    $ cat sample_two
    two     2
    three   3
    two     2
    two     2
    three   3
    $
    

    利用它的另一种方法是只显示一定数量的行。例如,只显示 2-6 行,同时不做其它的编辑修改:

    $ sed -n '2,6p' sample_one
    two     1
    three   1
    one     1
    two     1
    two     1
    $
    

    其它所有的行被忽略,只有 2-6 行作为输出显示。这是一项出色的功能,其它任何工具都不能容易地实现。Head 将显示一个文件的顶部,而 tail 将显示一个文件的底部,但 sed 允许从任意位置取出想要的任意内容。

    删除行

    用一个值替换另一个值远非流编辑器可以执行的唯一功能。它还具有许多的潜在功能,在我看来第二种最常用的功能是删除。删除与替换的工作方式相同,只是它删除指定的行(如果您想要删除一个单词而不是一行,不要考虑删除,而应考虑用空的内容来替换它—s/cat//)。

    该命令的语法是:

    '{what to find} d'
    

    从 sample_one 文件中删除包含 "two" 的所有行:

    $ sed '/two/ d' sample_one
    one     1
    three   1
    one     1
    three   1
    $
    

    从显示屏中删除前三行,而不管它们的内容是什么:

    $ sed '1,3 d' sample_one
    one     1
    two     1
    two     1
    three   1
    $
    

    只显示剩下的行,前三行不在显示屏中出现。对于流编辑器,一般当它们涉及到全局表达式时,特别是应用于删除操作时,有几点要记住:

    1. 上三角号 (^) 表示一行的开始,因此,如果 "two" 是该行的头三个字符,则

      sed '/^two/ d' sample_one
      

      将只删除该行。

    2. 美元符号 ($) 代表文件的结尾,或一行的结尾,因此,如果 "two" 是该行的最后三个字符,则

      sed '/two$/ d' sample_one 
      

      将只删除该行。

    将这两者结合在一起的结果:

    sed '/^$/ d' {filename}
    

    删除文件中的所有空白行。例如,以下命令将 "1" 替换为 "2",以及将 "1" 替换为 "3",并删除文件中所有尾随的空行:

    $ sed '/two/ s/1/2/; /three/ s/1/3/; /^$/ d' sample_one
    one     1
    two     1
    three   1
    one     1
    two     2
    two     2
    three   1
    $
    

    其通常的用途是删除一个标题。以下命令将删除文件中所有的行,从第一行直到第一个空行:

    sed '1,/^$/ d' {filename}
    

    添加和插入文本

    可以结合使用 sed 和 "a" 选项将文本添加到一个文件的末尾。实现方法如下:

    $ sed '$a
    > This is where we stop
    > the test' sample_one
    one     1
    two     1
    three   1
    one     1
    two     1
    two     1
    three   1
    This is where we stop
    the test
    $
    

    在该命令中,美元符号 ($) 表示文本将被添加到文件的末尾。反斜线 () 是必需的,它表示将插入一个回车符。如果它们被遗漏了,则将导致一个错误,显示该命令是错乱的;在任何要输入回车的地方您必须使用反斜线。

    要将这些行添加到第 4 和第 5 个位置而不是末尾,则命令变为:

    $ sed '3a
    > This is where we stop
    > the test' sample_one
    one     1
    two     1
    three   1
    This is where we stop
    the test
    one     1
    two     1
    two     1
    three   1
    $
    

    这将文本添加到第 3 行之后。和几乎所有的编辑器一样,您可以选择插入而不是添加(如果您希望这样的话)。这两者的区别是添加跟在指定的行之后,而插入从指定的行开始。当用插入来代替添加时,只需用 "i" 来代替 "a",如下所示:

    $ sed '3i
    > This is where we stop
    > the test' sample_one
    one     1
    two     1
    This is where we stop
    the test
    three   1
    one     1
    two     1
    two     1
    three   1
    $
    

    新的文本出现在输出的中间位置,而处理通常在指定的操作执行以后继续进行。

    读写文件

    重定向输出的功能已经演示过了,但需要指出的是,在编辑命令运行期间可以同步地读入和写出文件。例如,执行替换,并将 1-3 行写到名称为 sample_three 的文件中:

    $ sed '
    > /two/ s/1/2/
    > /three/ s/1/3/
    > 1,3 w sample_three' sample_one
    one     1
    two     2
    three   3
    one     1
    two     2
    two     2
    three   3
    $
    
    $ cat sample_three
    one     1
    two     2
    three   3
    $
    

    由于为 w (write) 命令指定了 "1,3",所以只有指定的行被写到了新文件中。无论被写的是哪些行,所有的行都在默认输出中显示。

    修改命令

    除了替换项目之外,还可以将行从一个值修改为另一个值。要记住的是,替换是对字符逐个进行,而修改功能与删除类似,它影响整行:

    $ sed '/two/ c
    > We are no longer using two' sample_one
    one     1
    We are no longer using two
    three   1
    one     1
    We are no longer using two
    We are no longer using two
    three   1
    $
    

    修改命令与替换的工作方式很相似,但在范围上要更大些—将一个项目完全替换为另一个项目,而无论字符内容或上下文。夸张一点讲,当使用替换时,只有字符 "1" 被字符 "2" 替换,而当使用修改时,原来的整行将被修改。在两种情况下,要寻找的匹配条件都仅为 "two"。

    修改全部但……

    对于大多数 sed 命令,详细说明各种功能要进行何种修改。利用感叹号,可以在除指定位置之外的任何地方执行修改—与默认的操作完全相反。

    例如,要删除包含单词 "two" 的所有行,操作为:

    $ sed '/two/ d' sample_one
    one     1
    three   1
    one     1
    three   1
    $
    

    而要删除除包含单词 "two" 的行之外的所有行,则语法变为:

    $ sed '/two/ !d' sample_one
    two     1
    two     1
    two     1
    $
    

    如果您有一个文件包含一系列项目,并且想对文件中的每个项目执行一个操作,那么首先对那些项目进行一次智能扫描并考虑将要做什么是很重要的。为了使事情变得更简单,您可以将 sed 与任意迭代例程(for、while、until)结合来实现这一目的。

    比如说,假定您有一个名为 "animals" 的文件,其中包含以下项目:

    pig
    horse
    elephant
    cow
    dog
    cat

    您希望运行以下例程:

    #mcd.ksh
    for I in $*
    do
    echo Old McDonald had a $I
    echo E-I, E-I-O
    done
    

    结果将为,每一行都显示在 "Old McDonald has a" 的末尾。虽然对于这些项目的大部分这是正确的,但对于 "elephant" 项目,它有语法错误,因为结果应当为 "an elephant" 而不是 "a elephant"。利用 sed,您可以在来自 shell 文件的输出中检查这种语法错误,并通过首先创建一个命令文件来即时地更正它们:

    #sublist
    / a a/ s/ a / an /
    / a e/ s/ a / an /
    /a i/ s / a / an /
    /a o/ s/ a / an /
    /a u/ s/ a / an /
    

    然后执行以下过程:

    $ sh mcd.ksh 'cat animals' | sed -f sublist  
    

    现在,在运行了 mcd 脚本之后,sed 将在输出中搜索单个字母 a (空格,"a",空格)之后紧跟了一个元音的任意位置。如果这种位置存在,它将把该序列修改为空格,"an",空格。这样就使问题更正后才显示在屏幕上,并确保各处的编辑人员在晚上可以更容易地入睡。结果是:

    Old McDonald had a pig
    E-I, E-I-O
    Old McDonald had a horse
    E-I, E-I-O
    Old McDonald had an elephant
    E-I, E-I-O
    Old McDonald had a cow
    E-I, E-I-O
    Old McDonald had a dog
    E-I, E-I-O
    Old McDonald had a cat
    E-I, E-I-O

    提前退出

    sed 默认读取整个文件,并只在到达末尾时才停止。不过,您可以使用退出命令提前停止处理。只能指定一条退出命令,而处理将一直持续直到满足调用退出命令的条件。

    例如,仅在文件的前五行上执行替换,然后退出:

    $ sed '
    > /two/ s/1/2/
    > /three/ s/1/3/
    > 5q' sample_one
    one     1
    two     2
    three   3
    one     1
    two     2
    $
    

    在退出命令之前的项目可以是一个行号(如上所示),或者一条查找/匹配命令:

    $ sed '
    > /two/ s/1/2/
    > /three/ s/1/3/
    > /three/q' sample_one
    one     1
    two     2
    three   3
    $
    

    您还可以使用退出命令来查看超过一定标准数目的行,并增加比 head 中的功能更强的功能。例如,head 命令允许您指定您想要查看一个文件的前多少行—默认数为 10,但可以使用从 1 到 99 的任意一个数字。如果您想查看一个文件的前 110 行,您用 head 不能实现这一目的,但用 sed 可以:

    sed 110q filename
    

    处理问题

    当使用 sed 时,要记住的重要事项是它的工作方式。它的工作方式是:读入一行,在该行上执行它已知要执行的所有任务,然后继续处理下一行。每一行都受给定的每一个编辑命令的影响。

    如果您的操作顺序没有十分彻底地考虑清楚,那么这可能会很麻烦。例如,假定您需要将所有的 "two" 项目修改为 "three",然后将所有的 "three" 修改为 "four":

    $ sed '
    > /two/ s/two/three/
    > /three/ s/three/four/' sample_one
    one     1
    four     1
    four   1
    one     1
    four     1
    four     1
    four   1
    $
    

    最初读取的 "two" 被修改为 "three"。然后它满足为下一次编辑建立的准则,从而变为 "four"。最终的结果不是想要的结果—现在除了 "four" 没有别的项目了,而本来应该有 "three" 和 "four"。

    当执行这种操作时,您必须非常用心地注意指定操作的方式,并按某种顺序来安排它们,使得操作之间不会互相影响。例如:

    $ sed '
    > /three/ s/three/four/
    > /two/ s/two/three/' sample_one
    one     1
    three     1
    four   1
    one     1
    three     1
    three     1
    four   1
    $
    

    这非常有效,因为 "three" 值在 "two" 变成 "three" 之前得到修改。

    标签和注释

    可以在 sed 脚本文件中放置标签,这样一旦文件变得庞大,可以更容易地说明正在发生的事情。存在各种各样与这些标签相关的命令,它们包括:

    接下来的步骤

    访问并收藏 Linux 技术中心

    阅读 Dale Dougherty 和 Arnold Robbins 的著作 sed & awk, 2nd Edition (O'Reilly & Associates 出版社)。

    1. : 冒号表示一个标签名称。例如:

      :HERE
      

      以冒号开始的标签可以由 "b" 和 "t" 命令处理。

    2. b {label} 充当 "goto" 语句的作用,将处理发送至前面有一个冒号的标签。例如,

      b HERE
      

      将处理发送给行

      :HERE
      

      如果紧跟 b 之后没有指定任何标签,则处理转至脚本文件的末尾。

    3. t {label} 只要自上次输入行或执行一次 "t" 命令以来进行了替换操作,就转至该标签。和 "b" 一样,如果没有给定标签名,则处理转至脚本文件的末尾。

    4. # 符号作为一行的第一个字符将使整行被当作注释处理。注释行与标签不同,不能使用 b 或 t 命令来转到注释行上。


    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/112

    DNS服务器地址(ZT)

    DNS服务器地址(ZT)

    jimmy | 18 十二月, 2006 09:45

    全国主要省份城市的DNS服务器地址

    省份/城市 DNS 名称 DNS IP ADDRESS
    ==========================================================

    常用:

    网通:202.106.0.20

    电信:202.96.199.133

    香港 ns1.netvigator.com 205.252.144.228

    澳门 vassun2.macau.ctm.net 202.175.3.8

    深圳 ns.shenzhen.gd.cn 202.96.134.133
    202.96.154.8
    202.96.154.15

    北京 ns.bta.net.cn 202.96.0.133
    ns.spt.net.cn 202.96.199.133
    ns.cn.net 202.97.16.195
    202.106.0.20
    202.106.148.1
    202.106.196.115

    广东 ns.guangzhou.gd.cn 202.96.128.143
    dns.guangzhou.gd.cn 202.96.128.68

    上海 ns.sta.net.cn 202.96.199.132
    202.96.199.133
    202.96.209.5
    202.96.209.133

    浙江 dns.zj.cninfo.net 202.96.102.3
    202.96.96.68
    202.96.104.18

    陕西 ns.snnic.com 202.100.13.11

    西安: 202.100.4.15
    202.100.0.68

    天津 ns.tpt.net.cn 202.99.96.68

    辽宁 ns.dcb.ln.cn 202.96.75.68
    202.96.75.64
    202.96.64.68
    202.96.69.38
    202.96.86.18
    202.96.86.24

    江苏 pub.jsinfo.net 202.102.29.3
    202.102.13.141
    202.102.24.35

    安徽:
    202.102.192.68
    202.102.199.68

    四川 ns.sc.cninfo.net 61.139.2.69

    重庆 61.128.128.68
    61.128.192.4

    成都: 202.98.96.68
    202.98.96.69

    河北 ns.hesjptt.net.cn 202.99.160.68

    保定: 202.99.160.68
    202.99.166.4

    山西 ns.sxyzptt.net.cn 202.99.198.6

    吉林 ns.jlccptt.net.cn 202.98.5.68

    山东 202.102.152.3
    202.102.128.68

    福建 dns.fz.fj.cn 202.101.98.55

    湖南 202.103.100.206

    广西 10.138.128.40
    202.103.224.68
    202.103.225.68

    江西 202.109.129.2
    202.101.224.68---j江西电信DNS
    202.101.226.68---江西电信DNS
    202.101.240.36 ---江西电信DNS

    云南 ns.ynkmptt.net.cn 202.98.160.68

    河南: 202.102.227.68
    202.102.224.68
    202.102.245.12

    新疆: 61.128.97.73

    乌鲁木齐 61.128.97.73
    61.128.97.74

    武汉: 202.103.24.68
    202.103.0.117

    厦门两个
    202.101.103.55
    202.101.103.54

    山东的: 202.102.134.68

    长沙
    202.103.96.68
    202.103.96.112

    一些教育网内的----不一定好用
    202.203.128.33 cernet云南中心主dns
    202.203.128.34

    210.14.232.241 and 203.93.19.133 罗湖
    202.112.10.37 长安
    202.115.64.33 and 202.115.64.34 西南交大
    202.201.48.1 and 202.201.48.2 nwnu
    210.33.116.112 浙江电大
    202.116.160.33 华南农业
    202.114.240.6 wust
    202.194.48.130 ytnc
    202.114.0.242 and 202.112.20.131 华中科大
    202.202.128.33 and 202.202.128.34 重庆医科大?西安交大?
    202.112.0.33 and 202.112.0.34 cernet 华北网
    210.38.192.33 韶关

    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/111

    项目管理的常用词汇(转)

    项目管理的常用词汇(转)

    jimmy | 12 十二月, 2006 18:02

    假设(Assumption)

    项目成功可能需要某种外部环境或者某些事件发生才行(或者应该发生,以增加您成功的几率)。如果您相信事件发生的几率是可以接受的,那么您就应该把它列为假设。假设所具有的几率处于0到100%之间。换句话说,事件会发生(0%)和肯定不会发生(100%)都是有可能的。它处于中间某个位置。假设是十分重要的,因为它为定义项目整个剩余部分确定了一个背景。如果假设没有得到满足,那么项目定义的预估和其他内容都不再有效。

    客户/用户(Client / Customs)

    项目或者服务的直接受益人或者团体被称作客户/用户。他们是项目进行的最终目标群(间接的受益人被称作利益相关人)。在很多机构里,内部受益人被称为“客户”,而外部受益人被称为“用户”,但是这并不是一个硬性规定。

    约束(Constraints)

    约束是不在项目小组掌控之内,但需要被管理的限制条件。它们并不都会带来问题。但是,项目主管应该知晓这些约束,因为它们表示项目必须在这些限制条件之内执行。例如,日期限制意味着特定的事件(也许是项目的结束之日)必须在特定的日期之前完成。资源几乎总是会成为一个限制,因为它们总是被限量提供的。

    重要路线(Critical path)

    重要路线是整个项目要按计划完成各项活动的顺序。它是整个工作计划中持续时间最长的路线。如果重要路线中的某项活动推迟了一天完成,那么这个项目的结束就会被推迟一天(除非重要路线中的另外一项活动能够提前一天完成)。

    可交付内容(Deliverable)

    可交付内容是项目所产生的任何有形的产出。所有的项目都会产生可交付内容。这可以是文档、计划书、计算机系统、建筑物、飞机等等。内部可交付内容是执行项目的结果所产生的,而且常常只有项目小组才需要。外部可交付内容是那些为客户和利益相关人所产出的东西。您的项目可以产生一个或者多个可交付内容。

    功能主管(Functional manager)

    功能主管是您功能机构里接受您报告的人。通常,这个人会对您的表现进行评估。项目主管也可以担任功能主管一职,但是他或者她未必就一定是功能主管。如果您的项目主管与功能主管不是同一个人,您的机构可能采取的是矩阵式管理(模式)。

    根特图表(Gantt chart)

    根特图表是一个用来说明不同时间段里各种活动的柱形图。它的开始和结尾就表示活动的开始和结束日期。

    难题(Issue)

    难题是会阻止项目顺利进行的重要问题,没有外部帮助,它是无法被项目主管和项目小组顺利解决的。项目主管应该积极主动地通过预先设定好的难题管理流程来处理这些难题。

    生命周期(Lifecycle)

    生命周期指的是用来构建项目所要产生的可交付内容的过程。用于项目生命周期的模式有很多种。对于软件开发来说,整个生命周期可能包括规划、分析、设计、构建/测试、实施,以及支持等阶段。这是“瀑布型”生命周期的一个例子。其他的生命周期模式包括反复开发、套装实施,以及研究和开发。这些生命周期模式中的每一种都代表创建项目可交付内容的一种方法。

    里程碑(Milestone)

    里程碑是代表一个主要可交付内容或者一系列相关的可交付内容被完成的计划事件。从定义上讲,一个里程碑的持续时间为零,所投入的精力也为零。也就是说,与里程碑相关的工作为零。它是工作计划中的一面旗帜,代表着某些其他的工作已经被完成。通常,里程碑被用作项目的检查点,来检验项目进展的情况。在很多情况下,在里程碑这个地方需要作出某个决定,例如检验项项目是否已经为进展到下一步做好了准备?

    目标(Objective)

    目标是用来描述项目将要实现的具体内容。目标应该在较低的层次编写,这样才能够在项目结束的时候检查它是否被实现。项目的成功由项目的目标是否被实现来决定。制定目标的一种技术是使其具体、可测量、可实现、实际和有时限(SMART)。

    程序(Program)

    程序是一个用来管理一系列相关项目的伞形结构。程序并不产生任何项目可交付内容。项目小组才会生产所有的项目可交付内容。程序的目的是提供总体上的指导和指南,确保相关的项目能够有效地沟通,提供合同的中心点和客户以及项目小组的工作重心,确定各个项目应该如何被定义,以保证所有的工作都被成功地完成。

    程序主管(Program manager)

    程序主管是负责管理程序的人。(要注意的是,这是一个角色。程序主管也可以负责程序内的一个或者多个项目。)程序主管负责领导程序的整体策划和管理。一个程序内的所有项目主管都要向程序主管报告。

    项目(Project

    项目是用来组织和管理工作并最终创建具体定义的可交付内容或者一组可交付内容的临时结构。从定义上讲,所有的项目都是独一无二的,这是为什么很难将一个项目与另一个项目进行对比的一个原因。

    项目定义或章程(Project definition, charter)

    在开始实施项目之前,了解项目的整体目标、范围、可交付内容、风险、假设、项目组织章程等是十分重要的。项目的定义(或者章程)就是描述相关信息的文档。项目主管负责制定项目的定义。定义文档应该由资助人批准通过,以表示项目主管和资助人都同意这些重要的项目内容。

    项目主管(Project manager)

    项目主管是负责管理项目的人。项目主管要对用于管理项目的流程负完全责任。他或者她对小组成员还负有人员管理职责,虽然这一工作要与小组成员的功能主管一起共同完成。用来管理项目的流程包括定义工作、创建工作计划和预算、管理工作计划和预算、范围管理、难题管理、风险管理等等。

    项目阶段(Project phase)

    阶段是项目工作的主要逻辑分组。它还表示一个主要可交付内容或者一组相关的可交付内容的完成。在IT开发项目里,逻辑阶段可以包括策划、分析、设计、构建(其中包括测试),以及实现等阶段。

    项目小组(Project team)

    项目小组包括分配在用于生产项目可交付内容上的全职和兼职资源。他们负责理解要被完成的工作;按照给定的预算、时限和质量预期要求完成指定的工作;向项目主管报告难题、范围变化,以及风险和质量问题;积极主动地沟通状态和管理预期。

    要求(Requirements)

    要求是对一个产品或者服务应该如何动作、呈现和表现的描述。要求一般指的是您正在构建的项目的可交付内容的特性和功能。要求被认为是项目范围的一部分。高层的范围在您的项目定义(章程)里被定义。要求组成了具体的范围。在您的要求被批准通过之后,它们可以通过范围变化管理流程来更改。

    风险(Risk)

    有一些潜在外部事件可能会对您的项目产生负面影响。风险指的是事件发生的几率和事件发生对项目产生(负面)影响的几率的组合。如果事件发生的几率和对项目产生影响的几率太高,您就应该将这个可能的事件标记为风险,并提供一个积极主动的计划来管理这一风险。

    范围(Scope)

    范围是您用来描述项目边界的方式。它定义了项目将要交付什么、不交付什么。高层的范围在您的项目定义(章程)里被定义,包括所有的可交付内容和项目的边界。具体的范围通过您的业务要求来确定。对项目可交付内容、边界或者要求的更改都要求通过范围变化管理来取得同意。

    范围变化管理(Scope change management)

    范围变化管理是对先前经过批准的范围和要求的更改进行管理。范围在项目定义(章程)的范围章节里和更加详细的业务要求里被定义和批准。如果范围或者业务要求在项目实施期间发生了变化(这常常意味着客户要求增加内容),那么对成本、投入和持续时间的预估可能不再有效。如果资助人同意在项目范围里加入新的工作,那么项目主管就有权预计当前的预算和时限将会被修改(常常是增加),以反映额外增加的工作。而新预估的成本、投入和持续时间现在就成为了经过批准的目标。

    有的时候项目主管认为范围管理意味着必须向客户说“不”。这让项目主管很紧张和不安。但是,好消息是管理范围是让“资助人”作出对项目范围进行改变的决定。

    资助人/执行资助人/项目资助人(Sponsor,executive sponsor,project sponsor)

    资助人是对项目拥有最终决定权的人。执行资助人会提供项目的经费、帮助解决难题和项目变化、批准主要的可交付内容,以及提供高层的指导。他或者她还在机构内部对项目提供积极支持。根据项目的内容以及执行资助人在机构中的职位级别,他可以让项目资助人替代他来进行每天的日常管理工作。如果被任命,项目资助人会代表执行资助人进行日常工作,并对需要资助人批准的大多决定作决定。

    利益相关人(Shareholder)

    利益相关人指的是享受项目成果的具体人员或者团体。在通常情况下,利益相关人来自于公司内部,可以包括内部客户、管理层、员工、管理人员等等。项目也可以由外部利益相关人,包括供应商、投资人、社区团体,以及政府机构等。

    指导委员会(Steering committee)

    指导委员会常常由一群负责提供整体战略方向指导的高层利益相关人组成。他们不会取代资助人的位置,但是会帮助将战略投入和股权分配到机构的更大范围里。如果您的项目会对多个机构产生影响,那么这个指导委员会就会显得尤其重要,因为这会让来自这些组织的输入参与到可能影响到他们的决策中去。

    工作计划(Workplan,schedule)

    项目工作计划会告诉您将如何完成项目。它用来描述所需要进行的活动、工作的顺序、工作被分配给谁、对需要投入多少进行预估、工作何时完成,以及项目主管感兴趣的其它信息。工作计划使得项目主管能够辨别完成项目所需要进行的工作,并允许项目经理对工作进行监督,以确定项目是否在按计划进行。

    欢迎转贴,请注明来处。【本帖地址】: http://www.jimmydong.com/blog/post/1/110