《MySQL必知必会》-Ben Forta(笔记)
步骤/目录:
无本文首发于个人博客https://lisper517.top/index.php/archives/4248/,转载请注明出处。
本文的原文写作日期为2020年9月20日,主要目的是总结《MySQL必知必会》。
下面的内容有些缩进或者图片缺失,可以参考 原word文档 。
本书的内容十分基础,建议直接看原文,或者忘记语法的时候查阅。初读的时候可以先粗略读一遍,然后把书上的每个例子都打一次。附录c可以作为复习使用。
1.可以创建多个数据库,每个数据库可包含多个表。同一个库中的表不能重名。Schema模式,指库和表的组织形式,可以用describe 表 来查看。表分为行、列,尽量把列细分(数据细分)方便查找;每行数据有一个主键来作为唯一标识,虽然有些数据不一定需要主键才能区分,但一般都应该定义一个主键,Mysql要求任意两行的主键不同,且每行都有一个非NULL的主键,主键也可以定义在多个列上。
定义主键时应该做到:不重用主键(删了以后不重用);用不可能变的值作为主键;不修改主键值。
除了主键,还可以定义外键,将表中数据关联到其他表。
2.mysql administrator 和 mysql query browser 提供了GUI,可以考虑下载。由于mysql8有一些改动,可能遇到问题。
进入数据库:在cmd输入mysql -uroot -h localhost -p(先将mysql加入环境变量),接下来要求输入密码。如果连接本地服务器,可以省略-h参数。
Mysql命令行中,每个语句末最好加上;
mysql不区分大小写,但很多人喜欢将关键字大写。
Mysql不会识别换行符,可以将命令分行写出,便于阅读。
在mysql命令行下,用show命令显示库和表,也可以用show columns from 表名 显示表内列的信息,或者用更简洁的describe 表名。
用help 命令名 获取相关信息。
Show的其他用法:
SHOW STATUS,用于显示广泛的服务器状态信息;
SHOW CREATE DATABASE和SHOW CREATE TABLE,分别用来显示创
建特定数据库或表的MySQL语句;
SHOW GRANTS,用来显示授予用户(所有用户或特定用户)的安
全权限;
SHOW ERRORS和SHOW WARNINGS,用来显示服务器错误或警告消息。
3.select命令返回的顺序可能变化(为底层存储顺序),因为mysql在数据删除后可能重用空间。
如果列中的值有重复,可使用distinct关键字指明查询不同值。Distinct会用于所有列,比如指定了多个列,如果查找到一行,其每个列的数据同前面某行完全一样,那么就不会打印出。
在末尾加limit a,b(a和b为数字),意为指定从行a开始(不包括行a,第一行为0)最多打印b个结果。
用.表示库和表的从属关系。
用order by 语句可以指定用某一列数据排序。该列可以不在显示出来的列中。Order by 后跟多个列时,表示按权重排名。在最后加desc表示降序(对多个列需要分别写一次desc)。Limit必须在order by 之后。Order by 应在where后。
Where:使用between a and b指定数值范围(返回结果包括a和b)。Is null 检索值为NULL的列。检索非某值时不会返回值为NULL的行。
And比or优先。用()分离不同条件,且应该尽量多用()。
In用法类似于python元组。In还可以嵌套where。
Not可以用来否定条件,比如对in、between、exists取反。
正则表达式、通配符:必须用like。
%表示任意字符任意次数(含0次)。%不匹配NULL。
_表示单个字符单次。
通配符搜索比较慢,尤其是放在开头。
用regexp代替like,表示后面跟的是re。不加^和$,regexp默认全部或部分匹配,而like只能全部匹配。Regexp后加binary可区分大小写。
Mysql中的re转义字符要求写成\\而不是单个\。
写的时候要[[]]
写完的re可以用select 字符串 regexp re表达式 的格式来测试,0为未找到。
4.计算字段,通过表中数据经过一定运算得出的数据列,不存在于表中。一般在服务器上就计算出相应数据并返回(因为DBMS的处理快),客户机上并不能区分计算字段和表中数据。
为了使客户机能引用计算字段,需要将计算字段赋予别名,select 计算字段 as 别名 from ...
As中,多个计算字段要分别写多个as、赋予不同的别名。
拼接:mysql用conca()函数实现拼接(其他sql用+或||等)。
删掉空格可以用RTrim、LTrim或者Trim函数。
Select省略from时可以用于实验输出。
5.sql的函数移植性较差。更多函数参见https://www.runoob.com/mysql/mysql-functions.html
用函数时区分大小写。
常用的字符处理函数:
SOUNDEX是一个将任何文本串转换为描述其语音表示的字母数字模式的算法。SOUNDEX考虑了类似的发音字符和音节,使得能对串进行发音比较而不是字母比较。虽然SOUNDEX不是SQL概念,但MySQL(就像多数DBMS一样)都提供对SOUNDEX的支持。
时间日期函数:
日期类型可以用between。
数值处理函数:
6.汇总函数:如果想要找出最大值、最小值,满足特定条件的行数等,不需要返回表中数据而只需要汇总时
所有的聚集函数都可以用运算符进行多个列上的运算。
Avg()函数忽略NULL行;count(*)则可以计数NULL行,如果指定列名则不计数。
Distinct可以修饰聚集函数里的列,从而只计算不重复的值。不可以distinct *,无意义。
7.分组数据:
Group by 列名可以将数据按列名分组,在各组里执行相应操作。
GROUP BY子句可以包含任意数目的列。这使得能对分组进行嵌套,
为数据分组提供更细致的控制。
如果在GROUP BY子句中嵌套了分组,数据将在最后规定的分组上
进行汇总。换句话说,在建立分组时,指定的所有列都一起计算
(所以不能从个别的列取回数据)。 GROUP BY子句中列出的每个列都必须是检索列或有效的表达式
(但不能是聚集函数)。如果在SELECT中使用表达式,则必须在
GROUP BY子句中指定相同的表达式。不能使用别名。
除聚集计算语句外,SELECT语句中的每个列都必须在GROUP BY子
句中给出。
如果分组列中具有NULL值,则NULL将作为一个分组返回。如果列
中有多行NULL值,它们将分为一组。
GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句之前。
对分组进行过滤使用having。其语法和where相同。Where在数据分组前过滤,having在分组后进行过滤。
子句小结(按在mysql命令行中使用顺序):
8.子查询:可以把一个select查询返回的结果用于另一个select,即嵌套查询。子查询的where语句中,尽量使用表名.列名来完全限定列。子查询最好从里到外慢慢构造。有时子查询的性能不够好,可以使用连结表。嵌套的子查询语句末不用写;,括号抱起来即可。
子查询可以返回一个列表/列,可以用在where中跟在in后面做条件;或者用于创建计算字段,跟在select后面(相当于将两个表每行的数据拼接在一起),即相关子查询(涉及外部),这时由于两个表中相关的列往往是同名的,一般都要完全限定列名。在mysql结构中,列表和列是一样的。
9.联结表:关系数据库。外键:表中某列包含其他表中的主键值。联结在物理存储上不存在,而是存在于查询的过程中。实际上,联结是通过where子句实现的,比如where 表1.列a =表2.列b(两个列大多数情况下应该是同名的,建立了外键),这种称为等值联结或内部联结,也可以使用FROM 表1 INNER JOIN 表2 ON 表1.列a =表2.列b 的语法写出,推荐使用INNER JOIN 写法(但该语句好像只能用于两个表联结)。
笛卡尔积:表的行数之积。联结表时去掉where语句就会得到该结果,表1中的每行对表2中的每行都建立了联结。所以要用where限定联结建立,过滤那些不应该建立的联结。
可以联结多个表,但性能下降的厉害(表的行数之积)。
10.在from里还可以用as起表别名,缩短语句。表别名不返回客户机。
自联结、自然联结和外部联结:
当需要从自身表查询信息并用于自身表的下一次查询就可以自联结。自联结就是分别将一个表给予两个别名,自己联结自己(只涉及一个表),写成子查询的形式,其内外的from后跟的都是自身表。这种情况下多用自联结而不是子查询。
自然联结:从本质上说,两个表必须要有所关联才能联结,就是说具有相同的列。自然联结排除相同的列,返回结果中每个列最多出现一次。自然联结没有特定的语法,在使用联结时,需要自己注意不要在输出时出现重复的列。
外部联结:当需要对照另一个表输出某表中所有内容,包括没有在另一个表中出现的内容时,用left join或right join。此时某表在另一个表中无关联的行会返回NULL。
11.组合查询:返回一个以上的查询结果,但是包含在同一个返回的表中。用union上下隔开两个select即可。如果将大量相同的数据存在了不同的表中(至少应该是多于一个表中的列有重复),想一次查询返回一个表方便查看,就可以组合查询。如果表中数据有重复,union会自动去除,用union all则不会去除。组合查询的order by跟在最后一个select中,应用于全部返回结果。
12.据说mysql自带的fulltext效率不高、仅支持英文。可以用coreseek和xunsearch等第三方应用来完成这个需求。
数据库引擎:mysql中最常用innodb和myisam,后者才支持全文本搜索。在创建表时,用CREATE TABLE 表名(
数据列
PRIMARY KEY(主键列)
FULLTEXT(全文本列)
)ENGINE=myisam;
来创建全文本搜索。这样全文本列就会自动创建索引(索引都是为了加快查表速度),并且数据更新时自动维护索引。另外,应该在导入完数据后再创建索引。创建索引后,用:
Where match(索引列) against(字符串)
来搜索列内包含字符串的行。如果创建了多个全文本列,在match中需要按次序全部列出。
全文本搜索根据字符串的匹配程度对结果排序,比如字符串出现位置靠前的排前面。如果用select输出match() against(),会发现其是一个值,不匹配的返回0,匹配的根据出现位置、次数等返回一个不同的小数。
扩展查询:mysql先用给定词全文本搜索一次,从匹配的行中选择相关的字符串,根据原来的词和相关词再次全文本搜索。使用扩展查询用match(索引列) against(字符串 WITH QUERY EXPANSION)。行数越多,扩展查询返回的结果相关性越好。
布尔查询:match(索引列) against(‘abc -def*’ IN BOOLEAN MODE)
排除def开头的词。
一些常用bool操作符:
例子:
另外:
在索引全文本数据时,短词被忽略且从索引中排除。短词定义为
那些具有3个或3个以下字符的词 ( 如果需要 , 这个数目可以更改 ) 。 MySQL带有一个内建的非用词 ( stopword ) 列表 , 这些词在索引全文本数据时总是被忽略。如果需要,可以覆盖这个列表(请参
阅MySQL文档以了解如何完成此工作)。 许多词出现的频率很高,搜索它们没有用处(返回太多的结果)。
因此,MySQL规定了一条50%规则,如果一个词出现在50%以上
的行中,则将它作为一个非用词忽略。50%规则不用于IN BOOLEAN
MODE。 如果表中的行数少于3行,则全文本搜索不返回结果(因为每个词
或者不出现,或者至少出现在50%的行中)。 忽略词中的单引号。例如,don't索引为dont。 不具有词分隔符(包括日语和汉语)的语言不能恰当地返回全文
本搜索结果。
如前所述,仅在MyISAM数据库引擎中支持全文本搜索。
13.插入数据用insert into 表名 () VALUES ()。自增的列或允许NULL值列可以用NULL或者不用给出。
对于insert into、delete、update等等语句,可以用low priority降低次序,比如insert low priority into,这样多个用户使用时就会有选择性。
insert into 表名 (列名) VALUES (同序的值), VALUES (同序的值);可以一次插入两行。多个同时插入的性能要更好,有条件时尽量多行数据一起插入表中。
Insert into还可以后接select,将select返回的结果插入表中。格式为insert into ... select ...;
这时的列名类似集合,每个元素都要有,但次序随便。
Update 表名 set 列名1 = 值1, 列名2 = 值2 where 条件
更新表中数据,一定记得加条件。用列名=NULL的方式删除行中的一部分。
Delete删除整行,就不需指定列名了。Delete from 表名 where 条件。
删除所有行应该使用truncate 表名 语句。
删除和更新应该谨慎,最好用select看一下where写对没有。
14.创建表:详见p145
每个表中最多有一个auto_increment列,而且应该是可索引的(比如做主键)。Select last_insert_id()可以返回最后一个增量值。如果在insert中指定了自增列的值(这个值必须没有出现过),后续的列值将在这个基础上增加。
在创建表时列名最后接default 默认值 ,mysql支持用常量或者now()函数作为默认值。注意,对于函数,好像只支持用now()函数,并且列的数据类型是datetime,其他都不行。
指定引擎时,不同引擎有不同的作用,不同引擎的表不能引用外链。
用alter table 表名更改表结构,比如增加列,在后面跟add 列名 数据类型;减少列,后接drop column 列名。一表多动,可以用一个alter接多个逗号分开的改动。
Add还可以添加外链,在后面加ADD CONSTRAINT xxx FOREIGN KEY (在本表中的显示名,应该尽量一致) REFERENCES 引用的表名(外链列)。
重命名:rename 表名 to 新表名
15.视图:视图是一种查询的顺序,按这种顺序返回相应的结果。视图和其他视图或表的名字不应该有重复。视图可以嵌套。
Create view as select ...来创建视图,更新视图可以用replace。创建或替换用create or replace。创建了视图以后,可以将之当作表一样使用,大大简化了查询语句。
在视图中使用的where条件和把视图当表用时的where会合并。
如果视图查询所使用的语句可以确定实际表中的数据,那么可以通过视图更新表。一般来说也不应该通过视图更新实际的表。
16.存储过程:通过一系列操作,将数据存储到库中或者其他对库的操作,由于有时涉及到一些复杂的判断等,需要创建存储过程,即编写函数。
创建存储过程:create procedure 函数名() begin xxx end; 其中的每条独立语句要用;分隔。
调用存储过程:call 函数名(参数表);
在命令行下,;可能引起歧义。可以在定义函数开头加delimiter //,写完后加delimiter ;,表示在这之间用//作为分隔符,这样函数体内写;就不会被认为是函数完结了,相应的end后要加//。除了\符号,其他的字符都可以作为自定义分隔符。
对于有参数的函数:
Create procedure 函数名(out a1 参数类型)
begin
Select avg(...)
Into a1
From 表名
End;
表示a1作为传出的参数用out,还有in、inout。不能用一个参数返回多个行列。
然后调用之:call 函数名(@abcd);,接下来select @abcd;,就可显示变量值。Mysql中的用户变量以@开头。
Mysql中的函数编写比较粗糙和简陋。
注释用--开头。帮助文档用comment。
17.游标:使用select返回结果集后,可以用游标查询不同的行。Mysql中的游标只能用于存储过程和函数(只能写在存储过程和函数里)。
在能够使用游标前,必须声明(定义)它。这个过程实际上没有
检索数据,它只是定义要使用的SELECT语句。
一旦声明后,必须打开游标以供使用。这个过程用前面定义的
SELECT语句把数据实际检索出来。
对于填有数据的游标,根据需要取出(检索)各行。
在结束游标使用时,必须关闭游标。
定义游标用declare 游标名 cursor for select ...,open 游标名,打开游标;close 游标名 关闭游标。如果不关,到end时会自动关闭。打开游标后,可以用 fetch 游标名 into 变量名,一次读取一行。
存储过程中的循环:p195
18.触发器:在一些条件下自动执行的存储过程。Mysql中的触发器可以对delete、insert、update做出反应,也可以位于begin和end//之间。
在创建触发器时,需要给出4条信息:
唯一的触发器名;
触发器关联的表;
触发器应该响应的活动(DELETE、INSERT或UPDATE);(一个表最多6个触发器)
触发器何时执行(处理之前或之后)。
在一个库的触发器最好不要同名。视图和临时表不能使用触发器。
Create trigger 触发器名 after/before delete/insert/update on 表名
内容,比如for each row ...
如果before触发器失败,mysql也不会执行相应的de/in/up操作,并且也不会执行after操作。Drop trigger 触发器名 删除触发器。
对于三种不同类型触发器,见p200
在结束本章之前,我们再介绍一些使用触发器时需要记住的重点。
与其他DBMS相比,MySQL 5中支持的触发器相当初级。未来的
MySQL版本中有一些改进和增强触发器支持的计划。
创建触发器可能需要特殊的安全访问权限,但是,触发器的执行
是自动的。如果INSERT、UPDATE或DELETE语句能够执行,则相关
的触发器也能执行。
应该用触发器来保证数据的一致性(大小写、格式等)。在触发器
中执行这种类型的处理的优点是它总是进行这种处理,而且是透
明地进行,与客户机应用无关。
触发器的一种非常有意义的使用是创建审计跟踪。使用触发器,
把更改(如果需要,甚至还有之前和之后的状态)记录到另一个
表非常容易。
遗憾的是,MySQL触发器中不支持CALL语句。这表示不能从触发
器内调用存储过程。所需的存储过程代码需要复制到触发器内。
19.事务处理:innodb支持事务处理。事务处理是对数据的一组操作,使用事务处理可以保证这些操作要么全执行,要么不执行,维持了库的结构(发生错误时撤销前面的操作)。在事务处理内也可以设置savepoint,进行部分回退。
Start transaction标识事务开始,rollback表示回退至start(Create和drop操作不能撤销)。Commit表示确认没问题就执行。Savepoint 保存点名,rollback to 保存点名 可以撤销至保存点。见p206
Set autocommit=0;,则写完一组语句后不会自动提交。自动提交是对每个连接而不是服务器。
20.字符集、编码和校对(校对指规定字符如何比较的指令):show character set; 可以查看mysql支持的字符集。Show collation显示mysql支持的校对,一个字符集可能有不止一种校对,比如支不支持大小写。创建表时设置表的字符集和校对:create table ....) default character set 字符集 collate 校对;,还可以对某列指定要用的字符集和校对。
如果要更改,可以用cast()和convert()函数。
进入库后,用show variables like “character%”或”collation%”来显示使用的字符集和校对。
20.数据库安全:给每个用户合适的访问权限。进行访问控制需要创建和管理账号。一般来说不要使用root账号,除非万不得已。在名为mysql的库中有user表,其中一列名为user,存储了用户名。
Create user 用户名 identified by ‘密码’; 创建用户并给出密码。Grant也可以创建用户,理论上还可以直接更改user表来创建用户,不过不推荐。
Rename user 用户名 to 新用户名;
Drop user 用户名;
新创建的用户没有权限。用show grants for 用户名; 显示用户权限。用户名@%,%表示默认的主机名。*.*,前面为库名,后面为表名。
为设置权限,使用GRANT语句。GRANT要求你至少给出以下信息:
要授予的权限;
被授予访问权限的数据库或表;
用户名。
以下例子给出GRANT的用法:
Grant 权限名 on 库名.表名 to 用户名 ,比如select查询权限。相应的revoke可以撤销权限。多个权限用,分隔。
使用grant和revoke时,用户必须存在,但表和库不一定。而且删除表和库后,相关权限仍然存在,重建表/库后也存在,需要注意。
Set password = password(‘新密码’) 可以为当前用户更新密码,或者set password for 用户名为指定用户改密码。
21.数据库维护:
备份数据: 使用命令行实用程序mysqldump转储所有数据库内容到某个外部
文件。在进行常规备份前这个实用程序应该正常运行,以便能正
确地备份转储文件。
可用命令行实用程序mysqlhotcopy从一个数据库复制所有数据
(并非所有数据库引擎都支持这个实用程序)。 可以使用MySQL的BACKUP TABLE或SELECT INTO OUTFILE转储所
有数据到某个外部文件。这两条语句都接受将要创建的系统文件
名,此系统文件必须不存在,否则会出错。数据可以用RESTORE
TABLE来复原。
为了保证所有数据被写到磁盘(包括索引
数据),可能需要在进行备份前使用FLUSH TABLES语句。
一些确保库正常运行的语句:
Analyze table 表名;
CHECK TABLE用来针对许多问题对表进行检查。在MyISAM表上还对
索引进行检查。CHECK TABLE支持一系列的用于MyISAM表的方式。
CHANGED检查自最后一次检查以来改动过的表。EXTENDED执行最
彻底的检查,FAST只检查未正常关闭的表,MEDIUM检查所有被删
除的链接并进行键检验,QUICK只进行快速扫描。如下所示,CHECK
TABLE发现和修复问题:
可以用repair table来修复myisam表,但若经常使用,表示有更大的问题;
从表中删除大量数据后,应该用optimize table收回空间。
在排除系统启动问题时,首先应该尽量用手动启动服务器。MySQL
服务器自身通过在命令行上执行mysqld启动。下面是几个重要的mysqld
命令行选项:
--help显示帮助——一个选项列表;
--safe-mode装载减去某些最佳配置的服务器;
--verbose显示全文本消息(为获得更详细的帮助消息与--help
联合使用); --version显示版本信息然后退出。
几个另外的命令行选项(与日志文件的使用有关)在下一节列出。
29.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命令行选项更改。
在使用日志时,可用FLUSH LOGS语句来刷新和重新开始所有日志文
件。
22.优化数据库:
数据库管理员把他们生命中的相当一部份时间花在了调整、试验以
改善DBMS性能之上。在诊断应用的滞缓现象和性能问题时,性能不良的
数据库(以及数据库查询)通常是最常见的祸因。
可以看出,下面的内容并不能完全决定MySQL的性能。我们只是
想回顾一下前面各章的重点,提供进行性能优化探讨和分析的一个出
发点。
首先,MySQL(与所有DBMS一样)具有特定的硬件建议。在学
习和研究MySQL时,使用任何旧的计算机作为服务器都可以。但
对用于生产的服务器来说,应该坚持遵循这些硬件建议。
一般来说,关键的生产DBMS应该运行在自己的专用服务器上。
MySQL是用一系列的默认设置预先配置的,从这些设置开始通常
是很好的。但过一段时间后你可能需要调整内存分配、缓冲区大
小等。(为查看当前设置,可使用SHOW VARIABLES;和SHOW
STATUS;。)
MySQL是一个多用户多线程的DBMS,换言之,它经常同时执行多
个任务。如果这些任务中的某一个执行缓慢,则所有请求都会执
行缓慢。如果你遇到显著的性能不良,可使用SHOW PROCESSLIST
显示所有活动进程 ( 以及它们的线程ID和执行时间 ) 。 你还可以用
KILL命令终结某个特定的进程(使用这个命令需要作为管理员登
录)。 总是有不止一种方法编写同一条SELECT语句。应该试验联结、并、
子查询等,找出最佳的方法。
使用EXPLAIN语句让MySQL解释它将如何执行一条SELECT语句。
一般来说,存储过程执行得比一条一条地执行其中的各条MySQL
语句快。
应该总是使用正确的数据类型。
决不要检索比需求还要多的数据。换言之,不要用SELECT *(除
非你真正需要每个列)。 有的操作(包括INSERT)支持一个可选的DELAYED关键字,如果
使用它,将把控制立即返回给调用程序,并且一旦有可能就实际
执行该操作。
在导入数据时,应该关闭自动提交。你可能还想删除索引(包括
FULLTEXT索引),然后在导入完成后再重建它们。
必须索引数据库表以改善数据检索的性能。确定索引什么不是一
件微不足道的任务,需要分析使用的SELECT语句以找出重复的
WHERE和ORDER BY子句。如果一个简单的WHERE子句返回结果所花
的时间太长,则可以断定其中使用的列(或几个列)就是需要索
引的对象。
你的SELECT语句中有一系列复杂的OR条件吗?通过使用多条
SELECT语句和连接它们的UNION语句,你能看到极大的性能改
进。
索引改善数据检索的性能,但损害数据插入、删除和更新的性能。
如果你有一些表,它们收集数据且不经常被搜索,则在有必要之
前不要索引它们。(索引可根据需要添加和删除。) LIKE很慢。一般来说,最好是使用FULLTEXT而不是LIKE。 数据库是不断变化的实体。一组优化良好的表一会儿后可能就面
目全非了。由于表的使用和内容的更改,理想的优化和配置也会
改变。
最重要的规则就是,每条规则在某些条件下都会被打破。