cp's profile我的资料库PhotosBlogLists Tools Help

Blog


    May 25

    Mysql怎么样支持中文查询?

    Q:

    我在写一个查询条件时的问题如下: 
    如我想写一个字段中包含“李”字的所有记录 
    $str="李"; 
    select * from table where field like '%$str%' ; 
    显示的记录中除了包含”李”字的记录,还有不包含“李”字的记录。为什么?

    A:

    在MySQL中,进行中文排序和查找的时候,对汉字的排序和查找结果是错误的。这种情况在MySQL的很多版本中都存在。如果这个问题不解决,那么MySQL将无法实际处理中文。 

      出现这个问题的原因是:MySQL在查询字符串时是大小写不敏感的,在编绎MySQL时一般以ISO-8859字符集作为默认的字符集,因此在比较过程中中文编码字符大小写转换造成了这种现象。

    方法一:

    解决方法是对于包含中文的字段加上"binary"属性,使之作为二进制比较,例如将"name char(10)"改成"name char(10)binary"。 

    方法二: 

      如果你使用源码编译MySQL,可以编译MySQL时使用--with--charset=gbk 参数,这样MySQL就会直接支持中文查找和排序了。

    方法三: 

      可以使用 Mysql 的 locate 函数来判断。以上述问题为例,使用方法为:
    SELECT * FROM table WHERE locate(field,'李') > 0;
    本站使用的就是这种方法,感觉还不错。:P

    MYSQL服务维护笔记

      本文介绍作者使用MYSQL服务的一些经验,主要从以下几个方面讲解MYSQL服务规划设计:
    1 MYSQL服务的安装/配置的通用性;
    2 系统的升级和数据迁移方便性;
    3 备份和系统快速恢复;
     
    MYSQL服务器的规划

    为了以后维护,升级备份的方便和数据的安全性,最好将MYSQL程序文件和数据分别安装在“不同的硬件”上。

    /usr <== 操作系统 }==> 硬盘1 
    /home/mysql <== mysql应用程序
    ... 
    /data/app_1/ <== 应用数据和脚本 }==> 硬盘2
    /data/app_2/
    /data/app_3/

    mysql服务的安装和服务的启动:
    MYSQL一般使用当前STABLE的版本,尽量不使用--with-charset=选项,我感觉with-charset只在按字母排序的时候才有用,这些选项会对数据的迁移带来很多麻烦。
    configure --prefix=/home/mysql
    make 
    make install

    服务的启动和停止 
    1 复制缺省的mysql/var/mysql到 /data/app_1/目录下,
    2 MYSQLD的启动脚本:start_mysql.sh
    #!/bin/sh
    rundir=`dirname ""`
    echo "$rundir"
    /home/mysql/bin/safe_mysqld --user=mysql --pid-file="$rundir"/mysql.pid --datadir="$rundir"/var "$@"\
    -O max_connections=500 -O wait_timeout=600 -O key_buffer=32M --port=3402 --socket="$rundir"/mysql.sock & 

    注释:
    --pid-file="$rundir"/mysql.pid --socket="$rundir"/mysql.sock --datadir="$rundir"/var 
    目的都是将相应数据和应用临时文件放在一起;
    -O 后面一般是服务器启动全局变量优化参数,有时候需要根据具体应用调整;
    --port: 不同的应用使用PORT参数分布到不同的服务上去,一个服务可以提供的连接数一般是MYSQL服务的主要瓶颈; 

    修改不同的服务到不同的端口后,在rc.local文件中加入:
    /data/app_1/start_mysql.sh
    /data/app_2/start_mysql.sh
    /data/app_3/start_mysql.sh
    注意:必须写全路径

    3 MYSQLD的停止脚本:stop_mysql.sh
    #!/bin/sh
    rundir=`dirname ""`
    echo "$rundir"
    /home/mysql/bin/mysqladmin -u mysql -S"$rundir"/mysql.sock shutdown 

    使用这个脚本的好处在于:
    1 多个服务启动:只需要修改脚本中的--port=参数。单个目录下的数据和服务脚本都是可以独立打包的。
    2 所有服务相应文件都位于/data/app_1/目录下:比如:mysql.pid mysql.sock,当一台服务器上启动多个服务时,多个服务不会互相影响。但都放到缺省的/tmp/下则有可能被其他应用误删。
    3 当硬盘1出问题以后,直接将硬盘2放到一台装好MYSQL的服务器上就可以立刻恢复服务(如果放到my.cnf里则还需要备份相应的配置文件)。 

    服务启动后/data/app_1/下相应的文件和目录分布如下:
    /data/app_1/
    start_mysql.sh 服务启动脚本
    stop_mysql.sh 服务停止脚本
    mysql.pid 服务的进程ID
    mysql.sock 服务的SOCK
    var/ 数据区 
    mysql/ 用户库
    app_1_db_1/ 应用库
    app_2_db_2/
    ...
    /data/app_2/
    ... 

    查看所有的应用进程ID:
    cat /data/*/mysql.pid 

    查看所有数据库的错误日志:
    cat /data/*/var/*.err 

    个人建议:MYSQL的主要瓶颈在PORT的连接数上,因此,将表结构优化好以后,相应单个MYSQL服务的CPU占用仍然在10%以上,就要考虑将服务拆分到多个PORT上运行了。 

    服务的备份
    尽量使用MYSQL DUMP而不是直接备份数据文件,以下是一个按weekday将数据轮循备份的脚本:备份的间隔和周期可以根据备份的需求确定 
    /home/mysql/bin/mysqldump -S/data/app_1/mysql.sock -umysql db_name | gzip -f>/path/to/backup/db_name.`data +%w`.dump.gz
    因此写在CRONTAB中一般是:
    * 6 * * * /home/mysql/bin/mysqldump -S/data/app_1/mysql.sock -umysql db_name | gzip -f>/path/to/backup/db_name.`data +\%w`.dump.gz
    注意:
    1 在crontab中'%'需要转义成'\%'
    2 根据日志统计,应用负载最低的时候一般是在早上6点

      

    先备份在本地然后传到远程的备份服务器上,或者直接建立一个数据库备份帐号,直接在远程的服务器上备份,远程备份只需要将以上脚本中的-S /path/to/msyql.sock改成-h IP.ADDRESS即可。

    数据的恢复和系统的升级
    日常维护和数据迁移:在数据盘没有被破坏的情况下
    硬盘一般是系统中寿命最低的硬件。而系统(包括操作系统和MYSQL应用)的升级和硬件升级,都会遇到数据迁移的问题。
    只要数据不变,先装好服务器,然后直接将数据盘(硬盘2)安装上,只需要将启动脚本重新加入到rc.local文件中,系统就算是很好的恢复了。

    灾难恢复:数据本身被破坏的情况下
    确定破坏的时间点,然后从备份数据中恢复。

    应用的设计要点
    非用数据库不可吗?
    数据库的确可以简化很多应用的结构设计,但本身也是一个系统资源消耗比较大的应用。所以很多应用如果没有很高的实时统计需求的话,完全可以先记录到文件日志中,定期的导入到数据库中做后续统计分析。如果还是需要记录2维表结构,结构足够简单的话可以使用DBM结构。即使需要使用数据库的,应用如果没有太复杂的数据完整性需求的化,完全可以不使用那些支持外键的商业数据库, 

    数据库服务的主要瓶颈:单个服务的连接数
    对于一个应用来说,如果数据库表结构的设计能够按照数据库原理的范式来设计的话,并且已经使用了最新版本的MYSQL,并且按照比较优化的方式运行了,那么最后的主要瓶颈一般在于单个服务的连接数,即使一个数据库可以支持并发500个连接,最好也不要把应用用到这个地步,因为并发连接数过多数据库服务本身用于调度的线程的开销也会非常大了。所以如果应用允许的话:让一台机器多跑几个MYSQL服务分担。将服务均衡的规划到多个MYSQL服务端口上:比如app_1 ==> 3301 app_2 ==> 3302...app_9 ==> 3309。一个1G内存的机器跑上10个MYSQL是很正常的。让10个MYSQLD承担1000个并发连接效率要比让2个MYSQLD承担1000个效率高的多。当然,这样也会带来一些应用编程上的复杂度; 

    使用单独的数据库服务器(不要和前台WEB服务抢内存),MYSQL拥有更多的内存就可能能有效的进行结果集的缓存; 

    应用尽量使用PCONNECT和polling机制,用于节省MYSQL服务建立连接的开销; 

    表的横向拆分:让最常被访问的10%的数据放在一个小表里,90%的历史数据放在一个归档表里,数据中间通过定期“搬家”和定期删除无效数据来节省。这样对于应用来说总是在10%数据中进行选择,比较有利于数据的缓存,不要指望MYSQL中对单表记录数在10万级以上还有比较高的效率。 

    表的纵向拆分(过渡范化):将所有的定长字段(char, int等)放在一个表里,所有的变长字段(varchar,text,blob等)放在另外一个表里,2个表之间通过主键关联,这样,定长字段表可以得到很大的优化(甚至可以使用HEAP表类型,数据完全在内存中存取),这里也说明另外一个原则,对于我们来说,尽量使用定长字段可以通过空间的损失换取访问效率的提高。MYSQL之所以支持多种表类型,实际上是针对不同应用提供了不同的优化方式; 

    仔细的检查应用的索引设计,甚至在服务启动中加入 --log-slow-queries[=file]用于跟踪分析应用瓶颈。

    mysql中的临时表

      当工作在非常大的表上时,你可能偶尔需要运行很多查询获得一个大量数据的小的子集,不是对整个表运行这些查询,而是让MySQL每次找出所需的少数记录,将记录选择到一个临时表可能更快
    些,然后多这些表运行查询。

    创建临时表很容易,给正常的CREATE TABLE语句加上TEMPORARY关键字:

    CREATE TEMPORARY TABLE tmp_table (
    name VARCHAR(10) NOT NULL,
    value INTEGER NOT NULL
    )

    临时表将在你连接MySQL期间存在。当你断开时,MySQL将自动删除表并释放所用的空间。当然你可以在仍然连接的时候删除表并释放空间。

    DROP TABLE tmp_table

    如果在你创建名为tmp_table临时表时名为tmp_table的表在数据库中已经存在,临时表将有必要屏蔽(隐藏)非临时表tmp_table。

    如果你声明临时表是一个HEAP表,MySQL也允许你指定在内存中创建它:

     CREATE TEMPORARY TABLE tmp_table (
    name VARCHAR(10) NOT NULL,
    value INTEGER NOT NULL
    ) TYPE = HEAP
     

    因为HEAP表存储在内存中,你对它运行的查询可能比磁盘上的临时表快些。然而,HEAP表与一般的表有些不同,且有自身的限制。详见MySQL参考手册。

    正如前面的建议,你应该测试临时表看看它们是否真的比对大量数据库运行查询快。如果数据很好地索引,临时表可能一点不快。

    如何将图片储存在MySQL数据库里

     如果你想把二进制的数据,比如说图片文件和HTML文件,直接保存在你的MySQL数据库,那么这篇文章就是为你而写的!我将告诉你怎样通过HTML表单来储存这些文件,怎样访问和使用这些文件。 

      一、本文概述

      本文的主要内容如下:

      * 在MySQL中建立一个新的数据库 
      * 一个怎样储存文件的例子程序 
      * 一个怎样访问文件的例子程序 

      二、在MySQL中建立一个新的database 

      首先,你必须在你的MySQL中建立一个新的数据库,我们将会把那些二进制文件储存在这个数据库里。在例子中我会使用下列结构,为了建立数据库,你必须做下列步骤: 

      1. 进入MySQL控制器 
      2. 输入命令"create database binary_data;" 
      3. 输入命令"use binary_data;" 

      输入如下命令:

      "CREATE TABLE binary_data ( id INT(4) NOT NULL AUTO_INCREMENT PRIMARY KEY,description CHAR(50), bin_data LONGBLOB, filename CHAR(50), filesize CHAR(50), filetype CHAR(50));" (不能断行) 

      如果没有意外,数据库 和 表 应该建立好了。 

    三、一个怎样储存文件的例子程序 

      用这个例子你可以通过Html表单将文件传输到数据库中。 

      store.php3 

      // store.php3 - by Florian Dittmer 

      <?php 

      // 如果提交了表单,代码将被执行: 

      if ($submit) { 

      // 连接到数据库 
      // (你可能需要调整主机名,用户名和密码) 

      MYSQL_CONNECT( "localhost", "root", "password"); 
      MySQL_select_db( "binary_data"); 
      $data = addslashes(fread(fopen($form_data, "r"), filesize($form_data))); 
      $result=MYSQL_QUERY( "INSERT INTO binary_data (description,bin_data,filename,filesize,filetype)VALUES ('$form_description','$data','$form_data_name','$form_data_size','$form_data_type')"); 
      $id= MySQL_insert_id(); 
      print "This file has the following Database ID: $id"; 
      MYSQL_CLOSE(); 
      } else { 

      // 否则显示储存新数据的表单 


        @MySQL_select_db( "binary_data"); 
      $query = "select bin_data,filetype from binary_data where id=$id"; 
      $result = @MYSQL_QUERY($query); 
      $data = @MYSQL_RESULT($result,0, "bin_data"); 
      $type = @MYSQL_RESULT($result,0, "filetype"); 
      Header( "Content-type: $type"); 
      echo $data; 
      }; 
      ?> 

    程序必须知道要访问那个文件, 你必须将ID作为一个参数。 

      例如: 一个文件在数据库中的ID为2. 你可以这样调用它: getdata.php3?id=2 

      如果你将图片储存在数据库里, 你可以向调用图片一样调用它。 

      Example: 一个图片文件在数据库中的ID为3. 你可以这样调用它: 

      五、怎样储存大于1MB的文件

      如果你想储存大于1MB的文件,你必须对你的程序、PHP设置、SQL设置进行许多修改。 

      下面几条也许可以帮助你储存小于24MB的文件: 

      1) 修改 store.php3,将 MAX_FILE_SIZE 的值改成 24000000。 

      2) 修改你的PHP设置,在一般情况下,PHP只允许小于2MB的文件,你必须将max_filesize(在php.ini中)的值改成24000000 

      3) 去掉MYSQL的数据包大小限制,在一般情况下 MYSQL 小于1 MB的数据包。

      4) 你必须用以下参数重启你的MYSQL :/usr/local/bin/safe_MySQLd -O key_buffer=16M -O table_cache=128 -O sort_buffer=4M -O record_buffer=1M -O max_allowed_packet=24M 

      5) 如果仍然出错:可能是超时错误,如果你通过一个很慢的连接来储存一个很大的文件,PHP缺省的时间限制为30秒。你可以将max_execution_time(在php.ini中)的值改为-1 。

    加固你的MySQL

    前言

      MySQL已经成为当前网络中使用最多的数据库之一,特别是在Web应用上,它占据了中小型应用的绝对优势。这一切都源于它的小巧易用、它的安全有效、它的开放式许可、它的多平台,更主要的是它与三大Web语言之——PHP的完美结合。

      但不幸的是,一个缺省安全的MySQL,会因为root密码为空及程序漏洞导致被溢出,使得安装MySQL的服务器成为被经常攻击的对象。更严重的是,被攻击之后数据库往往遭破坏,造成灾难性的后果。下面将进入为了保护数据而进行的保卫战中。

    环境要求

    1.系统环境

      一台Red Hat 9.0自定义安装的服务器,系统安装了GCC及一些其它要求的软件包,比如Apache、PHP等。安装完系统后的第一件事就是升级系统的软件包。作为Web服务器,系统接受PHP脚本的请求,PHP则使用下面将要安装的MySQL数据库作为动态发布的接触。

      分区情况的要求和一般系统差不多,惟一不同之处在于后面建立的/chroot与/tmp要求在同一个分区上。

    2.安全要求


    (1)MySQL运行在一个独立的(Chroot)环境下;
    (2)mysqld进程运行于一个独立的用户/用户组下,
       此用户和用户组没有根目录,没有shell,也不能用于其它程序;
    (3)修改MySQL的root帐号,并使用一个复杂的密码;
    (4)只允许本地连接MySQL,启动MySQL时网络连接被禁止掉;
    (5)保证连接MySQL的nobody帐号登陆被禁止;
    (6)删除test数据库。



    安装MySQL

    1.安装准备

      安装MySQL之前,按照上述安全要求需要创建一个用于启动MySQL的用户和组。


    #groupadd mysql
    #useradd mysql -c "start mysqld's account" -d /dev/null -g mysql -s /sbin/nologin



    2.编译和安装

      下载MySQL源代码包:


    #wget http://mysql.he.net/Downloads/MySQL-4.0/mysql-4.0.16.tar.gz



    解压缩:


    #tar -zxvf mysql-4.0.16.tar.gz



      一般把MySQL安装在/usr/local/mysql下,如果有特殊要求,也可自行调整。不过这样做意义不大,因为后面将Chrooting,到时只是使用这里的客户工具而已,比如mysql,mysqladmin,mysqldump等。下面就开始编译安装吧。


    #./configure --prefix=/usr/local/mysql \
    --with-mysqld-user=mysql \
    --with-unix-socket-path=/tmp/mysql.sock \
    --with-mysqld-ldflags=-all-static
    #make && make install
    #strip /usr/local/mysql/libexec/mysqld
    #scripts/mysql_install_db
    #chown -R root /usr/local/mysql
    #chown -R mysql /usr/local/mysql/var
    #chgrp -R mysql /usr/local/mysql



      上面各步骤的具体作用在MySQL手册里已有介绍,惟一需要解释、和一般步骤不同的地方在于--with-mysqld-ldflags=-all-static。因为需要用到Chroot环境,而MySQL本身连接成静态后就无需再创建一些库环境了。

    3.配置与启动

      MySQL的配置文件需要手工选择、拷贝几个模板文件中的一个到/etc下,这几个模板文件位于源文件的support-files目录,一共4个:small、medium、large、huge。


    #cp support-files/my-medium.cnf /etc/my.cnf
    #chown root:sys /etc/my.cnf
    #chmod 644 /etc/my.cnf


    启动MySQL,注意使用用户为mysql:
    #/usr/local/mysq/bin/mysqld_safe --user=mysql &



    4.测试

      为了测试安装的程序是否正确及MySQL是否已经启动正常,最好的办法就是用MySQL客户端来连接数据库。


    #/usr/local/mysql/bin/mysql
    [root@ftp bin]# mysql
    Welcome to the MySQL monitor. Commands end with ; or \g.
    Your MySQL connection id is 687 to server version: 3.23.58
    Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
    mysql>
    mysql> show databases;
    +--------------+
    | Database |
    +--------------+
    | mysql |
    | test |
    +--------------+
    2 rows in set (0.00 sec)
    mysql>quit



      连接成功,可以关闭数据库:


    #/usr/local/mysql/bin/mysqladmin -uroot shutdown



      如果连接失败则需要仔细分析出错原因:


    #more /usr/local/mysql/var/`hostname`.err



    Chrooting

    1.Chrooting环境

      Chroot是Unix/类Unix的一种手段,它的建立会将其与主系统几乎完全隔离,也就是说,一旦遭到什么问题,也不会危及到正在运行的主系统。这是一个非常有效的办法,特别是在配置网络服务程序的时候。

    2.Chroot的准确工作

      首先,应当建立如图1示目录结构:

    图1 目录结构
    #mkdir -p /chroot/mysql/dev
    #mkdir -p /chroot/mysql/etc
    #mkdir -p /chroot/mysql/tmp
    #mkdir -p /chroot/mysql/var/tmp
    #mkdir -p /chroot/mysql/usr/local/mysql/libexec
    #mkdir -p /chroot/mysql/usr/local/mysql/share/mysql/english



      然后设定目录权限:


    #chown -R root:sys /chroot/mysql
    #chmod -R 755 /chroot/mysql
    #chmod 1777 /chroot/mysql/tmp



    3.拷贝mysql下的程序和文件到chroot下


    #cp -p /usr/local/mysql/libexec/mysqld /chroot/mysql/usr/local/mysql/libexec/
    #cp -p /usr/local/mysql/share/mysql/english/errmsg.sys
    /chroot/mysql/usr/local/mysql/share/mysql/english/
    #cp -p /etc/hosts /chroot/mysql/etc/
    #cp -p /etc/host.conf /chroot/mysql/etc/
    #cp -p /etc/resolv.conf /chroot/mysql/etc/
    #cp -p /etc/group /chroot/mysql/etc/
    #cp -p /etc/passwd /chroot/mysql/etc/passwd
    #cp -p /etc/my.cnf /chroot/mysql/etc/



    4.编辑chroot下的passwd文件和group文件


    #vi /chroot/etc/passwd



      删除除了mysql、root、sys的所有行


    #vi /chroot/etc/group



      删除除了mysql、root的所有行

    5.创建特殊的设备文件/dev/null

      参照系统的样子做即可:


    #ls -al /dev/null
    crw-rw-rw- 1 root root 1, 3 Jan 30 2003 /dev/null
    #mknod /chroot/mysql/dev/null c 1 3
    #chown root:root /chroot/mysql/dev/null
    #chmod 666 /chroot/mysql/dev/null



    6.拷贝mysql的数据库文件到chroot下


    #cp -R /usr/local/mysql/var/ /chroot/mysql/usr/local/mysql/var
    #chown -R mysql:mysql /chroot/mysql/usr/local/mysql/var



    7.安装chrootuid程序

      下载chrootuid,然后RPM安装即可。


    http://rpm.pbone.net/index.php3/stat/4/idpl/355932/com/
    chrootuid-1.3-alt2.i586.rpm.html



    8.测试Chroot环境下的MySQL配置


    #chrootuid /chroot/mysql mysql /usr/local/mysql/libexec/mysqld &



      如果失败请注意chroot目录下面的权限问题。

    9.测试连接chroot下的MySQL


    #/usr/local/mysql/bin/mysql --socket=/chroot/mysql/tmp/mysql.sock
    ..............
    mysql>show databases;
    mysql>create database wgh;
    mysql>quit;
    #ls -al /chroot/mysql/var/
    ...............



    配置服务器

      为了更加安全地使用MySQL,需要对MySQL的数据库进行安全配置;并且由于Chroot的原因,配置文件也会有所不同。

    1.关闭远程连接

      首先,应该关闭3306端口,这是MySQL的默认监听端口。由于此处MySQL只服务于本地脚本,所以不需要远程连接。尽管MySQL内建的安全机制很严格,但监听一个TCP端口仍然是危险的行为,因为如果MySQL程序本身有问题,那么未授权的访问完全可以绕过MySQL的内建安全机制。关闭网络监听的方法很简单,在/chroot/mysql/etc/my.cnf文件中的[mysqld]部分,去掉#skip-networking前面的“#”即可。

      关闭了网络,本地程序如何连接MySQL数据库呢?本地程序可以通过mysql.sock来连接,速度比网络连接更快。后文将提到关于mysql.sock的具体情况。

      MySQL的备份通常使用SSH来执行!

    2.禁止MySQL导入本地文件

      下面,将禁止MySQL中用“LOAD DATA LOCAL INFILE”命令。这个命令会利用MySQL把本地文件读到数据库中,然后用户就可以非法获取敏感信息了。网络上流传的一些攻击方法中就有用它的,它也是很多新发现的SQL Injection攻击利用的手段!

      为了禁止上述命令,在/chroot/mysql/etc/my.cnf文件的[mysqld]部分加入:


    set-variable=local-infile=0



      为了管理方便,一般在系统中的MySQL管理命令如mysql,mysqladmin,mysqldump等,使用的都是系统的/etc/my.cnf文件。如果要连接,它会寻找/tmp/mysql.sock文件来试图连接MySQL服务器,但是这里要连接的是chroot下的MySQL服务器,解决办法有两个:一个是在管理命令后面加入--socket=/chroot/mysql/tmp/mysql.sock。例如:


    #/usr/local/mysql/bin/mysql -root -p --socket=/chroot/mysql/tmp/mysql.sock



      第二个就是在/etc/my.cnf的[client]部分加入socket=/chroot/mysql/tmp/mysql.sock。显然,第二个方法方便多了。

    3.修改MySQL的root用户ID和密码


    #chrootuid /chroot/mysql mysql /usr/local/mysql/libexec/mysqld &
    #/usr/local/mysql/bin/mysql -uroot
    ...............
    mysql>SET PASSWORD FOR root@localhost=PASSWORD('new_password');



      尽量养成在mysql下输入密码的习惯,因为Shell下面输入的时候可能会被其它人看见。


    mysql>use mysql;
    mysql>update user set user="wghgreat" where user="root";
    mysql>select Host,User,Password,Select_priv,Grant_priv from user;
    mysql>delete from user where user='';
    mysql>delete from user where password='';
    mysql>delete from user where host='%';
    mysql>Drop database test;
    mysql>flush privileges;
    mysql>quit;



    修改为一个不容易猜的ID

    4.删除历史命令记录

      这些历史文件包括~/.bash_history、~/.mysql_history等。如果打开它们,你会大吃一惊,怎么居然有一些明文的密码在这里?!


    #cat /dev/null > ~/.bash_history
    #cat /dev/null > ~/.mysql_history



    PHP和MySQL通信

      默认情况下,PHP会通过/tmp/mysql.sock来和MySQL通信,但这里的一个大问题是MySQL生成的根本不是它,而是/chroot/mysql/tmp/mysql.sock。解决的办法就是做一个连接:


    #ln /chroot/mysql/tmp/mysql.sock /tmp/mysql.sock



      注意:由于hard links不能在文件系统的分区之间做,所以该处的连接必须位于同一分区内部。

    自启动配置

      自启动配置前先提示一点:即用于PHP的数据库需要用一个新建的帐号,其上有数据库权限设置,比如FILE、GRANT、ACTER、SHOW DATABASE、RELOAD、SHUTDOWN、PROCESS、SUPER等。

      自启动脚本示例:


    #!/bin/sh
    CHROOT_MYSQL=/chroot/mysql
    SOCKET=/tmp/mysql.sock
    MYSQLD=/usr/local/mysql/libexec/mysqld
    PIDFILE=/usr/local/mysql/var/`hostname`.pid
    CHROOTUID=/usr/bin/chrootuid
    echo -n " mysql"
    case "" in
    start)
    rm -rf $
    nohup $ ${CHROOT_MYSQL} mysql $ >/dev/null 2>&1 &
    sleep 5 && ln ${CHROOT_MYSQL}/$ $
    ;;
    stop)
    kill `cat ${CHROOT_MYSQL}/$`
    rm -rf ${CHROOT_MYSQL}/$
    ;;
    *)
    echo ""
    echo "Usage: `basename ` {start|stop}" >&2
    exit 64
    ;;
    esac
    exit 0



      文件位于/etc/rc.d/init.d下,名为mysqld,注意要可执行。


    #chmod +x /etc/rc.d/init.d/mysqld
    #ln -s /etc/rc.d/init.d/mysql /etc/rc3.d/S90mysql
    #ln -s /etc/rc.d/init.d/mysql /etc/rc0.d/K20mysql



      结论:尽管我们不能做到100%的安全,但是这些措施可以保护我们的系统更加安全!

    MySQL索引分析和优化

    一、什么是索引?

    索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存。如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录。表里面的记录数量越多,这个操作的代价就越高。如果作为搜索条件的列上已经创建了索引,MySQL无需扫描任何记录即可迅速得到目标记录所在的位置。如果表有1000个记录,通过索引查找记录至少要比顺序扫描记录快100倍。

    假设我们创建了一个名为people的表:

    CREATE TABLE people ( peopleid SMALLINT NOT NULL, name CHAR(50) NOT NULL );

    然后,我们完全随机把1000个不同name值插入到people表。下图显示了people表所在数据文件的一小部分:

    可以看到,在数据文件中name列没有任何明确的次序。如果我们创建了name列的索引,MySQL将在索引中排序name列:

    对于索引中的每一项,MySQL在内部为它保存一个数据文件中实际记录所在位置的“指针”。因此,如果我们要查找name等于“Mike”记录的peopleid(SQL命令为“SELECT peopleid FROM people WHERE name='Mike';”),MySQL能够在name的索引中查找“Mike”值,然后直接转到数据文件中相应的行,准确地返回该行的peopleid(999)。在这个过程中,MySQL只需处理一个行就可以返回结果。如果没有“name”列的索引,MySQL要扫描数据文件中的所有记录,即1000个记录!显然,需要MySQL处理的记录数量越少,则它完成任务的速度就越快。

    二、索引的类型

      MySQL提供多种索引类型供选择:

    普通索引
    这是最基本的索引类型,而且它没有唯一性之类的限制。普通索引可以通过以下几种方式创建: 创建索引,例如CREATE INDEX <索引的名字> ON tablename (列的列表); 修改表,例如ALTER TABLE tablename ADD INDEX [索引的名字] (列的列表); 创建表的时候指定索引,例如CREATE TABLE tablename ( [...], INDEX [索引的名字] (列的列表) ); 

    唯一性索引
    这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一。唯一性索引可以用以下几种方式创建: 创建索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表); 修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表); 创建表的时候指定索引,例如CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (列的列表) ); 

    主键
    主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”。如果你曾经用过AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键一般在创建表的时候指定,例如“CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) ); ”。但是,我们也可以通过修改表的方式加入主键,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主键。 

    全文索引
    MySQL从3.23.23版开始支持全文索引和全文检索。在MySQL中,全文索引的索引类型为FULLTEXT。全文索引可以在VARCHAR或者TEXT类型的列上创建。它可以通过CREATE TABLE命令创建,也可以通过ALTER TABLE或CREATE INDEX命令创建。对于大规模的数据集,通过ALTER TABLE(或者CREATE INDEX)命令创建全文索引要比把记录插入带有全文索引的空表更快。本文下面的讨论不再涉及全文索引,要了解更多信息,请参见MySQL documentation。 

    三、单列索引与多列索引

    索引可以是单列索引,也可以是多列索引。下面我们通过具体的例子来说明这两种索引的区别。假设有这样一个people表:

    这个数据片段中有四个名字为“Mikes”的人(其中两个姓Sullivans,两个姓McConnells),有两个年龄为17岁的人,还有一个名字与众不同的Joe Smith。

    这个表的主要用途是根据指定的用户姓、名以及年龄返回相应的peopleid。例如,我们可能需要查找姓名为Mike Sullivan、年龄17岁用户的peopleid(SQL命令为SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan' AND age=17;)。由于我们不想让MySQL每次执行查询就去扫描整个表,这里需要考虑运用索引。

    首先,我们可以考虑在单个列上创建索引,比如firstname、lastname或者age列。如果我们创建firstname列的索引(ALTER TABLE people ADD INDEX firstname (firstname);),MySQL将通过这个索引迅速把搜索范围限制到那些firstname='Mike'的记录,然后再在这个“中间结果集”上进行其他条件的搜索:它首先排除那些lastname不等于“Sullivan”的记录,然后排除那些age不等于17的记录。当记录满足所有搜索条件之后,MySQL就返回最终的搜索结果。

    由于建立了firstname列的索引,与执行表的完全扫描相比,MySQL的效率提高了很多,但我们要求MySQL扫描的记录数量仍旧远远超过了实际所需要的。虽然我们可以删除firstname列上的索引,再创建lastname或者age列的索引,但总地看来,不论在哪个列上创建索引搜索效率仍旧相似。

    为了提高搜索效率,我们需要考虑运用多列索引。如果为firstname、lastname和age这三个列创建一个多列索引,MySQL只需一次检索就能够找出正确的结果!下面是创建这个多列索引的SQL命令:

    ALTER TABLE people ADD INDEX fname_lname_age (firstname,lastname,age);

    由于索引文件以B-树格式保存,MySQL能够立即转到合适的firstname,然后再转到合适的lastname,最后转到合适的age。在没有扫描数据文件任何一个记录的情况下,MySQL就正确地找出了搜索的目标记录!

    那么,如果在firstname、lastname、age这三个列上分别创建单列索引,效果是否和创建一个firstname、lastname、age的多列索引一样呢?答案是否定的,两者完全不同。当我们执行查询的时候,MySQL只能使用一个索引。如果你有三个单列的索引,MySQL会试图选择一个限制最严格的索引。但是,即使是限制最严格的单列索引,它的限制能力也肯定远远低于firstname、lastname、age这三个列上的多列索引。

    四、最左前缀

    多列索引还有另外一个优点,它通过称为最左前缀(Leftmost Prefixing)的概念体现出来。继续考虑前面的例子,现在我们有一个firstname、lastname、age列上的多列索引,我们称这个索引为fname_lname_age。当搜索条件是以下各种列的组合时,MySQL将使用fname_lname_age索引:

    firstname,lastname,age firstname,lastname firstname 

    从另一方面理解,它相当于我们创建了(firstname,lastname,age)、(firstname,lastname)以及(firstname)这些列组合上的索引。下面这些查询都能够使用这个fname_lname_age索引:

    MySQL文档找到有关该命令的更多说明。下面是一个例子: 

    table type possible_keys key key_len ref rows Extra people ref fname_lname_age fname_lname_age 102 const,const,const 1 Where used 

    下面我们就来看看这个EXPLAIN分析结果的含义。

    table:这是表的名字。 type:连接操作的类型。下面是MySQL文档关于ref连接类型的说明:

    “对于每一种与另一个表中记录的组合,MySQL将从当前的表读取所有带有匹配索引值的记录。如果连接操作只使用键的最左前缀,或者如果键不是UNIQUE或PRIMARY KEY类型(换句话说,如果连接操作不能根据键值选择出唯一行),则MySQL使用ref连接类型。如果连接操作所用的键只匹配少量的记录,则ref是一种好的连接类型。”

    在本例中,由于索引不是UNIQUE类型,ref是我们能够得到的最好连接类型。

    如果EXPLAIN显示连接类型是“ALL”,而且你并不想从表里面选择出大多数记录,那么MySQL的操作效率将非常低,因为它要扫描整个表。你可以加入更多的索引来解决这个问题。预知更多信息,请参见MySQL的手册说明。
    possible_keys:
    可能可以利用的索引的名字。这里的索引名字是创建索引时指定的索引昵称;如果索引没有昵称,则默认显示的是索引中第一个列的名字(在本例中,它是“firstname”)。默认索引名字的含义往往不是很明显。
    Key:
    它显示了MySQL实际使用的索引的名字。如果它为空(或NULL),则MySQL不使用索引。
    key_len:
    索引中被使用部分的长度,以字节计。在本例中,key_len是102,其中firstname占50字节,lastname占50字节,age占2字节。如果MySQL只使用索引中的firstname部分,则key_len将是50。
    ref:
    它显示的是列的名字(或单词“const”),MySQL将根据这些列来选择行。在本例中,MySQL根据三个常量选择行。
    rows:
    MySQL所认为的它在找到正确的结果之前必须扫描的记录数。显然,这里最理想的数字就是1。
    Extra:
    这里可能出现许多不同的选项,其中大多数将对查询产生负面影响。在本例中,MySQL只是提醒我们它将用WHERE子句限制搜索结果集。

    七、索引的缺点

    到目前为止,我们讨论的都是索引的优点。事实上,索引也是有缺点的。

    首先,索引要占用磁盘空间。通常情况下,这个问题不是很突出。但是,如果你创建每一种可能列组合的索引,索引文件体积的增长速度将远远超过数据文件。如果你有一个很大的表,索引文件的大小可能达到操作系统允许的最大文件限制。

    第二,对于需要写入数据的操作,比如DELETE、UPDATE以及INSERT操作,索引会降低它们的速度。这是因为MySQL不仅要把改动数据写入数据文件,而且它还要把这些改动写入索引文件。

    【结束语】在大型数据库中,索引是提高速度的一个关键因素。不管表的结构是多么简单,一次500000行的表扫描操作无论如何不会快。如果你的网站上也有这种大规模的表,那么你确实应该花些时间去分析可以采用哪些索引,并考虑是否可以改写查询以优化应用。要了解更多信息,请参见MySQL manual。另外注意,本文假定你所使用的MySQL是3.23版,部分查询不能在3.22版MySQL上执行。

    MySQL群集双机模拟

      硬件配置
      
      普通PC server * 2 (最小集群环境需要4台服务器)
      
      模拟环境
      
      red hat linux9 for x86 (or red hat AS 2以上版本),glibc-2.2, static, gcc
      
      MySQL版本4.1.12 binares
      
      mysql-max binary版本目前只支持linux、max os x和solaris
      
      本方案不涉及从源代码编译安装
      
      主机 IP地址 用途
      
      ndb1_mgmd_sqld 1192.168.1.100 Ndb node1+mgmd node1+sqld node1
      ndb2_sqld2 192.168.1.200 Ndb node2+sqld node2
      
      Mgmd:management server
      sqld:mysql server
      ndb:storaged node (share-nothing,base in memory)
      
      安装
      
      从http://dev.mysql.com/downloads/mysql/4.1.html下载mysql-max-4.1.12-pc-linux-gnu-i686.tar.gz到/var/tmp
      Storage and SQL Node Installation
      
      在两台主机上执行如下过程
      
      shell>groupadd mysql
      shell>useradd -g mysql mysql
      shell>tar zxfv mysql-max-4.1.12-pc-linux-gnu-i686.tar.gz
      shell>cp -vr mysql-max-4.1.12-pc-linux-gnu-i686 /usr/local/mysql-max-4.1.12-pc-linux-gnu-i686
      shell>cd /usr/local
      shell>ln -s mysql-max-4.1.12-pc-linux-gnu-i686 mysql
      shell>cd mysql;scripts/mysql_install_db –user=mysql
      shell>chown -R root .;chown -R mysql data;chgrp -R mysql .
      shell>cp support-files/mysql.server /etc/rc.d/init.d/
      shell>chmod +x /etc/rc.d/init.d/mysql.server
      shell>chkconfig --add mysql.server
      shell>chkconfig –level 3 mysql.server off
      Management Node Installation
      
      在主机ndb1_mgmd_sqld1上执行如下过程
      
      shell>cd /var/tmp
      shell>tar -zxvf mysql-max-4.1.12a-pc-linux-gnu-i686.tar.gz /usr/local/bin '*/bin/ndb_mgm*'
      Configuration
      Configuring the Storage and SQL Nodes
      
      在两台主机上执行如下过程:
      
      shell>vi /etc/my.cnf
      [MYSQLD]             # Options for mysqld process:
      ndbcluster           # run NDB engine
      ndb-connectstring=192.168.1.100  # location of MGM node
      
      [MYSQL_CLUSTER]         # Options for ndbd process:
      ndb-connectstring=192.168.1.100  # location of MGM node
      
      Configuring the Management Node
      
      在主机ndb1_mgmd_sqld1上执行如下过程
      
      shell>mkdir /var/lib/mysql-cluster
      shell>cd /var/lib/mysql-cluster
      shell>vi config.ini
      [NDBD DEFAULT]   # Options affecting ndbd processes on all data nodes:
      NoOfReplicas=2   # Number of replicas
      DataMemory=80M   # How much memory to allocate for data storage
      IndexMemory=52M  # How much memory to allocate for index storage
      # For DataMemory and IndexMemory, we have used the
      # default values. Since the "world" database takes up
      # only about 500KB, this should be more than enough for
      # this example Cluster setup.
      [TCP DEFAULT]
      
      [NDB_MGMD]        # Management process options:
      hostname=192.168.1.100 # Hostname or IP address of MGM node
      datadir=/var/lib/mysql-cluster  # Directory for MGM node logfiles
      
      [NDBD]             # Options for data node "A":
      # (one [NDBD] section per data node)
      HostName=192.168.1.100      # Hostname or IP address
      DataDir=/usr/local/mysql/data  # Directory for this data node's datafiles
      
      [NDBD]             # Options for data node "B":
      hostname=192.168.1.200      # Hostname or IP address
      datadir=/usr/local/mysql/data  # Directory for this data node's datafiles
      
      [MYSQLD]             # SQL node options:
      hostname=192.168.1.100     # Hostname or IP address
      # Directory for SQL node's datafiles
      # (additional mysqld connections can be
      # specified for this node for various
      # purposes such as running ndb_restore)
      
      [MYSQLD] # SQL node options:
      hostname=192.168.1.200     # Hostname or IP address
      # Directory for SQL node's datafiles
      # (additional mysqld connections can be
      # specified for this node for various
      # purposes such as running ndb_restore)
      
      第一次启动
      
      在主机ndb1_mgmd_sqld1上执行如下过程
      
      shell> ndb_mgmd -f /var/lib/mysql-cluster/config.ini
      
      在两台主机上执行如下过程
      
      shell>ndbd –initial (note:--initial选项只能在第一次启动的时候使用)
      shell>/etc/init.d/mysql.server start
      
      测试
      
      在主机ndb1_mgmd_sqld1上执行如下过程
      shell> ndb_mgm
      -- NDB Cluster -- Management Client --
      ndb_mgm> show
      Connected to Management Server at: localhost:1186
      Cluster Configuration
      ---------------------
      [ndbd(NDB)]   2 node(s)
      id=2  @192.168.0.100 (Version: 4.1.12, Nodegroup: 0, Master)
      id=3  @192.168.0.200 (Version: 4.1.12, Nodegroup: 0)
      
      [ndb_mgmd(MGM)] 1 node(s)
      id=1  @192.168.0.100 (Version: 4.1.12)
      
      [mysqld(SQL)]  1 node(s)
      id=4  (Version: 4.1.12)
      
      出现如上信息则表示mysql群集安装成功
      
      数据抽样测试
      
      在主机ndb1_mgmd_sqld1上执行如下过程
      
      shell>/usr/local/mysql/bin/mysql -u root test
      MySQL>DROP TABLE IF EXISTS City;
      CREATE TABLE City (
      ID int(11) NOT NULL auto_increment,
      Name char(35) NOT NULL default '',
      CountryCode char(3) NOT NULL default '',
      District char(20) NOT NULL default '',
      Population int(11) NOT NULL default '0',
      PRIMARY KEY (ID)
      ) ENGINE=NDBCLUSTER;
      
      MySQL>INSERT INTO City VALUES (1,'Kabul','AFG','Kabol',1780000);
      INSERT INTO City VALUES (2,'Qandahar','AFG','Qandahar',237500);
      INSERT INTO City VALUES (3,'Herat','AFG','Herat',186800);
      
      在主机ndb2_sqld2上执行如下过程
      
      shell>/usr/local/mysql/bin/mysql -u root mysql
      MySQSL>select * from City;
      
      如果成功显示数据信息,则表示集群已经成功启动
      
      Safe Shutdown and Restart
      
      在主机ndb1_mgmd_sqld1上执行如下过程
      
      shell>ndb_mgm -e shutdown (关闭集群服务器,storage node也会自动被关闭)
      
      在两台主机上执行如下过程
      
      shell>/etc/init.d/mysql.server stop
      
      重新启动集群(顺序不能弄错)
      
      在主机ndb1_mgmd_sqld1上执行如下过程
      
      shell> ndb_mgmd -f /var/lib/mysql-cluster/config.ini
      
      在两台主机上执行如下过程
      
      shell>/usr/local/mysql/bin/ndbd
      
      启动完ndbd进程后启动sqld进程
      
      shell>/etc/init.d/mysql.server start
      
      附:
      
      config.ini中各部分解释
      
      [COMPUTER]: 定义群集主机.
      
      [NDBD]: 定义群集数据节点.
      
      [MYSQLD]: 定义Sql server节点.
      
      [MGM|NDB_MGMD]: Defines the management server node in the cluster.
      [TCP]: Defines TCP/IP connections between nodes in the cluster, with TCP/IP being the default connection protocol.
      [SHM]: Defines shared-memory connections between nodes. 在MySQL 4.1.9之前,这个功能必须使用--with-ndb-shm option编译进去, 从MySQL 4.1.9-max版本开始, it is enabled by default(默认为打开状态).

    MySQL查询优化--数据类型与效率

      这一部分提供了如何选择数据类型来帮助提高查询运行速度的一些指导:
      
      在可以使用短数据列的时候就不要用长的。如果你有一个固定长度的CHAR数据列,那么就不要让它的长度超出实际需要。如果你在数据列中存储的最长的值有40个字符,就不要定义成CHAR(255),而应该定义成CHAR(40)。如果你能够用MEDIUMINT代替BIGINT,那么你的数据表就小一些(磁盘 I/O少一些),在计算过程中,值的处理速度也快一些。如果数据列被索引了,那么使用较短的值带来的性能提高更加显著。不仅索引可以提高查询速度,而且短的索引值也比长的索引值处理起来要快一些。
      
      如果你可以选择数据行的存储格式,那么应该使用最适合存储引擎的那种。对于 MyISAM数据表,最好使用固定长度的数据列代替可变长度的数据列。例如,让所有的字符列用CHAR类型代替VARCHAR类型。权衡得失,我们会发现数据表使用了更多的磁盘空间,但是如果你能够提供额外的空间,那么固定长度的数据行被处理的速度比可变长度的数据行要快一些。对于那些被频繁修改的表来说,这一点尤其突出,因为在那些情况下,性能更容易受到磁盘碎片的影响。
      
      · 在使用可变长度的数据行的时候,由于记录长度不同,在多次执行删除和更新操作之后,数据表的碎片要多一些。你必须使用OPTIMIZE TABLE来定期维护其性能。固定长度的数据行没有这个问题。
      
      · 如果出现数据表崩溃的情况,那么数据行长度固定的表更容易重新构造。使用固定长度数据行的时候,每个记录的开始位置都可以被检测到,因为这些位置都是固定记录长度的倍数,但是使用可变长度数据行的时候就不一定了。这不是与查询处理的性能相关的问题,但是它一定能够加快数据表的修复速度。
      
      尽管把MyISAM数据表转换成使用固定长度的数据列可以提高性能,但是你首先需要考虑下面一些问题:
      
      · 固定长度的数据列速度较快,但是占用的空间也较大。CHAR(n)列的每个值(即使是空值)通常占n个字符,这是因为把它存储到数据表中的时候,会在值的后面添加空格。VARCHAR(n)列占有的空间较小,因为只需要分配必要的字符个数用于存储值,加上一两个字节来存储值的长度。因此,在CHAR和 VARCHAR列之间进行选择的时候,实际上是时间与空间的对比。如果速度是主要的考虑因素,那么就使用CHAR数据列获取固定长度列的性能优势。如果空间很重要,那么就使用VARCHAR数据列。总而言之,你可以认为固定长度的数据行可以提高性能,虽然它占用了更大的空间。但是对于某些特殊的应用程序,你可能希望使用两种方式来实现某个数据表,然后运行测试来决定哪种情况符合应用程序的需求。
      
      · 即使愿意使用固定长度类型,有时候你也没有办法使用。例如,长于255个字符的字符串就无法使用固定长度类型。
      
      MEMORY数据表目前都使用固定长度的数据行存储,因此无论使用CHAR或VARCHAR列都没有关系。两者都是作为CHAR类型处理的。
      
      对于InnoDB数据表,内部的行存储格式没有区分固定长度和可变长度列(所有数据行都使用指向数据列值的头指针),因此在本质上,使用固定长度的 CHAR列不一定比使用可变长度VARCHAR列简单。因而,主要的性能因素是数据行使用的存储总量。由于CHAR平均占用的空间多于VARCHAR,因此使用VARCHAR来最小化需要处理的数据行的存储总量和磁盘I/O是比较好的。
      
      对于BDB数据表,无论使用固定长度或可变长度的数据列,差别都不大。两种方法你都可用试一下,运行一些实验测试来检测是否存在明显的差别。
      
      把数据列定义成不能为空(NOT NULL)。这会使处理速度更快,需要的存储更少。它有时候还简化了查询,因为在某些情况下你不需要检查值的NULL属性。
      
      考虑使用ENUM数据列。如果你拥有的某个数据列的基数很低(包含的不同的值数量有限),那么可以考虑把它转换为ENUM列。ENUM值可以被更快地处理,因为它们在内部表现为数值。
      
      使用PROCEDURE ANALYSE()。运行PROCEDURE ANALYSE()可以看到数据表中列的情况:
      
      SELECT * FROM tbl_name PROCEDURE ANALYSE();
      SELECT * FROM tbl_name PROCEDURE ANALYSE(16,256);
      
      输出的每一列信息都会对数据表中的列的数据类型提出优化建议。第二个例子告诉PROCEDURE ANALYSE()不要为那些包含的值多于16个或者256字节的ENUM类型提出建议。如果没有这样的限制,输出信息可能很长;ENUM定义通常很难阅读。

      根据的PROCEDURE ANALYSE()输出信息,你可能发现,可以修改自己的数据表来利用那些效率更高的数据类型。如果你决定改变某个数据列的类型,需要使用ALTER TABLE语句。
      
      使用OPTIMIZE TABLE来优化那些受到碎片影响的数据表。被大量修改的数据表,特别是那些包含可变长度数据列的表,容易遭受碎片的影响。碎片很糟糕,因为它会导致用于存储数据表的磁盘块形成无用空间(空洞)。随着时间的推移,为了得到有效的数据行,你必须读取更多的块,性能就会降低。这会出现在任何可变长度的数据行上,
      
      但是对于BLOB或TEXT数据列尤其突出,因为它们的长度差异太大了。在正常情况下使用OPTIMIZE TABLE会防止数据表的性能降低。OPTIMIZE TABLE可以用于MyISAM和BDB数据表,但是defragments只能用于MyISAM数据表。任何存储引擎中的碎片整理方法都是用 mysqldump来转储(dump)数据表,接着使用转储的文件删除并重新建立那些数据表:
      
      % mysqldump --opt db_name tbl_name > dump.sql
      % mysql db_name < dump.sql
      
      把数据打包放入BLOB或TEXT数据列。使用BLOB或TEXT数据列存储打包(pack)的数据,并在应用程序中进行解包(unpack),使你能够在一次检索操作中得到需要的任何信息,而不需要进行多次检索。它对那些很难用标准的数据表结构表现的数据值和频繁变化的数据值也是有帮助的。
      
      解决这个问题的另一种方法是让那些处理Web窗体的应用程序把数据打包成某种数据结构,然后把它插入到单个BLOB或TEXT数据列中。例如,你可以使用XML表示调查表回复,把那些XML字符串存储在TEXT数据列中。由于要对数据进行编码(从数据表中检索数据的时候还需要解码),它会增加客户端的开销,但是可以简化数据结构,而且它还消除了那些因为改变了调查表的内容而必须改变数据表结构的需求。
      
      另一方面,BLOB和TEXT值也会引起自己的一些问题,特别是执行了大量的删除或更新操作的时候。删除这种值会在数据表中留下很大的"空洞",以后填入这些"空洞"的记录可能长度不同(前面讨论的OPTIMIZE TABLE提出解决这个问题的一些建议)。
      
      使用合成的(synthetic)索引。合成的索引列在某些时候是有用的。一种办法是根据其它的列的内容建立一个散列值,并把这个值存储在单独的数据列中。接下来你就可以通过检索散列值找到数据行了。但是,我们要注意这种技术只能用于精确匹配的查询(散列值对于类似<或>=等范围搜索操作符是没有用处的)。我们可以使用MD5()函数生成散列值,也可以使用SHA1()或CRC32(),或者使用自己的应用程序逻辑来计算散列值。请记住数值型散列值可以很高效率地存储。同样,如果散列算法生成的字符串带有尾部空格,就不要把它们存储在CHAR或VARCHAR列中,它们会受到尾部空格去除的影响。
      
      合成的散列索引对于那些BLOB或TEXT数据列特别有用。用散列标识符值查找的速度比搜索BLOB列本身的速度快很多。
      
      在不必要的时候避免检索大型的BLOB或TEXT值。例如,SELECT *查询就不是很好的想法,除非你能够确定作为约束条件的WHERE子句只会找到所需要的数据行。否则,你可能毫无目的地在网络上传输大量的值。这也是 BLOB或TEXT标识符信息存储在合成的索引列中对我们有所帮助的例子。你可以搜索索引列,决定那些需要的数据行,然后从合格的数据行中检索BLOB或 TEXT值。
      
      把BLOB或TEXT列分离到单独的表中。在某些环境中,如果把这些数据列移动到第二张数据表中,可以让你把原数据表中的数据列转换为固定长度的数据行格式,那么它就是有意义的。这会减少主表中的碎片,使你得到固定长度数据行的性能优势。它还使你在主数据表上运行 SELECT *查询的时候不会通过网络传输大量的BLOB或TEXT值。
      
      高效率地载入数据
      
      在大多数情况下,你所关注的是SELECT查询的优化,因为SELECT查询是最常见的查询类型,而且如何优化它们又不是太简单。与此形成对比,把数据载入数据库的操作就相对直接了。然而,你仍然可以利用某些策略来改善数据载入操作的效率。基本的原理如下所示:
      
      · 批量载入比单行载入的效率高,因为在每条
      
      记录被载入后,键缓存(key cache)不用刷新(flush);可以在这批记录的末尾刷新键缓存。键缓存刷新的频率减少得越多,数据载入的速度就越快。
      
      · 没有索引的数据表的载入速度比有索引的要快一些。如果存在索引,不但要把记录添加到数据文件中,还必须修改索引来反映新增的记录。
      
      · 较短的SQL语句比较长的SQL语句快,因为它们所涉及到服务器端分析过程较少,同时通过网络把它们从客户端发送到服务器上的速度也更快。
      
      其中有些因素看起来是次要的(尤其是最后一个),但是如果你载入的数据很多,那么即使很小的效率差异也会导致一定的性能差别。我们可以从前面的一般原理得出几条如何快速载入数据的实践结论:
      
      · LOAD DATA(所有形式的)比INSERT效率高,因为它是批量载入数据行的。服务器只需要分析和解释一条语句,而不是多条语句。同样,索引只需要在所有的数据行被处理过之后才刷新,而不是每行刷新一次。
      
      · 不带LOCAL的LOAD DATA比带有LOCAL的LOAD DATA的速度要快。

    MySQL字段类型说明

      MySQL支持大量的列类型,它可以被分为3类:数字类型、日期和时间类型以及字符串(字符)类型。本节首先给出可用类型的一个概述,并且总结每个列类型的存储需求,然后提供每个类中的类型性质的更详细的描述。概述有意简化,更详细的说明应该考虑到有关特定列类型的附加信息,例如你能为其指定值的允许格式。

      由MySQL支持的列类型列在下面。下列代码字母用于描述中:

      M
      指出最大的显示尺寸。最大的合法的显示尺寸是 255 。
      D
      适用于浮点类型并且指出跟随在十进制小数点后的数码的数量。最大可能的值是30,但是应该不大于M-2。
      方括号(“[”和“]”)指出可选的类型修饰符的部分。

      注意,如果你指定一个了为ZEROFILL,MySQL将为该列自动地增加UNSIGNED属性。

      TINYINT[(M)] [UNSIGNED] [ZEROFILL]
      一个很小的整数。有符号的范围是-128到127,无符号的范围是0到255。

      
      SMALLINT[(M)] [UNSIGNED] [ZEROFILL]
      一个小整数。有符号的范围是-32768到32767,无符号的范围是0到65535。

      MEDIUMINT[(M)] [UNSIGNED] [ZEROFILL]
      一个中等大小整数。有符号的范围是-8388608到8388607,无符号的范围是0到16777215。

      INT[(M)] [UNSIGNED] [ZEROFILL]
      一个正常大小整数。有符号的范围是-2147483648到2147483647,无符号的范围是0到4294967295。

      INTEGER[(M)] [UNSIGNED] [ZEROFILL]
      这是INT的一个同义词。

      BIGINT[(M)] [UNSIGNED] [ZEROFILL]

      一个大整数。有符号的范围是-9223372036854775808到9223372036854775807,无符号的范围是0到

      18446744073709551615。注意,所有算术运算用有符号的BIGINT或DOUBLE值完成,因此你不应该使用大于 9223372036854775807(63位)的有符号大整数,除了位函数!注意,当两个参数是INTEGER值时,-、+和*将使用BIGINT运算!这意味着如果你乘2个大整数(或来自于返回整数的函数),如果结果大于9223372036854775807,你可以得到意外的结果。一个浮点数字,不能是无符号的,对一个单精度浮点数,其精度可以是<=24,对一个双精度浮点数,是在25 和53之间,这些类型如FLOAT和DOUBLE类型马上在下面描述。FLOAT(X)有对应的FLOAT和DOUBLE相同的范围,但是显示尺寸和小数位数是未定义的。在MySQL3.23中,这是一个真正的浮点值。在更早的MySQL版本中,FLOAT(precision)总是有2位小数。该句法为了ODBC兼容性而提供。

      FLOAT[(M,D)] [ZEROFILL]
      一个小(单精密)浮点数字。不能无符号。允许的值是-3.402823466E+38到 -1.175494351E-38,0 和1.175494351E-38到3.402823466E+38。M是显示宽度而D是小数的位数。没有参数的FLOAT或有<24 的一个参数表示一个单精密浮点数字。

      DOUBLE[(M,D)] [ZEROFILL]
      一个正常大小(双精密)浮点数字。不能无符号。允许的值是- 1.7976931348623157E+308到-2.2250738585072014E-308、 0和2.2250738585072014E-308到1.7976931348623157E+308。M是显示宽度而D是小数位数。没有一个参数的 DOUBLE或FLOAT(X)(25 < = X < = 53)代表一个双精密浮点数字。

      DOUBLE PRECISION[(M,D)] [ZEROFILL]
      

      REAL[(M,D)] [ZEROFILL]
      这些是DOUBLE同义词。

      DECIMAL[(M[,D])] [ZEROFILL]
      一个未压缩(unpack)的浮点数字。不能无符号。行为如同一个CHAR列: “未压缩”意味着数字作为一个字符串被存储,值的每一位使用一个字符。小数点,并且对于负数,“-”符号不在M中计算。如果D是0,值将没有小数点或小数部分。DECIMAL值的最大范围与DOUBLE相同,但是对一个给定的DECIMAL列,实际的范围可以通过M和D的选择被限制。如果D被省略,它被设置为0。如果M被省掉,它被设置为10。注意,在MySQL3.22里,M参数包括符号和小数点。

      NUMERIC(M,D) [ZEROFILL]
      这是DECIMAL的一个同义词。

      DATE
      一个日期。支持的范围是'1000-01-01'到'9999-12-31'。MySQL以'YYYY-MM-DD'格式来显示DATE值,但是允许你使用字符串或数字把值赋给DATE列。

      DATETIME
      一个日期和时间组合。支持的范围是'1000-01-01 00:00:00'到'9999-12-31 23:59:59'。MySQL以'YYYY-MM-DD HH:MM:SS'格式来显示DATETIME值,但是允许你使用字符串或数字把值赋给DATETIME的列。

      TIMESTAMP[(M)]
      一个时间戳记。范围是'1970-01-01 00:00:00'到2037年的某时。MySQL以YYYYMMDDHHMMSS、YYMMDDHHMMSS、YYYYMMDD或YYMMDD格式来显示TIMESTAMP值,取决于是否M是14(或省略)、12、8或6,但是允许你使用字符串或数字把值赋给TIMESTAMP列。一个 TIMESTAMP列对于记录一个INSERT或UPDATE操作的日期和时间是有用的,因为如果你不自己给它赋值,它自动地被设置为最近操作的日期和时间。你以可以通过赋给它一个NULL值设置它为当前的日期和时间。
      TIME
      一个时间。范围是'-838:59:59'到'838:59:59'。MySQL以'HH:MM:SS'格式来显示TIME值,但是允许你使用字符串或数字把值赋给TIME列。

      YEAR[(2|4)]
      一个2或4位数字格式的年(缺省是4位)。允许的值是1901到2155,和0000(4位年格式),如果你使用 2位,1970-2069( 70-69)。MySQL以YYYY格式来显示YEAR值,但是允许你把使用字符串或数字值赋给YEAR列。(YEAR类型在MySQL3.22中是新类型。)

      CHAR(M) [BINARY]
      一个定长字符串,当存储时,总是是用空格填满右边到指定的长度。M的范围是1 ~ 255个字符。当值被检索时,空格尾部被删除。CHAR值根据缺省字符集以大小写不区分的方式排序和比较,除非给出BINARY关键词。NATIONAL CHAR(短形式NCHAR)是ANSI SQL的方式来定义CHAR列应该使用缺省字符集。这是MySQL的缺省。CHAR是CHARACTER的一个缩写。

      [NATIONAL] VARCHAR(M) [BINARY]
      一个变长字符串。注意:当值被存储时,尾部的空格被删除(这不同于 ANSI SQL规范)。M的范围是1 ~ 255个字符。 VARCHAR值根据缺省字符集以大小写不区分的方式排序和比较,除非给出BINARY关键词值。 VARCHAR是CHARACTER VARYING一个缩写。

      TINYBLOB
      
      TINYTEXT
      一个BLOB或TEXT列,最大长度为255(2^8-1)个字符。
      BLOB
      

      TEXT
      一个BLOB或TEXT列,最大长度为65535(2^16-1)个字符。

      MEDIUMBLOB
      
      MEDIUMTEXT
      一个BLOB或TEXT列,最大长度为16777215(2^24-1)个字符。
      LONGBLOB
      
      LONGTEXT
      一个BLOB或TEXT列,最大长度为4294967295(2^32-1)个字符。

      ENUM('value1','value2',...)
      枚举。一个仅有一个值的字符串对象,这个值式选自与值列表'value1'、'value2', ...,或NULL。一个ENUM最多能有65535不同的值。

      SET('value1','value2',...)
      一个集合。能有零个或多个值的一个字符串对象,其中每一个必须从值列表'value1', 'value2', ...选出。一个SET最多能有64个成员。

    MySQL开发中的外键与参照完整性

      参照完整性(Referential integrity)是数据库设计中一个重要的概念。在系统不同的列表中,当数据库所有参照合法或非合法关联时都会涉及到参照完整性。当参照完整性存在时,任何与不存在记录的关联将变得无效化,由此可防止用户出现各种错误,从而提供更为准确和实用的数据库。

      参照完整性通常通过外键(foreign key)的使用而被广泛应用。长久以来,流行工具开源RDBMS MySQL并没有支持外键,原因是这种支持将会降低RDBMS的速度和性能。然而,由于很多用户对参照完整性的优点倍感兴趣,最近MySQL的不同版本都通过新InnoDB列表引擎支持外键。由此,在数据库组成的列表中保持参照完整性将变得非常简单。

      为了建立两个MySQL表之间的一个外键关系,必须满足以下三种情况:

      两个表必须是InnoDB表类型。
      使用在外键关系的域必须为索引型(Index)。
      使用在外键关系的域必须与数据类型相似。
      例子是理解以上要点的最好方法。如表A所示,建立两个表,其中一个列出动物种类及相应的代码(表名为:species),另一表列出动物园中的动物(表名为:zoo)。现在,我们想通过species关联这两个表,所以我们只需要接受和保存zoo表中包含species表中的合法动物的入口到数据库中。

      表A

      mysql> CREATE TABLE species (id TINYINT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, PRIMARY KEY(id)) ENGINE=INNODB;
      Query OK, 0 rows affected (0.11 sec)

      mysql> INSERT INTO species VALUES (1, 'orangutan'), (2, 'elephant'), (3, 'hippopotamus'), (4, 'yak');
      Query OK, 4 rows affected (0.06 sec)
      Records: 4 Duplicates: 0 Warnings: 0

      mysql> CREATE TABLE zoo (id INT(4) NOT NULL, name VARCHAR(50) NOT NULL, FK_species TINYINT(4) NOT NULL, INDEX (FK_species), FOREIGN KEY (FK_species) REFERENCES species (id), PRIMARY KEY(id)) ENGINE=INNODB;

      注意:对于非InnoDB表, FOREIGN KEY 语句将被忽略。

      现在,fieldszoo.species与species.id 之间存在一个外键关系。只有相应的zoo.specie与species.idfield的一个值相匹配,动物表中的入口才可被访问。以下的输出即演示了当你想输入一个Harry Hippopotamus记录,而使用到不合法的species代码:

      mysql> INSERT INTO zoo VALUES (1, 'Harry', 5);
      ERROR 1216 (23000): Cannot add or update a child row: a foreign key constraint fails

      这里,MySQL核查species表以查看species代码是否存在,如果发现不存在,就拒绝该记录。当你输入正确代码的,可以与以上做比较。

      mysql> INSERT INTO zoo VALUES (1, 'Harry', 3);
      Query OK, 1 row affected (0.06 sec)

      这里,MySQL核查species表以查看species代码是否存在,当发现存在,允许记录保存在zoo表中。

      为了删除一个外键关系,首先使用SHOW CREATE TABLE找出InnoDB的内部标签,如表B所示:

      表 B

      +-------+---------------------------------------------------+
      | Table | Create Table |
      +-------+---------------------------------------------------+
      | zoo | CREATE TABLE `zoo` (
      `id` int(4) NOT NULL default '0',
      `name` varchar(50) NOT NULL default '',
      `FK_species` tinyint(4) NOT NULL default '0',
      KEY `FK_species` (`FK_species`),
      CONSTRAINT `zoo_ibfk_1` FOREIGN KEY (`FK_species`)
      REFERENCES `species` (`id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
      +-------+----------------------------------------------------+

      然后使用带有DROP FOREIGN KEY 语句的ALTER TABLE命令,如以下:

      mysql> ALTER TABLE zoo DROP FOREIGN KEY zoo_ibfk_1;
      Query OK, 1 row affected (0.11 sec)
      Records: 1 Duplicates: 0 Warnings: 0

      为了将一个外键添加到一个现成的表中,使用ADD FOREIGN KEY的 ALTER TABLE语句指定合适的域作为一个外键:

      mysql> ALTER TABLE zoo ADD FOREIGN KEY (FK_species) REFERENCES species (id);
      Query OK, 1 rows affected (0.11 sec)
      Records: 1 Duplicates: 0 Warnings: 0

      如以上例子解释的,外键在捉摸数据入口错误上起着重要的作用,由此可建立更为强健更加集成的数据库。另一方面值得提到的是,执行外键核实是内部资料处理的过程,且不同表之间指定复杂的内部关系可以导致数据库的性能下降。所以,在参照完整性与性能考虑之间找到平衡点相当重要,而使用外键就是能够确保性能与稳健之间的最优结合。

    April 18

    Mysql 的数据库复制功能

    Mysql 数据库相信大家已经投入了生产使用。很多人都将他和 PHP 集成在 Apache 中,为WebSite 服务。的确,他们在WebSite 中的应用比较多,而且PhpMyAdmin 又是一个PHP+Mysql 的最好应用例子。
    那么Mysql 能不能实现两个系统之间通过TCP/IP去复制数据库?能不能实现实时复制呢?也就是说能不能实现同步(Synchronization)的问题。先概括介绍一下Mysql 的Replication Database功能。
    复制(Replication)类似于拷贝数据库到另一台服务器上,但它是通过定义Master 和Slave的关系去实时地保证两个数据库的完全同步。这个功能在Mysql的3.23版中开始出现。
    下面大家一起来测试一下Mysql的Replication 功能。

    作者的平台是:
    Master:Mysql 3.23.53-log on FreeBSD 4.7 Release IP:192.168.10.100
    Slave: Mysql 3.23.56-log on FreeBSD 4.8 Stable IP:192.168.10.200
    1、Master 机器设置权限,赋予Slave Relication 权利,并打包要同步的数据库结构。
    MasterBSD# pwd
    /usr/local/mysql/bin
    MasterBSD#./mysql –u root –p
    Enter password:
    Welcome to the MySQL monitor. Commands end with ; or \g.
    Your MySQL connection id is 2 to server version: 3.23.53-log

    Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

    mysql> GRANT FILE ON *.* TO replication@192.168.10.200 IDENTIFIED BY ‘repplication’;

    (赋予192.168.10.200也就是Slave 机器有File权限)
    然后打包要复制的数据库
    MasterBSD# cd var
    MasterBSD# tar czvf repdatabase.tar.gz repdatabase

    这样,我们的到一个repdatabase数据库的打包文件repdatabase.tar.gz

    2设置主服务器Master的my.cnf,启动Mysql服务
    MasterBSD# vi /etc/my.cnf
    在[mysqld]添加或修改以下的
    [mysqld]
    log-bin
    server-id=1
    sql-bin-update-same
    binlog-do-db= repdatabase
    针对repdatabase 库做replication 功能
    然后把Master主服务器的Mysql重启。
    MasterBSD# /usr/local/mysql/bin/mysqladmin –u root –p shutdown
    MasterBSD# /usr/local/mysql/bin/safe_mysqld --user=mysql &
    3、建立Slave数据库
    刚才我们在Master中打包了repdatabase.tar.gz,它的作用就是要在Slave恢复成一样的数据库。先把Master 的repdatabase.tar.gz文件传到Slave机器中去。然后
    SlaveBSD# tar zxvf repdatabase.tar.gz -C /usr/local/mysql/var/
    4、修改Slave服务器的my.cnf
    SlaveBSD# vi /etc/my.cnf
    在[mysqld]添加或修改以下的
    master-host=192.168.10.100
    master-user=replication
    master-password=replication
    master-port=3306
    server-id=2
    master-connect-retry=60
    replicate-do-db=reldatabase    [要更新的数据库]
    log-slave-updates
    5、重启动Slave的slave start。
    SlaveBSD# /usr/local/mysql/bin/mysqladmin –u root –p shutdown
    SlaveBSD# /usr/local/mysql/bin/safe_mysqld --user=mysql &
    6、测试
    先检测两个Mysql数据库中的repdatabase是否正常。
    正常情况应该是Master和Slave 中的Mysql 都有相同的repdatabase 数据库,并且里面的数据都一样。
    然后我们测试replication 功能是否起用。
    在Master中的repdatabas数据库添加一笔数据:
    MasterBSD# /usr/local/mysql/bin/mysql -uroot -p
    Enter password:
    Welcome to the MySQL monitor. Commands end with ; or \g.
    Your MySQL connection id is 12 to server version: 3.23.53-log

    Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

    mysql> use repdatabase;
    Database changed
    mysql> INSERT INTO `rep_table` ( `name` , `num` , `selectd ` ) VALUES ('test1', '4321', 'Y');
    Query OK, 1 row affected (0.00 sec)
    mysql>

    在Slave的数据库中应该也会同样有这样一条数据
    SlaveBSD# /usr/local/mysql/bin/mysql -uroot -p
    Enter password:
    Welcome to the MySQL monitor. Commands end with ; or \g.
    Your MySQL connection id is 17 to server version: 3.23.56-log
    Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
    mysql> select * from repdatabase.rep_table;
    +--------+-----------+------------+
    | name | num | selectd |
    +--------+-----------+------------+
    | aaa | 44444 | N |
    | ddd | 111112222 | N |
    | insert | 1234 | N |
    | test | 12345 | N |
    | test1 | 4321 | Y |    这一行就是Master插入的时候Slave 同步得回来的数据。
    +--------+-----------+------------+
    5 rows in set (0.01 sec)
    mysql>
    到此,我们的两个数据库replication 功能实验成功。



    7、互为replication
    在Mysql 的文档资料中也指出了,一台Mysql机器同样可以作为Master也可以作为Slave的,也可以互相replication数据。

    8、应用
    replication可以用在那方面呢?我的想法是,一台Mysql生产机器在提供繁忙的SQL查询,比如说是股市的查询,那它仅仅是作为查询而已。那么我们就可以通过两台机器,提供查询的机器为Slave,那么数据录入的机器是Master,通过双网卡去进行,请看下图:
    issue9_mysql_rep.jpg
    这样的好处是显而易见,Slave作为大量SQL查询的服务器,繁忙、任务大!而且对着Internet,破坏可能性也大!因此,引入Mysql的replication我们可以方便、安全地管理数据库!
    April 12

    mysql 5.1的计划任务

    mysql的网站上多了一个关于5.1新特性Event(事件)的介绍 ,咋一看象是windows的计划任务,细一看,呵呵,正是如此.假如你有这样的需求--在今年年底之前,每天给我查查用户表里新增了哪些美女,说不定就可以用上Events特性了,下面是新建立Event的语法

    SQL:
    1. CREATE EVENT
    2. e                               /* 事件名 */
    3. ON SCHEDULE
    4. EVERY 1 WEEK                    /* 执行间隔 */
    5. DO
    6. INSERT INTO t VALUES (0);       /* SQL statement */

    而要开启或关闭这个特性,只要执行下面的sql:

    SQL:
    1. SET GLOBAL event_scheduler = 1; /*0是关闭*/

    不管怎么说,mysql的新版本是越来越强大了,但是在实际的项目中,还没有机会用上它们,很难说性能上是不是会打上折扣
    原文地址:http://dev.mysql.com/tech-resources/articles/event-feature.html

    January 14

    mysql的表只读

    可能是文件系统只读,

    比如,这个表的文件,相对于系统中的mysql用户,只读,所以,无法修改。

    chown mysql.mysql tablename.*
    chmod u+w tablename
    November 11

    linux下mysql用户访问限制问题

    mysql.cnf里面有mysqld这个section中有没有bind-address这一个条目
    或service iptables stop
    看到了,bind-address  是127.0.0.1
    注释当然也可以,重启mysql daemon
    October 31

    什么是数据库事务

    什么是数据库事务   数据库事务是指作为单个逻辑工作单元执行的一系列操作。 设想网上购物的一次交易,其付款过程至少包括以下几步数据库操作:   · 更新客户所购商品的库存信息   · 保存客户付款信息--可能包括与银行系统的交互   · 生成订单并且保存到数据库中   · 更新用户相关信息,例如购物数量等等 正常的情况下,这些操作将顺利进行,最终交易成功,与交易相关的所有数据库信息也成功地更新。但是,如果在这一系列过程中任何一个环节出了差错,例如在更新商品库存信息时发生异常、该顾客银行帐户存款不足等,都将导致交易失败。一旦交易失败,数据库中所有信息都必须保持交易前的状态不变,比如最后一步更新用户信息时失败而导致交易失败,那么必须保证这笔失败的交易不影响数据库的状态--库存信息没有被更新、用户也没有付款,订单也没有生成。否则,数据库的信息将会一片混乱而不可预测。 数据库事务正是用来保证这种情况下交易的平稳性和可预测性的技术。   数据库事务的ACID属性 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性:   · 原子性 事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。通常,与某个事务关联的操作具有共同的目标,并且是相互依赖的。如果系统只执行这些操作的一个子集,则可能会破坏事务的总体目标。原子性消除了系统处理操作子集的可能性。   · 一致性 事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构(如 B 树索引或双向链表)都必须是正确的。某些维护一致性的责任由应用程序开发人员承担,他们必须确保应用程序已强制所有已知的完整性约束。例如,当开发用于转帐的应用程序时,应避免在转帐过程中任意移动小数点。   · 隔离性 由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。这称为可串行性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。当事务可序列化时将获得最高的隔离级别。在此级别上,从一组可并行执行的事务获得的结果与通过连续运行每个事务所获得的结果相同。由于高度隔离会限制可并行执行的事务数,所以一些应用程序降低隔离级别以换取更大的吞吐量。   · 持久性  事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持。 DBMS的责任和我们的任务 企业级的数据库管理系统(DBMS)都有责任提供一种保证事务的物理完整性的机制。就常用的SQL Server2000系统而言,它具备锁定设备隔离事务、记录设备保证事务持久性等机制。因此,我们不必关心数据库事务的物理完整性,而应该关注在什么情况下使用数据库事务、事务对性能的影响,如何使用事务等等。 
    October 26

    ADODB快速手册

    1、简要说明
       ADODB是PHP中的一个通用的数据库操作库,ADODB目前支持MySQL、PostgreSQL、Oracle、Interbase、Microsoft SQL Server、Access、FoxPro、Sybase、ODBC及ADO,你可以从 http://php.weblogs.com/adodb下载 ADODB。

    2、安装
       下载 tgz或 zip解压即可。

    3、使用
       使用前包含进 adodb.inc.php即可
       include("$adodb_path/adodb.inc.php"); // includes the adodb library

    4、函数
       全局函数几乎只有一个
       NewADOConnection('DataBaseType');
       作用:生成一个ADOdb对象。

       可以的值为: (不包括括号里的内容)
       access (Microsoft Access/Jet)
       ado (Generic ADO, the base for all the other ADO drivers)
       ado_access (Microsoft Access/Jet using ADO)
       ado_mssql (Microsoft SQL Server using ADO)
       db2 (DB2)
       vfp (Microsoft Visual FoxPro)
       fbsql (FrontBase)
       ibase (Interbase 6 or before)
       firebird (Firebird)
       informix72 (Informix databases before Informix 7.3)
       informix (Informix)
       maxsql (MySQL with transaction support)
       mssql (Microsoft SQL Server 7)
       mssqlpo (Portable mssql driver)
       mysql (MySQL without transaction support)
       mysqlt (MySQL with transaction support, identical to maxmysql)
       oci8 (Oracle 8/9)
       oci805 (Oracle 8.0.5)
       oci8po (Oracle 8/9 portable driver)
       odbc (Generic ODBC, the base for all the other ODBC drivers)
       odbc_mssql (MSSQL via ODBC)
       odbc_oracle (Oracle via ODBC)
       oracle (Oracle 7)
       postgres (PostgreSQL)
       postgres64 (PostgreSQL 6.4)
       postgres7 (PostgreSQL 7, currently identical to postgres )
       sqlanywhere (Sybase SQL Anywhere)
       sybase (Sybase)


       rs2html 把 RecordSet 对象转成html输出
       要包含 include('tohtml.inc.php');

    5、对象
       PHP ADODB与MS ADO 对象结构相似,
       主要用到两个对象 (ADOConnection 对象与 RecordSet 对象)
       ADOConnection负责数据库的连接,发送 sql 命令等;RecordSet主要是得到查询出来的记录集

    6、ADOConnection 对象说明
       1、建立
         ADODB是用 NewADOConnection() 函数来新建一个 ADODB连接对象,如:
         $db = NewADOConnection('$database_type'); // A new connection
         $database_type 是数据库格式,可能值看上面 第4小节。

       2、连接数据库
          用ADOConnection 的 Connect 方法,格式如下:
          Connect("主机名","用户名","用户密码","数据库名");
          返回值,连接成功返回一个 ADOConnection对象,错误返回 FALSE,可以用 ADOConnection的 ErrorMsg()函数得到错误说明.
          $db->Connect("$host", "$user", "$password", "$database_name");

       3、执行SQL指令
         用Execute函数执行 SQL 命令 如:
         $result = $db->Execute("SELECT * FROM employees");
         if ($result === false) die("failed");
         执行成功返回一个 Recordset 对象,否则返回 FALSE

       4、Affected_Rows()函数
          Affected_Rows()函数返回最后一个 sql 命令所影响到的记录行数。

       5、日期格式
         由于 ADODB需要支持不同的数据库系统,而这些不同的数据库可能用不同的方式来表示日期\时间格式。ADODB用 DBDate()函数来转换不同数据库之间的格式。
      
       6、字符串格式
         不同的数据库可以用不对的字符串表示格式,如'单括号的表示方法。例:
         $ID = 3
         $TheDate=mktime(0,0,0,8,31,2001) /* 31st August 2001 */
         $Note= sugar why don't we call it off
         $sql = "INSERT INTO table (id, thedate,note) values ("
                 . $ID . ','
                 . $db->DBDate($TheDate) .','
                 . $db->qstr($Note).")";
         $db->Execute($sql);

       7、Select指令的Limit及Top支持
          $connection->SelectLimit($sql,$nrows,$offset);
          注意:第二个参数是需要返回的行数,第三个参数才是从第几行开始。
          返回值:成功返回一个Recordset 对象,失败返回 FALSE; 对于分页时相当有用。
      
       8、Cache缓存
         CacheExecute 与 CacheSelectLimit 函数。
         第一个参数为超时时间,秒数。
         ADODB允许你在你的档案系统中暂存recordset的数据,并且在$connection->CacheExecute($secs2cache,$sql)及 $connection->CacheSelectLimit($secs2cache,$sql,$nrows,$offset)等设定的时间间隔到达之后,才真正去做数据库的查询以节省时间。
         可以用 $ADODB_CACHE_DIR 来指定 Cache 目录,好象要用绝对路径。但超时过后,PHP不能自动删除cache,要人工手动删除。

       9、事务
         StartTrans() 开始事务
         CompleteTrans() 结束事务 ADODB 自动处理事务的提交与回滚
         HasFailedTrans() 事务是否成功  TRUE 成功,FALSE为失败
         大多数数据库直持事务,但MySQL InnoDB表支持事务,而MyISAM表不支持。

       10、SetFetchMode
          设置 Recordset 对象的记录集索引方式。如:
          $db->SetFetchMode(ADODB_FETCH_ASSOC); // Return associative array
           ADODB_FETCH_NUM; 以数字方式索引

       12、GetInsertSQL , GetUpdateSQL
           生成一个 Insert SQL , 有两个参数,第一个是一个 RecordSet 第二个是一个数组。 例如:

           $sql = "SELECT * FROM ADOXYZ WHERE id = -1"; # 从资料库中查询出一个空的资料集
           $conn = &ADONewConnection("mysql");  # 建立一个连结
           $conn->debug=1;
           $conn->PConnect("localhost", "admin", "", "test"); # 连结到 MySQL, 资料库名称为 test
          
           $rs = $conn->Execute($sql); # 执行查询,并取得一个空的资料集

           $record = array(); # 初始化一个阵列,以便存放记录资料供新增用

           # 设定记录中的栏位值
           $record["firstname"] = "Bob";
           $record["lastname"] = "Smith";
           $record["created"] = time();

           # 传入空的资料集及栏位资料阵列到GetInsertSQL函数中,以执行功能
           # 这个函数将会依传入的资料,回传一个全格式的 INSERT SQL指令

           $insertSQL = $conn->GetInsertSQL($rs, $record);

           $conn->Execute($insertSQL); # 将记录挿入资料库中

           #==========================
     # 以下的程式码测试更新状态

     $sql = "SELECT * FROM ADOXYZ WHERE id = 1";
     # 选择一笔记录以便更新

     $rs = $conn->Execute($sql); # 执行这个查询,并取得一个存在的记录来更新

     $record = array(); # 初始化一个阵列,以存放要更新的资料

     # 设定栏位里的值
     $record["firstname"] = "Caroline";
     $record["lastname"] = "Smith"; # 更新 Caroline的姓由 Miranda 变成 Smith

     # 传入这个只有单一记录的资料集以及含有资料的阵列到 GetUpdateSQL函数里
     # 函数将会回传一个具有正确 WHERE 条件的 UPDATE(更新) SQL 指令
     $updateSQL = $conn->GetUpdateSQL($rs, $record);

     $conn->Execute($updateSQL); # 更新资料库中的记录
     $conn->Close();

         13、PageExecute  分页查询
             如:PageExecute($sql, $num_of_rows_per_page, $curr_page);
      可能用到的属性 AtFirstPage() , AtLastPage(),AbsolutePage()

     

    7、Recordset 对象
       1、Move($Pos)
         卷动目前的数据列,ADODB支持整个数据库往前卷动,有一些数据库并不支持往后的卷动,这倒不会是个问题,因为你能够用暂存纪录到快取来仿真往后卷动。

      2、RecordCount() 或 RowCount()
         传回SQL指令存取到的纪录笔数,有些数据库会因为不支持而传回-1

      3、EOF
         是否为记录尾

      4、fields
         用以获取记录集的值,用法,$rs->fields[编号或字段名],如果$db->SetFetchMode(ADODB_FETCH_ASSOC); 则可以用字段名直接访问。
     
      5、MoveNext()
         记录指针下移一个。

      6、MoveFirst
         记录指针移动在开头。

      7、FetchNextObject()
         得到一个行对象,且指针自动下移。如:
         $row = $rs->FetchNextObject();
         echo($row->UserName . $row->Age );

      8、Insert_ID
         得到记录集最后一次插入的值。

      9、MetaTypes
         得到字段的类型。如:
         $fld = $recordSet->FetchField(1);
         $type = $recordSet->MetaType($fld->type);
         if ( $type == 'D' || $type == 'T') {.....};
         可能的值与意义:
         C:  character 栏位,应该使用 <input type="text"> 标记来取值。
         X: 文字栏位(Text) , 长文字栏位,使用 <textarea> 标记来显示资料。
         B: Blob 栏位或者大型的二位元物件(像程式,图档等)。
         D: 日期栏位
         T: 时间栏位
         L: 逻辑栏位(真假值)或位元栏位
         N: 数字栏位,包含自动进位、编号、整数、浮点数、实数等。
         R: 序列栏位,包含了序列、自动增进整数,只对被选择的资料库作用。

      10、FetchField
        得到一个字段对象 如:
        $field = $rs->FetchField();
        field对象有三个属性 name 、type 、length 分别的意义为(名称、类型、长度,长度可能传回-1)
        而 type 可以通过 MetaTypes 转换成字符格式,如:
        $fld = $recordSet->FetchField(1);
        $type = $recordSet->MetaType($fld->type);
        if ( $type == 'D' || $type == 'T') {.....};

      11 UserDate

    October 14

    MySQL乱码终极解决方案

    从MySQL 4.1开始引入的多语言支持确实很棒,而且一些特性已经超过了其他的数据库系统。不过在测试过程中发现使用适用于MySQL 4.1之前的PHP语句操作MySQL数据库会造成乱码,即使是设置过了表字符集也是如此。
    MySQL 4.1的字符集支持(Character Set Support)有两个方面:字符集(Character set)和排序方式(Collation)。对于字符集的支持细化到四个层次: 服务器(server),数据库(database),数据表(table)和连接(connection)。

    当我们按照原来的方式通过PHP存取MySQL数据库时,就算设置了表的默认字符集为utf8并且通过UTF-8编码发送查询,你会发现存入数据库的仍然是乱码。
    其实简单的方法是通过phpMyAdmin来设置。
    设置下面几项:
    1: 语言设置为 chinese (zh-utf-8)
    2: MySQL 字符集: UTF-8 Unicode (utf8)
    3: MySQL 连接校对:  utf8_general_ci
    4: 新增数据库和数据表的时候,整理项选择 utf8_general_ci
    通过以上设置,在phpMyAdmin中操作和查询的时候,中文字符都不会乱码了。
    但是你会发现,在php程序中用以前的sql语句查询出来的结果还是乱码,问题就出在connection连接层上。
    解决方法是在成功连接数据库之后,发送一句查询语句:
    1: $this->LinkID = @mysql_connect($this->Host, $this->User, $this->Password);
    2: @mysql_query('SET NAMES 'utf8'', $this->LinkID);

    October 12

    一个Mysql自动备份脚本

    可以将这个脚本放进crontab,每天凌晨执行一次,自动备份 

    这个脚本每天最多只执行一次,而且只保留最近五天的备份在服务器上。

    [code:1:893e3c81e2]

    #!/bin/bash 
    #This is a ShellScript For Auto DB Backup 
    #Powered by aspbiz 
    #2004-09 

    #Setting 
    #设置数据库名,数据库登录名,密码,备份路径,日志路径,数据文件位置,以及备份方式 
    #默认情况下备份方式是tar,还可以是mysqldump,mysqldotcopy 
    #默认情况下,用root(空)登录mysql数据库,备份至/root/dbxxxxx.tgz 
    DBName=mysql 
    DBUser=root 
    DBPasswd= 
    BackupPath=/root/ 
    LogFile=/root/db.log 
    DBPath=/var/lib/mysql/ 
    #BackupMethod=mysqldump 
    #BackupMethod=mysqlhotcopy 
    #BackupMethod=tar 
    #Setting End 


    NewFile="$BackupPath"db$(date +%y%m%d).tgz 
    DumpFile="$BackupPath"db$(date +%y%m%d) 
    OldFile="$BackupPath"db$(date +%y%m%d --date='5 days ago').tgz 

    echo "-------------------------------------------" >> $LogFile 
    echo $(date +"%y-%m-%d %H:%M:%S") >> $LogFile 
    echo "--------------------------" >> $LogFile 
    #Delete Old File 
    if [ -f $OldFile ] 
    then 
       rm -f $OldFile >> $LogFile 2>&1 
       echo "[$OldFile]Delete Old File Success!" >> $LogFile 
    else 
       echo "[$OldFile]No Old Backup File!" >> $LogFile 
    fi 

    if [ -f $NewFile ] 
    then 
       echo "[$NewFile]The Backup File is exists,Can't Backup!" >> $LogFile 
    else 
       case $BackupMethod in 
       mysqldump) 
          if [ -z $DBPasswd ] 
          then 
             mysqldump -u $DBUser --opt $DBName > $DumpFile 
          else 
             mysqldump -u $DBUser -p$DBPasswd --opt $DBName > $DumpFile 
          fi 
          tar czvf $NewFile $DumpFile >> $LogFile 2>&1 
          echo "[$NewFile]Backup Success!" >> $LogFile 
          rm -rf $DumpFile 
          ;; 
       mysqlhotcopy) 
          rm -rf $DumpFile 
          mkdir $DumpFile 
          if [ -z $DBPasswd ] 
          then 
             mysqlhotcopy -u $DBUser $DBName $DumpFile >> $LogFile 2>&1 
          else 
             mysqlhotcopy -u $DBUser -p $DBPasswd $DBName $DumpFile >>$LogFile 2>&1 
          fi 
          tar czvf $NewFile $DumpFile >> $LogFile 2>&1 
          echo "[$NewFile]Backup Success!" >> $LogFile 
          rm -rf $DumpFile 
          ;; 
       *) 
          /etc/init.d/mysqld stop >/dev/null 2>&1 
          tar czvf $NewFile $DBPath$DBName >> $LogFile 2>&1 
          /etc/init.d/mysqld start >/dev/null 2>&1 
          echo "[$NewFile]Backup Success!" >> $LogFile 
          ;; 
       esac 
    fi 

    echo "-------------------------------------------" >> $LogFile 


    [/code:1:893e3c81e2]

    MYSQL出错代码列表

    1005:创建表失败
    1006:创建数据库失败
    1007:数据库已存在,创建数据库失败
    1008:数据库不存在,删除数据库失败
    1009:不能删除数据库文件导致删除数据库失败
    1010:不能删除数据目录导致删除数据库失败
    1011:删除数据库文件失败
    1012:不能读取系统表中的记录
    1020:记录已被其他用户修改
    1021:硬盘剩余空间不足,请加大硬盘可用空间
    1022:关键字重复,更改记录失败
    1023:关闭时发生错误
    1024:读文件错误
    1025:更改名字时发生错误
    1026:写文件错误
    1032:记录不存在
    1036:数据表是只读的,不能对它进行修改
    1037:系统内存不足,请重启数据库或重启服务器
    1038:用于排序的内存不足,请增大排序缓冲区
    1040:已到达数据库的最大连接数,请加大数据库可用连接数
    1041:系统内存不足
    1042:无效的主机名
    1043:无效连接
    1044:当前用户没有访问数据库的权限
    1045:不能连接数据库,用户名或密码错误
    1048:字段不能为空
    1049:数据库不存在
    1050:数据表已存在
    1051:数据表不存在
    1054:字段不存在
    1065:无效的SQL语句,SQL语句为空
    1081:不能建立Socket连接
    1114:数据表已满,不能容纳任何记录
    1116:打开的数据表太多
    1129:数据库出现异常,请重启数据库
    1130:连接数据库失败,没有连接数据库的权限
    1133:数据库用户不存在
    1141:当前用户无权访问数据库
    1142:当前用户无权访问数据表
    1143:当前用户无权访问数据表中的字段
    1146:数据表不存在
    1147:未定义用户对数据表的访问权限
    1149:SQL语句语法错误
    1158:网络错误,出现读错误,请检查网络连接状况
    1159:网络错误,读超时,请检查网络连接状况
    1160:网络错误,出现写错误,请检查网络连接状况
    1161:网络错误,写超时,请检查网络连接状况
    1062:字段值重复,入库失败
    1169:字段值重复,更新记录失败
    1177:打开数据表失败
    1180:提交事务失败
    1181:回滚事务失败
    1203:当前用户和数据库建立的连接已到达数据库的最大连接数,请增大可用的数据库连接数或重启数据库
    1205:加锁超时
    1211:当前用户没有创建用户的权限
    1216:外键约束检查失败,更新子表记录失败
    1217:外键约束检查失败,删除或修改主表记录失败
    1226:当前用户使用的资源已超过所允许的资源,请重启数据库或重启服务器
    1227:权限不足,您无权进行此操作
    1235:MySQL版本过低,不具有本功能