mysqld程序--目录和文件
引用
basedir = path  使用给定目录作为根目录(安装目录)。

character-sets-dir = path  给出存放着字符集的目录。

datadir = path  从给定目录读取数据库文件。

pid-file = filename  为mysqld程序指定一个存放进程ID的文件(仅适用于UNIX/Linux系统); Init-V脚本需要使用这个文件里的进程ID结束mysqld进程。

socket = filename  为MySQL客户程序与服务器之间的本地通信指定一个套接字文件(仅适用于UNIX/Linux系统; 默认设置一般是/var/lib/mysql/mysql.sock文件)。在Windows环境下,如果MySQL客户与服务器是通过命名管道进行通信的,--sock选项给出的将是该命名管道的名字(默认设置是MySQL)。

lower_case_table_name = 1/0  新目录和数据表的名字是否只允许使用小写字母; 这个选项在Windows环境下的默认设置是1(只允许使用小写字母)。


mysqld程序--语言设置
引用
character-sets-server = name  新数据库或数据表的默认字符集。为了与MySQL的早期版本保持兼容,这个字符集也可以用--default-character-set选项给出; 但这个选项已经显得有点过时了。

collation-server = name  新数据库或数据表的默认排序方式。

lanuage = name  用指定的语言显示出错信息。


mysqld程序--通信、网络、信息安全
引用
enable-named-pipes  允许Windows 2000/XP环境下的客户和服务器使用命名管道(named pipe)进行通信。这个命名管道的默认名字是MySQL,但可以用--socket选项来改变。

local-infile [=0]  允许/禁止使用LOAD DATA LOCAL语句来处理本地文件。

myisam-recover [=opt1, opt2, ...]  在启动时自动修复所有受损的MyISAM数据表。这个选项的可取值有4种:DEFAULT、BACKUP、QUICK和FORCE; 它们与myisamchk程序的同名选项作用相同。

old-passwords  使用MySQL 3.23和4.0版本中的老算法来加密mysql数据库里的密码(默认使用MySQL 4.1版本开始引入的新加密算法)。

port = n  为MySQL程序指定一个TCP/IP通信端口(通常是3306端口)。

safe-user-create  只有在mysql.user数据库表上拥有INSERT权限的用户才能使用GRANT命令; 这是一种双保险机制(此用户还必须具备GRANT权限才能执行GRANT命令)。

shared-memory  允许使用内存(shared memory)进行通信(仅适用于Windows)。

shared-memory-base-name = name  给共享内存块起一个名字(默认的名字是MySQL)。

skip-grant-tables  不使用mysql数据库里的信息来进行访问控制(警告:这将允许用户任何用户去修改任何数据库)。

skip-host-cache  不使用高速缓存区来存放主机名和IP地址的对应关系。

skip-name-resovle  不把IP地址解析为主机名; 与访问控制(mysql.user数据表)有关的检查全部通过IP地址行进。

skip-networking  只允许通过一个套接字文件(Unix/Linux系统)或通过命名管道(Windows系统)进行本地连接,不允许ICP/IP连接; 这提高了安全性,但阻断了来自网络的外部连接和所有的Java客户程序(Java客户即使在本地连接里也使用TCP/IP)。

user = name  mysqld程序在启动后将在给定UNIX/Linux账户下执行; mysqld必须从root账户启动才能在启动后切换到另一个账户下执行; mysqld_safe脚本将默认使用--user=mysql选项来启动mysqld程序。


mysqld程序--内存管理、优化、查询缓存区
引用
bulk_insert_buffer_size = n  为一次插入多条新记录的INSERT命令分配的缓存区长度(默认设置是8M)。

key_buffer_size = n  用来存放索引区块的RMA值(默认设置是8M)。

join_buffer_size = n  在参加JOIN操作的数据列没有索引时为JOIN操作分配的缓存区长度(默认设置是128K)。

max_heap_table_size = n  HEAP数据表的最大长度(默认设置是16M); 超过这个长度的HEAP数据表将被存入一个临时文件而不是驻留在内存里。

max_connections = n  MySQL服务器同时处理的数据库连接的最大数量(默认设置是100)。

query_cache_limit = n  允许临时存放在查询缓存区里的查询结果的最大长度(默认设置是1M)。

query_cache_size = n  查询缓存区的最大长度(默认设置是0,不开辟查询缓存区)。

query_cache_type = 0/1/2  查询缓存区的工作模式:0, 禁用查询缓存区; 1,启用查询缓存区(默认设置); 2,"按需分配"模式,只响应SELECT SQL_CACHE命令。

read_buffer_size = n  为从数据表顺序读取数据的读操作保留的缓存区的长度(默认设置是128KB); 这个选项的设置值在必要时可以用SQL命令SET SESSION read_buffer_size = n命令加以改变。

read_rnd_buffer_size = n  类似于read_buffer_size选项,但针对的是按某种特定顺序(比如使用了ORDER BY子句的查询)输出的查询结果(默认设置是256K)。

sore_buffer = n  为排序操作分配的缓存区的长度(默认设置是2M); 如果这个缓存区太小,则必须创建一个临时文件来进行排序。

table_cache = n  同时打开的数据表的数量(默认设置是64)。

tmp_table_size = n  临时HEAP数据表的最大长度(默认设置是32M); 超过这个长度的临时数据表将被转换为MyISAM数据表并存入一个临时文件。


mysqld程序--日志
引用
log [= file]  把所有的连接以及所有的SQL命令记入日志(通用查询日志); 如果没有给出file参数,MySQL将在数据库目录里创建一个hostname.log文件作为这种日志文件(hostname是服务器的主机名)。

log-slow-queries [= file]  把执行用时超过long_query_time变量值的查询命令记入日志(慢查询日志); 如果没有给出file参数,MySQL将在数据库目录里创建一个hostname-slow.log文件作为这种日志文件(hostname是服务器主机名)。

long_query_time = n  慢查询的执行用时上限(默认设置是10s)。

long_queries_not_using_indexs  把慢查询以及执行时没有使用索引的查询命令全都记入日志(其余同--log-slow-queries选项)。

log-bin [= filename]  把对数据进行修改的所有SQL命令(也就是INSERT、UPDATE和DELETE命令)以二进制格式记入日志(二进制变更日志,binary update log)。这种日志的文件名是filename.n或默认的hostname.n,其中n是一个6位数字的整数(日志文件按顺序编号)。

log-bin-index = filename  二进制日志功能的索引文件名。在默认情况下,这个索引文件与二进制日志文件的名字相同,但后缀名是.index而不是.nnnnnn。

max_binlog_size = n  二进制日志文件的最大长度(默认设置是1GB)。在前一个二进制日志文件里的信息量超过这个最大长度之前,MySQL服务器会自动提供一个新的二进制日志文件接续上。

binlog-do-db = dbname  只把给定数据库里的变化情况记入二进制日志文件,其他数据库里的变化情况不记载。如果需要记载多个数据库里的变化情况,就必须在配置文件使用多个本选项来设置,每个数据库一行。

binlog-ignore-db = dbname  不把给定数据库里的变化情况记入二进制日志文件。

sync_binlog = n  每经过n次日志写操作就把日志文件写入硬盘一次(对日志信息进行一次同步)。n=1是最安全的做法,但效率最低。默认设置是n=0,意思是由操作系统来负责二进制日志文件的同步工作。

log-update [= file]  记载出错情况的日志文件名(出错日志)。这种日志功能无法禁用。如果没有给出file参数,MySQL会使用hostname.err作为种日志文件的名字。


mysqld程序--镜像(主控镜像服务器)
引用
server-id = n  给服务器分配一个独一无二的ID编号; n的取值范围是1~2的32次方启用二进制日志功能。

log-bin = name  启用二进制日志功能。这种日志的文件名是filename.n或默认的hostname.n,其中的n是一个6位数字的整数(日志文件顺序编号)。

binlog-do/ignore-db = dbname  只把给定数据库里的变化情况记入二进制日志文件/不把给定的数据库里的变化记入二进制日志文件。


mysqld程序--镜像(从属镜像服务器)
引用
server-id = n  给服务器分配一个唯一的ID编号

log-slave-updates  启用从属服务器上的日志功能,使这台计算机可以用来构成一个镜像链(A->B->C)。

master-host = hostname  主控服务器的主机名或IP地址。如果从属服务器上存在mater.info文件(镜像关系定义文件),它将忽略此选项。

master-user = replicusername  从属服务器用来连接主控服务器的用户名。如果从属服务器上存在mater.info文件,它将忽略此选项。

master-password = passwd  从属服务器用来连接主控服务器的密码。如果从属服务器上存在mater.info文件,它将忽略此选项。

master-port = n  从属服务器用来连接主控服务器的TCP/IP端口(默认设置是3306端口)。

master-connect-retry = n  如果与主控服务器的连接没有成功,则等待n秒(s)后再进行管理方式(默认设置是60s)。如果从属服务器存在mater.info文件,它将忽略此选项。

master-ssl-xxx = xxx  对主、从服务器之间的SSL通信进行配置。

read-only = 0/1  0: 允许从属服务器独立地执行SQL命令(默认设置); 1: 从属服务器只能执行来自主控服务器的SQL命令。

read-log-purge = 0/1  1: 把处理完的SQL命令立刻从中继日志文件里删除(默认设置); 0: 不把处理完的SQL命令立刻从中继日志文件里删除。

replicate-do-table = dbname.tablename  与--replicate-do-table选项的含义和用法相同,但数据库和数据库表名字里允许出现通配符"%" (例如: test%.%--对名字以"test"开头的所有数据库里的所以数据库表进行镜像处理)。

replicate-do-db = name  只对这个数据库进行镜像处理。

replicate-ignore-table = dbname.tablename  不对这个数据表进行镜像处理。

replicate-wild-ignore-table = dbn.tablen  不对这些数据表进行镜像处理。

replicate-ignore-db = dbname  不对这个数据库进行镜像处理。

replicate-rewrite-db = db1name > db2name  把主控数据库上的db1name数据库镜像处理为从属服务器上的db2name数据库。

report-host = hostname  从属服务器的主机名; 这项信息只与SHOW SLAVE HOSTS命令有关--主控服务器可以用这条命令生成一份从属服务器的名单。

slave-compressed-protocol = 1  主、从服务器使用压缩格式进行通信--如果它们都支持这么做的话。

slave-skip-errors = n1, n2, ...或all  即使发生出错代码为n1、n2等的错误,镜像处理工作也继续进行(即不管发生什么错误,镜像处理工作也继续进行)。如果配置得当,从属服务器不应该在执行SQL命令时发生错误(在主控服务器上执行出错的SQL命令不会被发送到从属服务器上做镜像处理); 如果不使用slave-skip-errors选项,从属服务器上的镜像工作就可能因为发生错误而中断,中断后需要有人工参与才能继续进行。


mysqld--InnoDB--基本设置、表空间文件
引用
skip-innodb  不加载InnoDB数据表驱动程序--如果用不着InnoDB数据表,可以用这个选项节省一些内存。

innodb-file-per-table  为每一个新数据表创建一个表空间文件而不是把数据表都集中保存在中央表空间里(后者是默认设置)。该选项始见于MySQL 4.1。

innodb-open-file = n  InnoDB数据表驱动程序最多可以同时打开的文件数(默认设置是300)。如果使用了

innodb-file-per-table选项并且需要同时打开很多数据表的话,这个数字很可能需要加大。

innodb_data_home_dir = p  InnoDB主目录,所有与InnoDB数据表有关的目录或文件路径都相对于这个路径。在默认的情况下,这个主目录就是MySQL的数据目录。

innodb_data_file_path = ts  用来容纳InnoDB为数据表的表空间: 可能涉及一个以上的文件; 每一个表空间文件的最大长度都必须以字节(B)、兆字节(MB)或千兆字节(GB)为单位给出; 表空间文件的名字必须以分号隔开; 最后一个表空间文件还可以带一个autoextend属性和一个最大长度(max:n)。例如,ibdata1:1G; ibdata2:1G:autoextend:max:2G的意思是: 表空间文件ibdata1的最大长度是1GB,ibdata2的最大长度也是1G,但允许它扩充到2GB。除文件名外,还可以用硬盘分区的设置名来定义表空间,此时必须给表空间的最大初始长度值加上newraw关键字做后缀,给表空间的最大扩充长度值加上raw关键字做后缀(例如/dev/hdb1:20Gnewraw或/dev/hdb1:20Graw); MySQL 4.0及更高版本的默认设置是ibdata1:10M:autoextend。

innodb_autoextend_increment = n  带有autoextend属性的表空间文件每次加大多少兆字节(默认设置是8MB)。这个属性不涉及具体的数据表文件,那些文件的增大速度相对是比较小的。

innodb_lock_wait_timeout = n  如果某个事务在等待n秒(s)后还没有获得所需要的资源,就使用ROLLBACK命令放弃这个事务。这项设置对于发现和处理未能被InnoDB数据表驱动程序识别出来的死锁条件有着重要的意义。这个选项的默认设置是50s。

innodb_fast_shutdown 0/1  是否以最快的速度关闭InnoDB,默认设置是1,意思是不把缓存在INSERT缓存区的数据写入数据表,那些数据将在MySQL服务器下次启动时再写入(这么做没有什么风险,因为INSERT缓存区是表空间的一个组成部分,数据不会丢失)。把这个选项设置为0反面危险,因为在计算机关闭时,InnoDB驱动程序很可能没有足够的时间完成它的数据同步工作,操作系统也许会在它完成数据同步工作之前强行结束InnoDB,而这会导致数据不完整。


mysqld程序--InnoDB--日志
引用
innodb_log_group_home_dir = p  用来存放InnoDB日志文件的目录路径(如ib_logfile0、ib_logfile1等)。在默认的情况下,InnoDB驱动程序将使用MySQL数据目录作为自己保存日志文件的位置。  

innodb_log_files_in_group = n  使用多少个日志文件(默认设置是2)。InnoDB数据表驱动程序将以轮转方式依次填写这些文件; 当所有的日志文件都写满以后,之后的日志信息将写入第一个日志文件的最大长度(默认设置是5MB)。这个长度必须以MB(兆字节)或GB(千兆字节)为单位进行设置。

innodb_flush_log_at_trx_commit = 0/1/2  这个选项决定着什么时候把日志信息写入日志文件以及什么时候把这些文件物理地写(术语称为"同步")到硬盘上。设置值0的意思是每隔一秒写一次日志并进行同步,这可以减少硬盘写操作次数,但可能造成数据丢失; 设置值1(设置设置)的意思是在每执行完一条COMMIT命令就写一次日志并进行同步,这可以防止数据丢失,但硬盘写操作可能会很频繁; 设置值2是一般折衷的办法,即每执行完一条COMMIT命令写一次日志,每隔一秒进行一次同步。

innodb_flush_method = x  InnoDB日志文件的同步办法(仅适用于UNIX/Linux系统)。这个选项的可取值有两种: fdatasync,用fsync()函数进行同步; O_DSYNC,用O_SYNC()函数进行同步。

innodb_log_archive = 1  启用InnoDB驱动程序的archive(档案)日志功能,把日志信息写入ib_arch_log_n文件。启用这种日志功能在InnoDB与MySQL一起使用时没有多大意义(启用MySQL服务器的二进制日志功能就足够用了)。


mysqld程序--InnoDB--缓存区的设置和优化
引用
innodb_log_buffer_pool_size = n  为InnoDB数据表及其索引而保留的RAM内存量(默认设置是8MB)。这个参数对速度有着相当大的影响,如果计算机上只运行有MySQL/InnoDB数据库服务器,就应该把全部内存的80%用于这个用途。

innodb_log_buffer_size = n  事务日志文件写操作缓存区的最大长度(默认设置是1MB)。

innodb_additional_men_pool_size = n  为用于内部管理的各种数据结构分配的缓存区最大长度(默认设置是1MB)。

innodb_file_io_threads = n  I/O操作(硬盘写操作)的最大线程个数(默认设置是4)。

innodb_thread_concurrency = n  InnoDB驱动程序能够同时使用的最大线程个数(默认设置是8)。


mysqld程序--其它选项
引用
bind-address = ipaddr  MySQL服务器的IP地址。如果MySQL服务器所在的计算机有多个IP地址,这个选项将非常重要。

default-storage-engine = type  新数据表的默认数据表类型(默认设置是MyISAM)。这项设置还可以通过--default-table-type选项来设置。

default-timezone = name  为MySQL服务器设置一个地理时区(如果它与本地计算机的地理时区不一样)。

ft_min_word_len = n  全文索引的最小单词长度工。这个选项的默认设置是4,意思是在创建全文索引时不考虑那些由3个或更少的字符构建单词。

Max-allowed-packet = n  客户与服务器之间交换的数据包的最大长度,这个数字至少应该大于客户程序将要处理的最大BLOB块的长度。这个选项的默认设置是1MB。

Sql-mode = model1, mode2, ...  MySQL将运行在哪一种SQL模式下。这个选项的作用是让MySQL与其他的数据库系统保持最大程度的兼容。这个选项的可取值包括ansi、db2、oracle、no_zero_date、pipes_as_concat。


注意:如果在配置文件里给出的某个选项是mysqld无法识别的,MySQL服务器将不启动。
以下MySQL提升性能优化的服务器系统参数是比较关键的:

(1)、back_log:

引用
要求 MySQL 能有的连接数量。当主要MySQL线程在一个很短时间内得到非常多的连接请求,这就起作用,然后主线程花些时间(尽管很短)检查连接并且启动一个新线程。

back_log值指出在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。只有如果期望在一个短时间内有很多连接,你需要增加它,换句话说,这值对到来的TCP/IP连接的侦听队列的大小。你的操作系统在这个队列大小上有它自己的限制。 试图设定back_log高于你的操作系统的限制将是无效的。

当你观察你的主机进程列表,发现大量 264084 | unauthenticated user | xxx.xxx.xxx.xxx | NULL | Connect | NULL | login | NULL 的待连接进程时,就要加大 back_log 的值了。默认数值是50,我把它改为500。


(2)、interactive_timeout:

引用
服务器在关闭它前在一个交互连接上等待行动的秒数。一个交互的客户被定义为对 mysql_real_connect()使用 CLIENT_INTERACTIVE 选项的客户。 默认数值是28800,我把它改为7200。


(3)、key_buffer_size:

引用
索引块是缓冲的并且被所有的线程共享。key_buffer_size是用于索引块的缓冲区大小,增加它可得到更好处理的索引(对所有读和多重写),到你能负担得起那样多。如果你使它太大,系统将开始换页并且真的变慢了。默认数值是8388600(8M),我的MySQL主机有2GB内存,所以我把它改为402649088(400MB)。


(4)、max_connections:

引用
允许的同时客户的数量。增加该值增加 mysqld 要求的文件描述符的数量。这个数字应该增加,否则,你将经常看到 Too many connections 错误。 默认数值是100,我把它改为1024 。


(5)、record_buffer:

引用
每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,你可能想要增加该值。默认数值是131072(128K),我把它改为16773120 (16M)


(6)、sort_buffer:

引用
每个需要进行排序的线程分配该大小的一个缓冲区。增加这值加速ORDER BY或GROUP BY操作。默认数值是2097144(2M),我把它改为 16777208 (16M)。


(7)、table_cache:

引用
为所有线程打开表的数量。增加该值能增加mysql要求的文件描述符的数量。MySQL对每个唯一打开的表需要2个文件描述符。默认数值是64,我把它改为512。


(8)、thread_cache_size:

引用
可以复用的保存在中的线程的数量。如果有,新的线程从缓存中取得,当断开连接的时候如果有空间,客户的线置在缓存中。如果有很多新的线程,为了提高性能可以这个变量值。通过比较 Connections 和 Threads_created 状态的变量,可以看到这个变量的作用。我把它设置为 80。


(10)、wait_timeout:

引用
服务器在关闭它之前在一个连接上等待行动的秒数。 默认数值是28800,我把它改为7200。


注:参数的调整可以通过修改 /etc/my.cnf 文件并重启 MySQL 实现。这是一个比较谨慎的工作,上面的结果也仅仅是我的一些看法,你可以根据你自己主机的硬件情况(特别是内存大小)进一步修改。

数据库结构
引用
字段类型的定义时遵循以下规则:
选用字段长度最小
优先使用定长型
尽可能的定义 "NOT NULL"
数值型字段中避免使用 "ZEROFILL"
如果要储存的数据为字符串, 且可能值已知且有限, 优先使用 enum 或 set


索引的优化
引用
被索引的字段的长度越小, 该索引的效率越高
被索引的字段中, 值的重复越少, 该索引的效率越高
查询语句中, 如果使用了 "group" 子句, 根据其中字段出现的先后顺序建立多字段索引
查询语句中, 如果使用了 "distinct", 根据其中字段出现的先后顺序建立多字段索引
"where" 子句中, 出现对同一表中多个不同字段的 "and" 条件时, 按照字段出现的先后顺序建立多字段索引
"where" 子句中, 出现对同一表中多个不同字段的 "or" 条件时, 对重复值最少的字段建立单字段索引
进行 "内/外连接" 查询时, 对 "连接字段" 建立索引
对 "主键" 的 "unique" 索引 毫无意义, 不要使用
被索引字段尽可能的使用 "NOT NULL" 属性
对写入密集型表, 尽量减少索引, 尤其是 "多字段索引" 和 "unique" 索引


查询语句的优化
引用
多多利用 "explain" 查询索引使用情况, 以便找出最佳的查询语句写法和索引设置方案
慎用 "select *", 查询时只选出必须字段
查询使用索引时, 所遍历的索引条数越少, 索引字段长度越小, 查询效率越高 (可使用 "explain" 查询索引使用情况)
避免使用 mysql 函数对查询结果进行处理, 将这些处理交给客户端程序负责
使用 "limit" 时候, 尽量使 "limit" 出的部分位于整个结果集的前部, 这样的查询速度更快, 系统资源开销更低
在 "where" 子句中使用多个字段的 "and" 条件时, 各个字段出现的先后顺序要与多字段索引中的顺序相符
在 "where" 子句 中使用 "like" 时, 只有当通配符不出现在条件的最左端时才会使用索引
在 mysql 4.1 以上版本中, 避免使用子查询, 尽量使用 "内/外连接" 实现此功能
减少函数的使用, 如果可能的话, 尽量用单纯的表达式来代替
避免在 "where" 子句中, 对不同字段进行 "or" 条件查询, 将其拆分成多个单一字段的查询语句效率更高
一、什么是索引?

  索引用来快速地寻找那些具有特定值的记录,所有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)命令创建全文索引要比把记录插入带有全文索引的空表更快。
  三、单列索引与多列索引

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

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索引:

tabletypepossible_keyskeykey_lenrefrowsExtra
peoplereffname_lname_agefname_lname_age102const,const,const1Where 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是3.23版,部分查询不能在3.22版MySQL上执行。
MySQL对于成为一个非常快速的数据库服务器有着当之无愧的名声,它也非常容易设置和使用。随着它作为网站后端数据库得声望日增,其效果在去年开始有明显提高。但是很多MySQL用户更多地知道如何创建一个数据库并编写对它的查询。就像成千上万的人通过载闲暇时用Linux做实验来学习Unix那样,很多人通过玩MySQL学习关系数据库。这些MySQL新手的大多数既没有关系数据库理论的背景,又没有时间阅读MySQL手册全文。

因此,我们决定研究某些方法,你可以用针对优化性能来调节MySQL。在读完本文后,你将理解一些帮助你设计你的MySQL数据库和查询的技术,值得你的应用很有效率。我们将假定你熟悉MySQL和SQL基础,但不假定你有这两方面的广博知识。

只存储你需要的信息

这听上去是常识,但人们常常采取“厨房下水道”的方式进行数据库设计。他们认为可能项要得每样东西都要存储并设计数据库保存所有者这些数据。你需要对你的需求现实些,并确定取确实需要什么信息。你常常能随意产生一些数据而不把它存在数据库表中。在这种情况下,从一个应用开发者的角度看也有道理这样做。

例如,在线目录的产品表可能包含各种产品的名称、介绍、尺寸、重量和价格。除了价格,你可能想存储每个项目相关的税和运输成本。但实际上不必这样做。首先税和运输成本可以方便地(由你的应用或MySQL)计算出来。其次,如果税和运输成本改变了,你可能必须编写必要的查询更新每个产品记录中的税和运输的费率。

有时人们认为这太难不能在以后往数据库表中加入字段,所以他们感觉不得不定义尽可能多的列。这是明显的概念错误。在MySQL中,你可以用ALTER TABLE命令方便地修改表定义以适应你改变的需求。

例如,如果你突然认识到你需要给你的产品表增加一个级别列(可能你想允许用户在你的目录中给产品评级),你可以这样做:

ALTER TABLE products ADD rank INTEGER  

这给你的产品表增加了一个整数类型的级别列,你能用ALTER TABLE做什么的完整介绍参见MySQL手册。

只要求你需要的东西--要清晰

就像说“只存储你需要的东西”那样,这可能看来是常识,但这一点常常被忽视,为什么呢?因为在一个应用开发时,需求经常改变,所以很多查询最终看来是这样:

SELECT * FROM sometable  

当你不能肯定你将需要哪一列时,要求所有列明显是最省力的事情,然而随着你的表不断增大和修改,这可能变成一个性能问题。最好是在你的最初开发完成后再花些时间并确定你真正从你的查询中需要什么:

SELECT name, rank, description FROM products
 

这带来了一个相关的观点,即代码维护比性能更重要。大多数变成语言(Perl、Python、PHP、Java等)允许通过字段名和数字编号访问一条查询的结果,这意味着你可以访问命名字段或字段0都可以得到相同的数据。

长期看,最好使用列名而不是其编号位置,为什么?因为一个表中或一条查询中地列的相对位置可以改变。它们在表中可能因为重复使用ALTER TABLE而改变,它们在查询中将因重写了查询而忘记更新应用逻辑来匹配而改变。

当然,你仍然需要小心改变列名!但如果你使用列名而非标号位置,如列名改变,你可以用grep搜索源代码或使用编辑器的搜索能力查找你需要修改的代码。

规范化你的表结构

如果你以前从未听说过“数据规范化”,不要害怕。规范化可能是一个复杂的专题,你可以从只理解最基本的规范化概念中正真正获益。

理解它的最容易的方法是认为你的表是一个电子报表。如果你想以一个报表跟踪你的CD收藏,你可以如图1种那样进行设计:

图1
引用

album                      track1        track2             track10

-----                      ------        ------             -------

Billboard Top Hits - 1984  Loverboy      Shout              St. Elmo's Fire

                          (Billy Ocean) (Tears for Fears) (John Parr)




这看上去很合理。大多数CD只有10首曲子,对否?不尽然。如果你拥有一张有100首曲子的CD且几张超过20首改怎么办。这意味着用这种方法,在极端的情况下,你将需要一个非常宽的表格(或一个超过100个字段的表)来保存所有的数据。

规范化表结构的目标是使“空单元”的数量最少,在上述CD表的情况下,如果你允许CD可能包含100首曲子,你会有很多这样的空单元。不管你何时处理可能扩展到类似该CD表那样数量的字段列表,它是你需要将你的数据分割成2个或更多表的标志,然后你一起访问并获得你需要的数据。

很多关系数据库的新手不真正知道关系数据库管理系统中关系是什么。简单地说,就像一组信息存在可以基于共性数据联结(JOIN)在一起的不同表中,很不幸,这听上去更学术化和含糊,但CD数据库提出了一个具体情况,我们可以研究如何规范数据。

每个CD列表有一个固定的属性(标题、艺术家、年份、分类)集和一个不定的属性(曲目表)集的理解给了我们一些如何分成成能相互关联的表的思路。

你可以创建一个所有专辑及其固定属性的表,另一个包含这些专辑的所有曲目的表。这样不是水平思考(像表格),你垂直思考--就好像你创建列表而不是行--并建立一个如图2的表结构:



点击查看大图

专辑的编号(MySQL镜自动为你生成,因为我们在列上使用了AUTO_INCREMENT属性)关联不同曲目到一给定专辑,tracks表中的album_id字段匹配专辑表中的一个id。这样要获得给定专辑的所有曲目,你应该用如下查询:

 SELECT tracks.num, tracks.name
FROM albums, tracks
WHERE albums.title = 'Billboard Top Hits - 1984'
AND albums.id = tracks.album_id
 

该结构即灵活又有效。灵活性来自你可以在以后将数据加入系统而不必重新你已完整的工作的事实。例如,如果你想增加每一张专辑的艺术家信息,你可以床架一个artists表,关联到albums表,就像tracks那样。你无需修改现有的结构--只是增加它。

有效性来自于在你的数据中没有明显的数据重复且没有大量的空洞(空单元)的实施。这样MySQL在你的数据库表中既不存储多余的数据,也不比花额外的精力搜索大量空区域。

如果你对关系数据库是新手,规范化你的数据看起来有点奇怪,但在存储和检索数据时,它使MySQL非常有效,并给予你扩展和伸缩你的应用却不必多次重构你的数据库的灵活性。尽可能早的花时间想清楚数据库设计,并考虑你的需求怎样随时间增长,前期花的时间永远是值得的。

复合索引

复合索引(有时称组合索引)是急于多个列的单一索引。MySQL在处理一条查询时每个表只使用一个索引,这意味着如果你有多个经常出现在WHERE子句中的列,你可能要通过创建一个复合索引来加快这些查询。

考虑下列表结构片断:

 CREATE TABLE people (
last_name VARCHAR(50) NOT NULL,
first_name VARCHAR(50) NOT NULL,
favorite_color VARCHAR(10) NOT NULL,
.
.
.
);

 

如果你常常基于last_name和first_name查询表,你可以从last_name和first_name的复合索引中获益:

INDEX last_first (last_name, first_name)

由于MySQL构建复合索引的方式,它可以使用last_first索引来回答基于last_name本身或last_name与first_name两者的索引。这是因为如果列涉及复合索引的“最左前缀”的形式,MySQL将只使用一个复合索引。

所以如果一个复合索引有多个列合成:

INDEX big_index (a, b, c, d, e, f, g, h, i)

MySQL可以用它来回答基于a、或a和b、或a和b和c、或a和b和c和d的查询。但它不能使用big_index处理基于e、或c和f、或g和i的查询,因为这些序列没有一个是从索引的最左边开始的。

复合索引尝被用于加快某些复杂查询,但你需要理解起局限,而且你永远应该进行一些测试,而不是简单地假设这样一个索引将会有帮助。

 
使用索引加快查询

当MySQL试图回达一条查询时,它查看有关你的数据的各种统计,并决定如何以最快的速度找出你想要的数据。对于前小节的查询,MySQL将读取albums表的所有titles并把它们与“Billboard Top Hits --1984”进行比较看是否匹配。它一旦找到一个匹配还不能停止,因为有相同曲目的专辑不止一个(如你可以有12张CD标有“Greatest Hits”),结果MySQL必须读取表中的每一行。这常称为“全表扫描”且可以避免。

你应该避免全表扫描,因为:

引用
CPU开销:如果你没有很多专辑,检查所有这些标题的处理相对快些。但如果你需要在你的数据库中存储很多专辑呢?你有的专辑越多,花的时间越长。在专辑数量或检查它们所花的时间时间存在一种线性关系。
并发性:在MySQL正在从表中读取数据时,它锁定表使得没有其他人可以写入,但可以读取。当MySQL更新或删除表中的行时,它锁定表使得没有其他人可以从它读取。
磁盘开销:在一个大数据表上,一次全表扫描将消耗大量磁盘I/O。这可能明显地减慢你的数据库服务器 -- 特别是如果你的服务器是较慢的IDE驱动器。
最好是让全表扫描将到最少 -- 特别是你的应用需要以规模或用户数伸缩。MySQL最新版确实有几个并发性方面的改善(BDB、InnoDB和Gemini表类型)。


在这里索引可以帮助你,简单地放一个,一个索引允许MySQL很快地确定任何给定值如“Billboard Top Hits -- 1984”是否将匹配表中的任何行。

怎样做到的呢?当你告诉MySQL索引一个特定列时,它在幕后创建另一个数据结构(索引)并用它存储关于被索引列中的值的某些额外信息(被索引的值常称为健码)。这是一种简化,MySQL将所有键码存储在一个树状数据结构中。该数据结构允许MySQL非常快速地找到特定键码。

当MySQL发现列上有一个索引,它将使用索引而不是执行一个全表扫描。这节省了CPU时间(不必读取所有可能的值)和磁盘I/O,而且它改善了并发性,因为MySQL只锁定表足够长的时间来获得所需的行(基于它在索引中找什么)。当你在表中有大量的数据,最终的改善可能非常明显。

对图3的albums表的CREATE TABLE语句的改进:

图3

CREATE TABLE albums (
id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(80)NOT NULL,

INDEX title_idx (title)
);



正如你所见的,语句只是简单地在定义后增加了一个INDEX行告诉MySQL在albums表中的title列上创建名为title_idx的索引。你可以给一个表增加多个索引,就像你可在表中有多个列一样。单个索引也可以有多个列合成。

要给现有的表加上一个索引而不是重建表,你可以用ALTER TABLE命令:

ALTER TABLE albums ADD INDEX title_idx (title)  

查询处理

如果你的查询复杂,MySQL用于精确确定如何获取数据的原则可能变得难于理解。幸运的是,有几个一般原则和一条命令允许你获得正在做什么的更好的理解。首先,原则是:

引用
如果MySQL确定了简单地扫描全表更快些,则它L将不使用索引。一般地,如果一个索引告诉MySQL访问表中大概30%的行,它放弃索引并简单地执行全表扫描。
如果多索引可以用来满足查询,MySQL将使用最严格的一个 -- 即导致最少的行被提取的那个。
如果你正在选择的列是一个索引的所有部分,MySQL可以直接从索引中读取锁需的数据,绝不接触(或锁定)表本身。
当联结几个表时,MySQL将首先从可能返回最少行的表中读取数据。你指定表的次序可能与MySQL使用它们的次序不同,这也影响到最终返回给你的行的次序,所以如果你需要行以特定的次序出现,要保证在你的查询中使用一个ORDER子句。
已经说了很多了,重要的是认识到MySQL所做的一些决策实际上是基于猜测,就像人类进行大量猜测一样,偶尔也会出错。


如果你怀疑已经发生或只是想理解MySQL怎样处理一条查询,你可以使用EXPLAIN命令。简单地在你的查询前面加上EXPLAIN这个字,并要求MySQL执行它,MySQL不执行查询,相反将报告有助于查询的候选索引列表和所知道的有关它们的一切。

EXPLAIN输出的完整讨论参见MySQL参考手册。



不要过分使用索引

已经知道索引使查询更快,你可能倾向于索引你表中的每个列。但是得益于索引的性能提高是有代价的,在表中每次执行INSERT、UPDATE、REPLACE或DELETE,MySQL不得不更新表上的每个索引以反映变化。

那么你如何确定何时食用它们呢?最常见的答案是“看情况”。它依赖你运行的查询类型和你运行它们的频度,它依赖于你的数据,它依赖于你的期望和需求。你得到了答案 -- 它依赖于很多事情。

在列上有索引的理由是MySQL缩窄其搜索范围以便尽可能会的匹配行(且避免全表扫描)。你可以认为索引是对列中的每个唯一值只包含一项。在索引中,MySQL必须考虑任何重复值,这些重复值稍微降低效率和索引的用途。

所以在索引一个列之前,考虑数据重复的百分比,如果该百分比太高,你可能觉察不到用索引带来的任何性能改善。

要以更简明的术语描述,它清楚地知道在albums表中索引曲目字段,因为有可能有大量不同的值,而且重复非常少。但如果在albums表中有一个分类列,索引它则可能价值不大,一般的CD收集包含多少不同的类型呢?将会有很多重复的类型值。

另一件要考虑的事情是你的查询可能使用的频度。MySQL只能对出现在一条查询种的WHERE子句中的特定列使用索引,试图回答查询:

SELECT * FROM albums WHERE id = 500  

MySQL不能使用在title上的查询,该查询要求MySQL基于其id查找记录而不是其title。

如果你很少使用查询中WHERE子句中的一个列,它可能不值得索引该列。可能在极少的情况下容人全表扫描比要求MySQL在每次修改时保持索引更新总体上更有效些。.

有疑问时,进行测试。你总能运行某些到索引或不带索引的基准测试看哪一个更快,只要试图让你的基准测试逼真些。如果你的查询有20%是UPDATE,80%是SLEECT,肯定你的基准测试能发映出来,详见MySQL参考手册。

使用REPLACE查询

有可能你想往表中插入一条记录,除非它已经存在。如果记录已经存在,你想UPDATE它。不是重写代码中做这件事的逻辑,并需运行多个查询,而是MySQL使用REPLACE来做此项工作。

如果id是6的专辑假定有曲目“Shaking the Tree”,你可以这样写查询:

REPLACE INTO albums VALUES (6, 'Shaking the Tree')  

重要的是理解REPLACE如何确定一条记录是否在表中存在。MySQL将在表上使用PRIMARY KEY或UNIQUE KEY来执行检查,如果都不存在,REPLACE效果上变成了INSERT。

使用临时表

当工作在非常大的表上时,你可能偶尔需要运行很多查询获得一个大量数据的小的子集,不是对整个表运行这些查询,而是让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 3.23.0和更新版才有。

分页: 1/3 第一页 1 2 3 下页 最后页 [ 显示模式: 摘要 | 列表 ]