今天继续学习《MySQL 必知必会》,今天是23章-30章的内容。
Ch 23.使用存储过程
所谓的存储过程,就是在需要针对多个表进行多条SQL语句处理的复杂场合,将多条SQL语句保存成的一个集合。存储过程相当于一个SQL的集合函数,封装了若干条SQL语句。
1.存储过程的优缺点:
存储过程主要有以下优点:
-
简单
- 通过把处理封装在容易使用的单元中,简化复杂的操作。
- 由于不要求反复建立一系列处理步骤,这保证了数据的完整性。
-
安全
简化对变动的管理。如果表名、列名或业务逻辑(或别的内容)有变化,只需要更改存储过程的代码。使用它的人员甚至不需要知道这些变化。通过存储过程可以限制对基础数据的访问,减少了数据讹传。
-
高性能
- 提高性能。因为使用存储过程比使用单独的SQL语句要快。
- 存在一些只能用在单个请求中的MySQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码。
存储过程的缺点也很明显:编写存储过程要比编写基本的SQL语句复杂很多;存储过程也往往存在一定的限制,比如只允许用户使用存储过程而不允许创建。
2.使用存储过程
调用存储过程
MySQL 调用执行过程使用的是 CALL关键字,如:
CALL productpricing(@pricelow,
@pricehigh,
@priceaverage);
#CALL 存储过程名(参数列表);
创建存储过程
以下创建了一个简单的无参的存储过程:
CREATE PROCEDURE productpricing()
BEGIN
SELECT Avg(prod_price) AS priceaverage
FROM products;
END;
#CREATE PROCEDURE (参数列表)
#BEGIN
# SQL 语句
#END;
注意:如果使用的是命令行版的MySQL,存储过程体中的SQL语句中的;
会被作为分隔符使用,造成错误。解决措施是临时临时更改命令行版MySQL的语句分隔符:
#将分隔符替换为//
DELIMITER //
CREATE PROCEDURE productpricing()
BEGIN
SELECT Avg(prod_price) AS priceaverage
FROM products;
END //
DELIMITER ;
#将分隔符改回;
除了\符号外,其他的符号都可以作为命令行 MySQL 的分隔符。
调用上述存储过程:
CALL productpricing();
删除存储过程
删除上述存储过程:
DROP PROCEDURE productpricing;
#存储过程名后面不需要带()
#仅存在时删除:DROP PROCEDURE IF EXISTS
使用参数
使用存储过程时,常常将结果存储到某个变量里面,因此需要在存储过程中使用参数传递变量:
CREATE PROCEDURE productpricing(
OUT pl DECIMAL(8,2),
OUT ph DECIMAL(8,2),
OUT pa DECIMAL(8,2)
)
BEGIN
SELECT Min(prod_price)
INTO pl
FROM products;
SELECT Max(prod_price)
INTO ph
FROM products;
SELECT Avg(prod_price)
INTO pa
FROM products;
END;
此存储过程接受3个参数:pl存储产品最低价格,ph存储产品最高价格,pa存储产品平均价格。每个参数必须具有指定的类型,这里使用十进制值。关键字OUT指出相应的参数用来从存储过程传出一个值(返回给调用者)。MySQL支持IN(传递给存储过程)、OUT(从存储过程传出,如这里所用)和INOUT(对存储过程传入和传出)类型的参数。存储过程的代码位于BEGIN和END语句内,如前所见,它们是一系列SELECT语句,用来检索值,然后保存到相应的变量(通过指定INTO关键字)。
注意:使用变量时,尤其是对变量进行赋值时需要注意变量的数据类型。
调用上述存储过程,并输出结果:
CALL productpricing(@pricelow,
@pricehigh,
@priceaverage);
SELECT @priceaverage;
注意:所有MySQL变量都需要使用@
。
以下是一个使用 IN 和 OUT 变量的存储过程:
CREATE PROCEDURE ordertotal(
IN onumber INT,
OUT ototal DECIMAL(8,2)
)
BEGIN
SELECT Sum(item_price*quantity)
FROM orderitems
WHERE order_num = onumber
INTO ototal;
END;
调用上述存储过程并输出结果:
CALL ordertotal(20005,@total);
SELECT @total;
建立智能存储过程
以下是声明了一个复杂存储过程:
-- Name: ordertotal
-- Parameters: onumber = order number
-- taxable = 0 if not taxable, 1 if taxable
-- ototal = order total variable
CREATE PROCEDURE ordertotal(
IN onumber INT,
IN taxable BOOLEAN,
OUT ototal DECIMAL(8,2)
) COMMENT 'Obtain order total, optionally adding tax'
BEGIN
-- Declare variable for total
DECLARE total DECIMAL(8,2);
-- Declare tax percentage
DECLARE taxrate INT DEFAULT 6;
-- Get the order total
SELECT Sum(item_price*quantity)
FROM orderitems
WHERE order_num = onumber
INTO total;
-- Is this taxable?
IF taxable THEN
-- Yes, so add taxrate to the total
SELECT total+(total/100*taxrate) INTO total;
END IF;
-- And finally, save to out variable
SELECT total INTO ototal;
END;
注意以下几点:
- 增加了注释
--
。在编写复杂的存储过程时,添加一定的注视非常重要。 - 添加了另外一个参数taxable,它是一个布尔值,传入变量为0或1。
- 用DECLARE语句定义了两个局部变量。
- IF语句检查taxable是否为真,如果为真,则用另一SELECT语句增加营业税到局部变量total。最后,用另一SELECT语句将total(它增加或许不增加营业税)保存到ototal。
- 添加了COMMENT输出信息,相当于日志的作用,其信息将在SHOW PROCEDURE STATUS的结果中显示。
调用上述存储过程,并输出结果:
CALL ordertotal(20005, 0, @total);
SELECT @total;
检查存储过程
查看存储过程的创建语句:
SHOW CREATE PROCEDURE ordertotal;
输出存储过程的详细信息:
SHOW PROCEDURE STATUS;
以上语句会输出全部的存储过程的信息,可以通过以下方式进行过滤:
SHOW PROCEDURE STATUS LIKE 'ordertotal';
Ch 24.使用游标
SQL 检索语句返回的结果称为结果集,之前的操作无法完成这样的操作:获取下一行,获取前十行等等。这就要使用游标了。游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改。不同于其他DBMS,MySQL的游标只能用于存储过程。
1.使用游标
使用游标涉及几个明确的步骤:
- 在能够使用游标前,必须声明(定义)它。这个过程实际上没有检索数据,它只是定义要使用的SELECT语句。
- 一旦声明后,必须打开游标以供使用。这个过程用前面定义的SELECT语句把数据实际检索出来。
- 对于填有数据的游标,根据需要取出(检索)各行。
- 在结束游标使用时,必须关闭游标。
在声明游标后,可根据需要频繁地打开和关闭游标。在游标打开后,可根据需要频繁地执行取操作。
定义游标
CREATE PROCEDURE processorders()
BEGIN
DECLARE ordernumbers CURSOR
FOR
SELECT ordernum FROM orders;
END;
游标的命名方式为DECLARE 游标名 CURSOR
,需要注意的是,存储过程执行完毕后游标就消失。
打开和关闭游标
#打开游标
OPEN ordernumbers;
#关闭游标
CLOSE ordernumbers;
注意:只有打开游标后才能使用它,在游标使用完毕后需要关闭它以释放资源。即使不手动关闭游标,当存储过程执行完毕游标也会被 MySQL 关闭。
CREATE PROCEDURE processorders()
BEGIN
-- Declare the cursor
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
-- Open the cursor
OPEN ordernumbers;
-- Close the cursor
CLOSE ordernumbers;
END;
使用游标数据
在一个游标被打开后,可以使用FETCH语句分别访问它的每一行。FETCH指定检索什么数据(所需的列),检索出来的数据存储在什么地方。它还向前移动游标中的内部行指针,使下一条FETCH语句检索下一行(不重复读取同一行)。
第一个例子是利用FETCH检索第一行
CREATE PROCEDURE processorders()
BEGIN
-- Declare local variables
DECLARE o INT;
-- Declare the cursor
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
-- Open the cursor
OPEN ordernumbers;
-- Get order number
--将检索出的数据存放在局部变量o中
FETCH ordernumbers INTO o;
-- Close the cursor
CLOSE ordernumbers;
END;
下一个例子是循环检索数据,从第一行到最后一行
CREATE PROCEDURE processorders()
BEGIN
-- Declare local variables
DECLARE done BOOLEAN DEFAULT 0;
DECLARE o INT;
-- Declare the cursor
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
-- Declare continue handler
-- SQLSTATE '02000'指的是未找到的错误,强行类比的话像是NullPointerException
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
-- Open the cursor
OPEN ordernumbers;
-- Loop through all rows
REPEAT
-- Get order number
FETCH ordernumbers INTO o;
-- End of loop
UNTIL done END REPEAT;
-- Close the cursor
CLOSE ordernumbers;
END;
注意:DECLARE语句的发布存在特定的次序。用DECLARE语句定义的局部变量必须在定义任意游标或句柄之前定义,而句柄必须在游标之后定义。不遵守此顺序将产生错误消息。
接下来是一个复杂的例子,其中ordertotal是上一章定义的一个计算税率的存储过程:
CREATE PROCEDURE processorders()
BEGIN
-- Declare local variables
DECLARE done BOOLEAN DEFAULT 0;
DECLARE o INT;
DECLARE t DECIMAL(8,2);
-- Declare the cursor
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
-- Declare continue handler
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
-- Create a table to store the results
CREATE TABLE IF NOT EXISTS ordertotals
(order_num INT, total DECIMAL(8,2));
-- Open the cursor
OPEN ordernumbers;
-- Loop through all rows
REPEAT
-- Get order number
FETCH ordernumbers INTO o;
-- Get the total for this order
CALL ordertotal(o, 1, t);
-- Insert order and total into ordertotals
INSERT INTO ordertotals(order_num, total)
VALUES(o, t);
-- End of loop
UNTIL done END REPEAT;
-- Close the cursor
CLOSE ordernumbers;
END;
在这个存储过程中,将检索出来的数据存储到新表中。
Ch 25.使用触发器
如果想使得某些语句在一些事件发生后自动执行,如每当订购一个产品时,都从库存数量中减去订购的数量;无论何时删除一行,都在某个存档表中保留一个副本。想要完成这样的操作,就要使用触发器。
触发器是MySQL响应以下任意语句而自动执行的一组MySQL语句:
- DELETE;
- INSERT;
- UPDATE。
其他MySQL语句不支持触发器。
1.创建触发器
创建触发器需要提供以下信息:
- 唯一的触发器名;
- 触发器关联的表;
- 触发器应该响应的活动(DELETE、INSERT或UPDATE);
- 触发器何时执行(处理之前或之后)。
需要注意的是,最好是保持每个数据库的触发器名唯一。另外,只有表支持触发器,视图不支持、临时表也不支持。
CREATE TRIGGER newproduct AFTER INSERT ON products
FOR EACH ROW SELECT 'Product added';
上述语句对 products 定义了一个触发器,每当对该表进行插入操作后,对每个插入行显示“Product added”消息。
MySQL 触发器按每个表每个事件每次地定义,每个表每个事件每次只允许一个触发器。因此,每个表最多支持6个触发器,即INSERT、DELETE、UPDATE的执行前和执行后。
注意:在MySQL中,如果BEFORE触发器失败,则不会执行请求的操作;如果BEFORE触发器失败或者语句本身失败,也不会执行AFTER触发器操作。
2.删除触发器
DROP TRIGGER newproduct;
3.使用触发器
INSERT触发器
INSERT触发器在INSERT语句执行之前或之后执行。需要知道以下几点:
- 在INSERT触发器代码内,可引用一个名为NEW的虚拟表,访问被插入的行;
- 在BEFORE INSERT触发器中,NEW中的值也可以被更新(允许更改被插入的值);
- 对于AUTO_INCREMENT列,NEW在INSERT执行之前包含0,在INSERT执行之后包含新的自动生成值。
CREATE TRIGGER neworder AFTER INSERT ON orders
FOR EACH ROW SELECT NEW.order_num;
上述语句定义了一个 neworder 的触发器,在对orders表进行插入操作后对于每个插入行显示该新插入记录的order_num属性值。
#运行如下语句
INSERT INTO orders(order_date, cust_id)
VALUES(Now(), 10001);
#可得到如下输出
+-----------+
| order_num |
+-----------+
| 20010 |
+-----------+
注意:BEFORE触发器多用于数据的验证和净化,而AFTER触发器多用于显示提示信息。
DELETE触发器
DELETE触发器在DELETE语句执行之前或之后执行。需要知道以下两点:
- 在DELETE触发器代码内,你可以引用一个名为OLD的虚拟表,访问被删除的行;
- OLD中的值全都是只读的,不能更新。
下面的例子演示使用OLD将要被删除的行保存到一个存档表中:
CREATE TRIGGER deleteorder BEFORE DELETE ON orders
FOR EACH ROW
BEGIN
INSERT INTO archive_orders(order_num, order_date, cust_id)
VALUES(OLD.order_num, OLD.order_date, OLD.cust_id);
END;
#触发器体可以使用BEGIN和END包裹多条SQL语句。
根据前文所提到的,如果BEFORE触发器执行失败,则说明存档失败,因此不会执行删除操作。
UPDATE触发器
UPDATE触发器在UPDATE语句执行之前或之后执行。需要知道以下几点:
- 在UPDATE触发器代码中,你可以引用一个名为OLD的虚拟表访问以前(UPDATE语句前)的值,引用一个名为NEW的虚拟表访问新更新的值;
- 在BEFORE UPDATE触发器中,NEW中的值可能也被更新(允许更改将要用于UPDATE语句中的值);
- OLD中的值全都是只读的,不能更新。
下面的例子保证州名缩写总是大写(不管UPDATE语句中给出的是大写还是小写):
CREATE TRIGGER updatevendor BEFORE UPDATE ON vendors
FOR EACH ROW SET NEW.vend_state = Upper(NEW.vend_state);
注意事项
以下是原书中所写的触发器注意事项:
- 与其他DBMS相比,MySQL5中支持的触发器相当初级。未来的MySQL版本中有一些改进和增强触发器支持的计划。
- 创建触发器可能需要特殊的安全访问权限,但是,触发器的执行是自动的。如果INSERT、UPDATE或DELETE语句能够执行,则相关的触发器也能执行。
- 应该用触发器来保证数据的一致性(大小写、格式等)。在触发器中执行这种类型的处理的优点是它总是进行这种处理,而且是透明地进行,与客户机应用无关。
- 触发器的一种非常有意义的使用是创建审计跟踪。使用触发器,把更改(如果需要,甚至还有之前和之后的状态)记录到另一个表非常容易。
- 遗憾的是,MySQL触发器中不支持CALL语句。这表示不能从触发器内调用存储过程。所需的存储过程代码需要复制到触发器内。
Ch 26.管理事务处理
正如前文所讲的,并非所有的数据库引擎都支持事务处理,MySQL最常用的引擎中,InnoDB支持事务而MyISAM不支持。
事务处理(transactionprocessing)可以用来维护数据库的完整性,它保证成批的MySQL操作要么完全执行,要么完全不执行,以保证数据库不包含不完整的操作结果。如果没有错误发生,整组语句提交给(写到)数据库表。如果发生错误,则进行回退(撤销)以恢复数据库到某个已知且安全的状态。
首先定义几个概念:
- 事务(transaction)指一组SQL语句;
- 回退(rollback)指撤销指定SQL语句的过程;
- 提交(commit)指将未存储的SQL语句结果写入数据库表;
- 保留点(savepoint)指事务处理中设置的临时占位符(place-holder),你可以对它发布回退(与回退整个事务处理不同)。
1.控制事务处理
管理事务处理的关键在于将SQL语句组分解为逻辑块,并明确规定数据何时应该回退,何时不应该回退。
MySQL使用下面的语句来标识事务的开始:
START TRANSACTION
使用ROLLBACK
SELECT * FROM ordertotals;
START TRANSACTION;
DELETE FROM ordertotals;
SELECT * FROM ordertotals;
ROLLBACK;
SELECT * FROM ordertotals;
上述语句开启事务后将ordertotals表中的记录全部删除,然后回滚事务,将数据库状态恢复至刚刚开启事务时。
注意:只能回退INSERT、UPDATE、DELETE语句,SELECT语句回退没有意义。不能回退CREATE和DROP,虽然可以在事务处理块中使用这两种语句,但是执行回退他们不会被撤销。
使用COMMIT
一般的MySQL语句都是直接针对数据库表执行和编写的。这就是所谓的隐含提交(implicitcommit),即提交(写或保存)操作是自动进行的。但是,在事务处理块中,提交不会隐含地进行。为进行明确的提交,使用COMMIT语句。
START TRANSACTION;
DELETE FROM orderitems WHERE order_num = 20010;
DELETE FROM orders WHERE order_num = 20010;
COMMIT;
最后的COMMIT语句仅在不出错时写出更改。如果第一条DELETE起作用,但第二条失败,则DELETE不会提交(实际上,它是被自动撤销的)。
注意:当COMMIT或ROLLBACK语句执行后,事务会自动关闭(将来的更改会隐含提交)。
使用保留点
更复杂的事务处理可能需要部分提交或回退,为了支持回退部分事务处理,必须能在事务处理块中合适的位置放置占位符。这样,如果需要回退,可以回退到某个占位符。这些占位符称为保留点。为了创建占位符,可如下使用SAVEPOINT语句:
SAVEPOINT delete1;
#回退到保留点
ROLLBACK TO delete1;
注意:在进行复杂的事务操作时,可以尽可能多地设置保留点,这样可以灵活回退。保留点在事务完成后会自动释放,但也可以使用RELEASE SAVEPOINT
明确地释放保留点。
2.更改默认的自动提交
默认的MySQL行为是自动提交所有更改。换句话说,任何时候你执行一条MySQL语句,该语句实际上都是针对表执行的,而且所做的更改立即生效。为指示MySQL不自动提交更改,需要使用以下语句:
SET autocommit=0;
注意:autocommit标志是针对每个连接而不是服务器的。
Ch 27.全球化和本地化
1.使用字符集和校正顺序
查看MySQL所支持字符集的完整列表:
SHOW CHARACTER SET;
查看所支持的校正顺序的完整列表:
SHOW COLLATION;
确定所使用的字符集和校正顺序:
SHOW VARIABLES LIKE 'character%';
SHOW VARIABLES LIKE 'collation%';
在创建表时指定字符集和校正顺序:
CREATE TABLE mytable
(
columnn1 INT,
columnn2 VARCHAR(10)
) DEFAULT CHARACTER SET hebrew
COLLATE hebrew_general_ci;
一般,MySQL如下确定使用什么样的字符集和校对:
- 如果指定CHARACTER SET和COLLATE两者,则使用这些值。
- 如果只指定CHARACTER SET,则使用此字符集及其默认的校对(如SHOW CHARACTER SET的结果中所示)。
- 如果既不指定CHARACTER SET,也不指定COLLATE,则使用数据库默认。
MySQL还允许对每个列设置字符集和校正顺序:
CREATE TABLE mytable
(
columnn1 INT,
columnn2 VARCHAR(10),
column3 VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_general_ci
) DEFAULT CHARACTER SET hebrew
COLLATE hebrew_general_ci;
校对在对用ORDER BY子句检索出来的数据排序时起重要的作用。如果你需要用与创建表时不同的校对顺序排序特定的SELECT语句,可以在SELECT语句自身中进行:
SELECT * FROM customers
ORDER BY lastname, firstname COLLATE latin1_general_cs;
Ch 28.安全管理
1.访问控制
MySQL服务器的安全基础是:用户应该对他们需要的数据具有适当的访问权,既不能多也不能少。
MySQL默认数据库的用户root拥有对数据库的绝对管理权限,应该严肃对待root登录的使用。仅在绝对需要时使用它(或许在你不能登录其他管理账号时使用)。不应该在日常的MySQL操作中使用root。
2.管理用户
MySQL用户账号和信息存储在名为mysql的MySQL数据库中。可以在其中的user表中查询到所有的用户:
USE mysql;
SELECT user FROM user;
创建用户
CREATE USER ben IDENTIFIED BY 'p@$$w0rd';
重命名用户
RENAME USER ben TO bforta;
删除用户
DROP USER bforta;
设置访问权限
新创建的用户没有任何权限,只能登录服务器,不能操作任何表和数据库。
查看权限:
SHOW GRANTS FOR bforta;
#输出为
+-------------------------------------------------+
| Grants for bforta@% |
+-------------------------------------------------+
| GRANT USAGE ON *.* TO 'bforta'@'%' |
+-------------------------------------------------+
ON*.*
表示在任意数据库和任意表上对任何东西没有权限。
另外,“MySQL的权限用用户名和主机名结合定义,即user@host
。如果不指定主机名,则使用默认的主机名%(授予用户访问权限而不管主机名)。
可以使用 GRANT 语句授予用户权限,
- 要授予的权限;
- 被授予访问权限的数据库或表;
- 用户名。
以下例子给出GRANT的用法:
GRANT SELECT ON crashcourse.* TO beforta;
这条语句授予beforta用户对crashcourse数据库中所有的表上使用SELECT语句的权限。
可以使用REVOKE语句来撤销GRANT授予的权限:
REVOKE SELECT ON crashcourse.* FROM beforta
GRANT和REVOKE可在几个层次上控制访问权限:
- 整个服务器,使用GRANT ALL和REVOKE ALL;
- 整个数据库,使用ON database.*;
- 特定的表,使用ON database.table;
- 特定的列;
- 特定的存储过程。
以下是具体权限内容:
可通过列出各权限并用逗号分隔,将多条GRANT语句串在一起,如下所示:
GRANT SELECT, INSERT ON crashcourse.* TO beforta;
更改口令
可以使用下列语句更改口令:
SET PASSWORD FOR bforta = Password('n3w p@$$w0rd');
新口令必须传递到Password()函数进行加密。
Ch 29.数据库维护
1.备份数据
对数据库进行备份主要有以下几种方式:
- 在MySQL命令行使用
mysqldump
将所有数据库内容存储到某个外部文件中; - 在MySQL命令行使用
mysqlhotcopy
从一个数据库复制所有数据(并非所有的数据库引擎都支持); - 可以使用MySQL的
BACKUP TABLE
或SELECT INTO OUTFILE
转储所有数据到某个外部文件。数据可以用RESTORE TABLE
来复原。
注意:为了保证所有数据被写到磁盘(包括索引数据),可能需要在进行备份前使用FLUSH TABLES
语句。
2.进行数据库维护
应该知道以下语句:
ANALYZE TABLE
,用来检查表键是否正确。ANALYZE TABLE返回如下所示的状态信息:
ANALYZE TABLE orders;
CHECK TABLE
用来针对许多问题对表进行检查。CHECK TABLE支持一系列的用于MyISAM引擎表的方式。CHANGED检查自最后一次检查以来改动过的表。EXTENDED执行最彻底的检查,FAST只检查未正常关闭的表,MEDIUM检查所有被删除的链接并进行键检验,QUICK只进行快速扫描。如下所示,CHECK TABLE发现和修复问题:
CHECK TABLE orders, orderitems;
3.诊断启动问题
在排除系统启动问题时,首先应该尽量用手动启动服务器。MySQL服务器自身通过在命令行上执行mysqld启动。下面是几个重要的mysqld命令行选项:
- --help显示帮助——一个选项列表;
- --safe-mode装载减去某些最佳配置的服务器;
- --verbose显示全文本消息(为获得更详细的帮助消息与--help联合使用);
- --version显示版本信息然后退出。
4.查看日志文件
MySQL维护管理员依赖的一系列日志文件。主要的日志文件有以下几种:
- 错误日志。它包含启动和关闭问题以及任意关键错误的细节。此日志通常名为hostname.err,位于data目录中。此日志名可用--log-error命令行选项更改。
- 查询日志。它记录所有MySQL活动,在诊断问题时非常有用。此日志文件可能会很快地变得非常大,因此不应该长期使用它。此日志通常名为hostname.log,位于data目录中。此名字可以用--log命令行选项更改。
- 二进制日志。它记录更新过数据(或者可能更新过数据)的所有语句。此日志通常名为hostname-bin,位于data目录内。此名字可以用--log-bin命令行选项更改。注意,这个日志文件是MySQL 5中添加的,以前的MySQL版本中使用的是更新日志。
- 缓慢查询日志。顾名思义,此日志记录执行缓慢的任何查询。这个日志在确定数据库何处需要优化很有用。此日志通常名为hostname-slow.log,位于data目录中。此名字可以用--log-slow-queries命令行选项更改。
Ch 30.改善性能
数据库管理员把他们生命中的相当一部份时间花在了调整、试验以改善DBMS性能之上。在诊断应用的滞缓现象和性能问题时,性能不良的数据库(以及数据库查询)通常是最常见的祸因。
回顾之前的章节,总结了一下的几个性能Tips:
- 首先,MySQL(与所有DBMS一样)具有特定的硬件建议。在学习和研究MySQL时,使用任何旧的计算机作为服务器都可以。但对用于生产的服务器来说,应该坚持遵循这些硬件建议。
- 一般来说,关键的生产DBMS应该运行在自己的专用服务器上。
- MySQL是用一系列的默认设置预先配置的,从这些设置开始通常是很好的。但过一段时间后你可能需要调整内存分配、缓冲区大小等。(为查看当前设置,可使用SHOW VARIABLES;和SHOW STATUS;。)
- MySQL是一个多用户多线程的DBMS,换言之,它经常同时执行多个任务。如果这些任务中的某一个执行缓慢,则所有请求都会执行缓慢。如果你遇到显著的性能不良,可使用SHOW PROCESS LIST显示所有活动进程(以及它们的线程ID和执行时间)。你还可以用KILL命令终结某个特定的进程(使用这个命令需要作为管理员登录)。
- 总是有不止一种方法编写同一条SELECT语句。应该试验联结、并、子查询等,找出最佳的方法。
- 使用EXPLAIN语句让MySQL解释它将如何执行一条SELECT语句。
- 一般来说,存储过程执行得比一条一条地执行其中的各条MySQL语句快。
- 应该总是使用正确的数据类型。
- 决不要检索比需求还要多的数据。换言之,不要用SELECT*(除非你真正需要每个列)。
- 有的操作(包括INSERT)支持一个可选的DELAYED关键字,如果使用它,将把控制立即返回给调用程序,并且一旦有可能就实际执行该操作。
- 在导入数据时,应该关闭自动提交。你可能还想删除索引(包括FULLTEXT索引),然后在导入完成后再重建它们。
- 必须索引数据库表以改善数据检索的性能。确定索引什么不是一件微不足道的任务,需要分析使用的SELECT语句以找出重复的WHERE和ORDER BY子句。如果一个简单的WHERE子句返回结果所花的时间太长,则可以断定其中使用的列(或几个列)就是需要索引的对象。
- 你的SELECT语句中有一系列复杂的OR条件吗?通过使用多条SELECT语句和连接它们的UNION语句,你能看到极大的性能改进。
- 索引改善数据检索的性能,但损害数据插入、删除和更新的性能。如果你有一些表,它们收集数据且不经常被搜索,则在有必要之前不要索引它们。(索引可根据需要添加和删除。)
- LIKE很慢。一般来说,最好是使用FULLTEXT而不是LIKE。
- 数据库是不断变化的实体。一组优化良好的表一会儿后可能就面目全非了。由于表的使用和内容的更改,理想的优化和配置也会改变。
- 最重要的规则就是,每条规则在某些条件下都会被打破。
以上差不多就是《MySQL 必知必会》这本书的全部内容了,这本书深入浅出对MySQL数据库进行了一个整体的介绍。但是我们的高性能SQL的计划才刚刚开始。
未完待续······