简介
*
在项目上线初期,业务数据量相对较少,SQL的执行效率对程序运行效率的影响可能不太明显,因此开发和运维人员可能无法判断SQL对程序的运行效率有多大。但随着时间的积累,业务数据量的增多,SQL的执行效率对程序的运行效率的影响逐渐增大,此时对SQL的优化就很有必要。
* 优化SQL可以使其更有效地使用索引、减少硬盘I/O等,从而提高程序的运行效率。
* 优化SQL可以使其更好地利用缓存,从而降低程序的响应时间。
* 优化SQL可以使其更好地处理大数据量,从而减少程序的运行时间。
* 优化SQL可以使其更符合程序的业务需求,从而提高程序的性能和用户体验。
一、查询SQL尽量不要使用select *,而是具体字段。
1、反例
select * from emp
2、正例
select id,sex,user_name,salary from emp
3、理由
* 使用 * 号是因为使用 * 号会查询出表中所有列的数据,
* 查询所有列可能会导致性能问题,特别是当表中包含大量列时,查询所有列可能会导致查询速度变慢。
* 查询所有列可能会导致数据冗余,特别是当表中包含多个关联表时,查询所有列可能会导致返回大量重复数据。
* 指定需要查询的列可以使查询结果更易于理解和维护,特别是当表中包含大量列时,指定需要查询的列可以使查询结果更加清晰和易于理解。
二、where中使用默认值代替NULL
说明:
为了方便 我就使用* 代替了。
1、反例
select * from emp where salary is not null
2、正例
select * from emp where salary > 0
3、理由
* 提高查询效率:在 WHERE 子句中使用 NULL 值需要进行 IS NULL 或 IS NOT NULL 的判断,这需要额外的计算成本。而使用默认值
0 代替 NULL 值可以使查询条件更加简单明确,避免进行额外的判断,从而提高查询效率。
* 避免 NULL 值带来的问题:NULL 值具有特殊的语义,即无法进行任何比较。因此,在 WHERE 子句中使用 NULL
值可能会导致查询结果不准确或无法执行。使用默认值 0 代替 NULL 值可以避免这种情况,确保查询结果的准确性和可执行性。
* 提高代码可读性和可维护性:使用默认值 0 代替 NULL 值可以使代码更加清晰易懂,提高可读性。同时,在修改查询条件时,使用默认值 0
也可以避免忘记处理 NULL 值的情况,提高代码的可维护性。
三、避免在where子句中使用 or 来连接条件
1、反例
select * from emp where id = 1 or salary = 2500.10
* 使用 or 可能会引起索引失效,从而进行全表扫描
* or 操作符连接的两个条件只要有一个条件成立,就会返回相应的结果集。因此,当使用 where id=1 or
salary=2500.10 进行查询时,如果 id 字段上有索引,但 salary 字段上没有索引,那么查询引擎会优先使用 id 索引进行查询,因为 id
字段上的条件一定成立。但如果要查询的 salary 值不在 id 索引的范围内,那么查询引擎可能需要进行全表扫描或放弃使用索引,导致 id 索引失效。
2、正例
select * from emp where id = 1 union all select * from emp where salary =
2500.10
* 使用 UNION ALL 用于将两个SELECT语句的结果合并在一起,形成一个结果集。
* 与UNION不同的是,UNION ALL不会去除重复的数据行,而是将所有符合条件的数据行都列出来。
* 在 salary 字段上创建索引,这样查询引擎就可以使用两个字段上的索引来优化查询,提高查询性能。
* 使用 UNION ALL 操作符将两个查询结果合并,这样可以避免使用 OR 操作符,从而避免 id 索引失效的问题。
四、尽量使用数值替代字符串类型
1、正例
主键(id):primary key优先使用数值类型int,tinyint。
性别(sex) :0代表女,1代表男,数据库没有布尔类型,mysql推荐使用tinyint。
2、理由
* 引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了,因此数值类型可以大大提高查询和连接的性能。
* 字符串和数字之间的比较需要进行隐式的类型转换,这会导致查询性能的降低。
* 索引的优缺点之一是可以加速表与表之间的连接,特别是在实现数据的参考完整性方面特别有意义。通过使用数值类型,可以在创建索引时提高查询性能和连接性能。
* 在使用分组和排序字句进行数据检索时,数值类型可以显著减少查询中分组和排序的时间。
五、使用varchar代替char
1、反例
`address` char(100) DEFAULT NULL COMMENT '地址'
*
sql中使用char(100)作为数据类型有一些缺点。首先,char(100)会占用更多的磁盘空间,因为每个字符都需要固定长度的存储空间。其次,使用char(100)时,如果存储的字符串长度小于100个字符,会出现浪费存储空间的情况。
*
另外,如果某些字段涉及到文件排序或者基于磁盘的临时表时,使用char(100)可能会消耗更多的内存,因为固定长度的字符型数据在内存中是连续的空间。因此,在使用char(100)时需要考虑这些缺点,并根据实际需求选择更适合的数据类型。
2、正例
`address` varchar(100) DEFAULT NULL COMMENT '地址'
*
存储灵活:相比于固定长度的数据类型,varchar(100)可以存储长度为100的字符串,也可以存储长度小于100的字符串,或者不存储任何字符,因此可以更加灵活地处理数据。
*
内存消耗较小:对于较短的字符串,使用varchar(100)可以减少内存消耗。例如,如果需要存储一个长度为10的字符串,使用固定长度的数据类型需要11个字节的内存空间,而使用varchar(100)只需要2个字节的内存空间。
*
可以避免浪费磁盘空间:如果使用固定长度的数据类型存储长度不足的值,会浪费一些磁盘空间。而使用varchar(100)可以根据实际需要使用或浪费较少的磁盘空间。
*
可以提高查询性能:在某些情况下,使用varchar(100)可以提高查询性能。例如,如果一个表中的某些列经常需要被查询,而这些列使用固定长度的数据类型可能会浪费一些内存空间,从而导致查询性能下降。而使用varchar(100)可以根据实际情况使用更少的内存空间,从而提高查询性能。
六、char与varchar2的区别
1、CHAR的长度是固定的,VARCHAR2的长度是可以变化的。
* 长度固定 vs
长度可变:CHAR的长度是固定的,而VARCHAR2的长度是可以变化的。例如,存储字符串“101”,对于CHAR(10),表示你存储的字符将占10个字节(包括7个空字符),而同样的VARCHAR2(10)则只占用3个字节的长度。
*
存储空间:CHAR比VARCHAR2更节省存储空间,因为CHAR类型在数据库中以空格填充,而VARCHAR2则不会。但是,VARCHAR2在效率上比CHAR稍差一点。
* 性能:CHAR类型在某些方面比VARCHAR2类型稍快,因为它具有固定的长度,因此在处理和搜索数据时,它可以更快地定位和读取。
2、什么时候选择CHAR什么时候选择VARCHAR2
*
存储需求:如果需要存储的字符数据长度固定且长度较小,则使用CHAR更为节省空间。如果数据长度变化范围较大,或者长度较小,则使用VARCHAR2更为节省空间。
*
性能需求:如果应用程序需要快速高效的查询和搜索数据,并且对数据的长度有要求,则使用CHAR可能更合适。如果对性能要求不高,而对数据长度和变化范围有要求,则更适合使用VARCHAR2。
注意: 在使用VARCHAR2时,如果一个列经常被修改,并且每次修改的数据长度不同,这可能会导致Row
Migration(行迁移)现象,这可能会影响数据库性能并增加I/O负担。在这种情况下,使用CHAR可能更好。
CHAR中还会自动补齐空格,因为你INSERT到一个CHAR字段自动补充了空格的,但是SELECT后空格没有删除,因此CHAR类型查询的时候一定要记得使用TRIM函数去除字符串两端的空格。
3、如何使用TRIM
在查询CHAR类型的列时,如果要使用TRIM函数去除字符串两端的空格,可以使用以下语法:
select * from emp where TRIM(text) = 'csdn';
*
emp是要查询的表名,text是要查询的CHAR类型的列名,csdn是要匹配的字符串。TRIM函数将去除csdn中字符串两端的空格,并返回与csdn相等的唯一值。
*
需要注意:使用TRIM函数可能会影响查询性能。如果查询速度非常重要,则可以考虑将CHAR类型的列转换为VARCHAR2类型,因为VARCHAR2类型会自动去除字符串两端的空格。但是,这种方法可能会导致存储空间的浪费。
七、避免在where子句中使用!=或<> 等操作符
1、反例
select * from emp where salary !=2500.10 select * from emp where salary
<>2500.10
2、理由
* 当查询语句中使用 IS NULL 或 IS NOT NULL
运算符进行空值判断时,数据库无法利用索引进行查询,因为索引只是一种用于快速查找数据的数据结构,无法判断数据是否为空。此时,数据库需要遍历整个表来查找符合条件的行,从而导致索引失效。
* 即使在查询语句中使用了 = 或 > 等运算符进行等值或范围查询,但如果查询条件中包含了一个未使用索引的列,也会导致索引失效。
3、举例
select * from emp where user_name ='choudidi' and salary is not null;
* 在这个查询语句中,user_name 列使用了索引,但 salary 列没有使用索引。因此,即使 user_name
列的条件满足,数据库仍然需要遍历整个表来查找符合条件的行,从而导致索引失效。
八、inner join 、left join、right join,优先使用inner join
* 优先使用INNER JOIN的原因是其性能相对更好。INNER JOIN是等值连接,只保留两张表中完全匹配的结果集,而LEFT JOIN和RIGHT
JOIN会返回左表或右表的所有行,即使在另一张表中没有匹配的记录。
* 因此,如果使用LEFT JOIN或RIGHT JOIN,可能会导致返回的数据量较大,从而影响查询性能。
* INNER JOIN将数据集的较小数据驱动,而LEFT JOIN和RIGHT JOIN则将较大的数据集驱动,这也是MySQL优化原则之一。
* 小表驱动大表,小的数据集驱动大的数据集,从而让性能更优。
九、提高group by语句的效率
1、反例
select department, avg(salary) from emp group by department having department
='1' or department = '3';
* 先分组,再过滤
2、正例
select department,avg(salary) from emp where department ='1' or department =
'3' group by department;
*
先过滤,后分组
*
筛选出符合条件的记录:where子句中的条件筛选出了部门为'1'或'3'的员工记录,只对符合条件的记录进行后续计算。
* 按部门分组:group by子句将结果集按照部门进行分组,这样每个部门的数据都会单独计算平均值,避免了将所有记录混合在一起计算平均值的情况。
十、清空表时优先使用truncate
* 速度更快:truncate比delete更快,因为它不会记录在日志中,也不需要在事务日志中为所删除的每行记录一项。
*
占用的资源更少:truncate通过释放存储表数据所用的数据页来删除数据,而不是在事务日志中记录页的释放,而delete语句每次删除一行,并在事务日志中为所删除的每行记录一项。
*
保持表结构及其列、约束、索引等不变:truncate删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果要保留标识计数值,请改用delete。如果要删除表定义及其数据,请使用drop
table语句。
* 不适用于参与了索引视图的表:truncate不能用于参与了索引视图的表,而delete可以。
*
不激活触发器:truncate不会激活触发器,而delete会。truncate不会激活触发器的原因是它不会记录各行的日志删除操作。由于truncate操作不会记录在事务日志中,所以它不会激活delete触发器。
十一、操作delete或者update语句,加limit或使用循环分批次删除
* 降低数据库负载:一次性删除(或更新)大量数据会对数据库性能造成较大的压力,导致其他操作变慢或阻塞。分批次删除可以降低数据库负载,保证其他操作的正常运行。
*
提高数据安全性:删除或更新大量数据时,如果不小心出现错误操作,可能会导致数据丢失或不可恢复。分批次删除可以降低操作风险,即使不小心删除也可以通过binlog日志恢复进行回滚操作。
*
便于代码实现:一次性删除大量数据可能会导致内存溢出或其他异常情况,使用循环分批次删除可以避免这些问题。同时,使用循环分批次删除也可以降低代码复杂度,提高代码可读性和可维护性。
* 锁表:一次性大量删除太多数据,可能造成锁表,会有lock wait timeout exceed的错误,所以建议分批操作。
*
减少锁定时间:删除或更新数据时,数据库会对相关行进行锁定,确保数据一致性。如果一次性删除大量数据,锁定时间会较长,影响其他操作。而分批次删除可以缩短锁定时间,提高并发性能。
1、正例
1、使用limit限制删除数量
delete from emp limit 1000; -- 或 delete from emp where 条件 limit 1000; # 每次删除
1000 行数据 -- 或者 UPDATE emp SET column1 = value1, column2 = value2, ... WHERE
condition LIMIT 1000; # 每次更新 1000 行数据
* 可以在delete或update语句中添加limit关键字,指定每次删除的数据量。列如以上SQL每次删除1000条数据:
2、使用循环分批次删除
CREATE DEFINER = `root` @`localhost` PROCEDURE `P_xiao_jian` () BEGIN DECLARE
i INT DEFAULT 1;#Routine body goes here... START TRANSACTION; SET @counter = 1;
WHILE @counter <= 1000 DO DELETE FROM order_for_goods WHERE order_id >=
@counter; SET @counter = @counter + 1; END WHILE; COMMIT; END
* 定义一个名为 P_xiao_jian 的存储过程,使用 CREATE PROCEDURE 语句创建。
* 定义一个名为 @counter 的整型变量,并初始化为 1。
* 使用 START TRANSACTION 语句开始一个事务。
* 使用 WHILE 循环,循环条件为 @counter 小于等于 1000。
* 在循环体内,使用 DELETE FROM 语句删除 order_for_goods 表中 order_id 大于等于当前循环变量 @counter
的所有记录。
* 循环结束后,使用 COMMIT 语句提交事务。
* 使用 END 语句结束存储过程的定义。
十二、UNION操作符
* UNION操作符用于将两个或多个SELECT语句的结果集合并成一个结果集。 select user_name,salary from emp
union select departmentname from department
* union在进行表连接后会筛选掉重复的记录,所以在表连接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。
* 在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
推荐:采用union all操作符替代union,因为union all操作只是简单的将两个结果合并后就返回。
十三、批量插入性能提升
1、反例
* 多条提交 insert into emp (id,user_name) values(1,'提交1条'); insert into emp
(id,user_name) values(2,'提交2条');
2、正例
* 批量提交 insert into emp (id,user_name) values(1,'提交1条'),(2,'提交2条');
*
当执行INSERT、UPDATE或DELETE语句默认SQL有事务控制,导致每条都需要事务开启和事务提交,而批量处理是一次事务开启和提交,效率提升明显,达到一定量级,效果显著。
十四、表连接不宜太多,索引不宜太多,建议5个以内
1、表连接不宜太多原因
* 关联的表个数越多,编译的时间和开销也就越大。
* 每次关联内存中都生成一个临时表,应该把连接表拆开成较小的几个执行,可读性更高。
* 如果一定需要连接很多表才能得到数据,那么意味着这是个不理想的的设计。
* 可以查看阿里规范手册,其中包含一条建议多表联查三张表以下
*
通常情况下,建议将表连接的数量控制在5个以内,这样可以提高查询的效率和性能。如果需要连接更多的表,可以考虑对查询进行拆分或重构,以减少连接的数量。此外,还可以使用索引、临时表、视图等技术来优化查询性能。
2、索引不宜太多原因
* 增加额外的开销:索引需要维护,包括更新、插入和删除等操作,而这些操作都会增加额外的开销。特别是对于频繁更新的表,索引可能会成为性能瓶颈。
*
占用磁盘空间:索引需要存储在磁盘上,因此会增加磁盘空间的占用。过多的索引可能会使查询变得缓慢,因为数据量越大,查询所花费的时间可能比表里索引的时间还要短,索引可能不会产生优化效果。
* 降低查询效率:索引设计不合理或者缺少索引都会对数据库性能造成不良影响。过多的索引可能会使查询变得复杂,降低查询效率。
十五、避免在索引列上使用内置函数
说明:
在索引列上使用内置函数可能会导致索引失效,从而降低查询性能。这是因为索引是为了快速访问表中数据而创建的,但是内置函数可能会改变索引列中的数据,导致索引无法识别数据。
为了避免在索引列上使用内置函数,可以考虑以下几种方法:
* 优化查询语句:如果必须使用内置函数,可以尝试优化查询语句,以减少函数的使用次数,或者使用其他方法来避免使用内置函数。
* 创建计算列:如果必须使用内置函数,可以创建一个计算列,并在该列上创建索引。这样,内置函数的结果将存储在新列中,而不是原始列中,从而避免影响索引。
* 创建视图:如果必须使用内置函数,可以创建一个视图,并在视图中使用内置函数。然后,在查询中使用视图而不是表,以避免在索引列上使用内置函数。
*
更改数据类型:如果可能,将索引列的数据类型更改为适合内置函数的类型,以避免使用内置函数。例如,如果要在索引列上使用日期函数,可以将数据类型更改为日期类型。
十六、组合索引
说明:
如果查询中排序的顺序与组合索引的列顺序不匹配,那么查询效率可能会下降。这是因为数据库无法利用索引进行排序,而只能使用其他算法进行排序,从而导致查询效率下降。
create index idx_username_tel on employees (deptid,username,createtime);
select username,tel from employees where deptid= 1 and username = 'CSDN' order
by deptid,position,createtime desc;
虽然创建了基于deptid、username和createtime列的索引,但是查询条件中只使用了deptid=1和username=csdn列,没有利用到createtime列。因此,这个查询不能利用到基于createtime列的索引,查询性能可能会受到影响。同时,如果表中的数据量非常大,创建索引可能会对系统的性能产生较大的影响。
十七、复合索引最左特性
说明:
复合索引(也称为联合索引)具有"最左前缀"特性。这意味着,当查询条件中使用了复合索引中的第一个列(最左边的列),则该复合索引可以被使用。如果查询条件中使用了复合索引中的多个列,则该复合索引也可以被使用,但是前提是这些列的顺序必须与复合索引中的顺序相同。
1、创建复合索引
CREATE INDEX idx_name_age ON employees (name, age);
2、假设我们有一个查询,需要按name和age进行排序,可以使用该复合索引(如下)
SELECT * FROM employees WHERE name = 'John' AND age = 25 ORDER BY name, age;
在这个查询中,我们使用了复合索引中的第一个列name和第二个列age。由于这些列的顺序与复合索引中的顺序相同,因此该复合索引可以被使用。
3、然而,如果我们改变了查询条件中列的顺序,或者添加了其他函数或条件,可能会导致复合索引无法被使用(如下)
-- 复合索引无法被使用 SELECT * FROM employees WHERE age = 25 AND name = 'John' ORDER BY
name, age; -- 复合索引无法被使用 SELECT * FROM employees WHERE SUBSTRING(name, 1, 1) =
'J' AND age = 25 ORDER BY name, age;
在第一个查询中,我们改变了查询条件中列的顺序,因此复合索引无法被使用。在第二个查询中,我们添加了一个函数SUBSTRING,这使得复合索引无法被使用。因此,在使用复合索引时,需要注意查询条件中列的顺序,以及是否添加了任何函数或条件。
十八、优化like语句
1、反例
select * from emp where user_name like '%王' (不使用索引) select * from emp where
user_name like '%王%' (不使用索引)
2、正例
select * from emp where user_name like '王%' (使用索引) 。
3、理由
* 使用前缀匹配,当你知道要搜索的字符串具有某个特定的前缀时,可以使用前缀匹配来提高查询效率。例如,如果你知道要搜索的名字都以 "张" 开头,可以使用
LIKE '张%'。
* 首先尽量避免模糊查询,如果必须使用,不采用全模糊查询,也应尽量采用右模糊查询, 即like '...%',是会使用索引的,左模糊like
'%...'无法直接使用索引,但可以利用reverse + function index的形式,变化成 like '...%'。
* 全模糊查询是无法优化的,一定要使用的话建议使用搜索引擎。
reverse + function index的形式如下:
SELECT * FROM emp WHERE LEFT(user_name, 1) LIKE '肖%';
* 这将返回名字以 "肖" 开头的所有名字。在这个语句中,LEFT(user_name, 1) 提取名字左侧的第一个字符,并将其与 '肖%'
进行比较。注意,这里使用了 % 通配符来表示任意字符(包括 0 个字符),因此 '肖%' 表示以 "肖" 开头的任意字符串。
十九、使用explain分析你SQL执行计划
1、type类型
* system:表仅有一行,基本用不到;
* const:表最多一行数据配合,主键查询时触发较多;
* eq_ref:对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型;
* ref:对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取;
* range:只检索给定范围的行,使用一个索引来选择行。当使用=、<>、>、>=、<、<=、IS
NULL、<=>、BETWEEN或者IN操作符,用常量比较关键字列时,可以使用range;
* index:该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小;
* all:全表扫描;
* 性能排名:system > const > eq_ref > ref > range > index > all。
* 实际sql优化中,最后达到ref或range级别。
2、Extra常用关键字
* Using index:只从索引树中获取信息,而不需要回表查询;
* Using where:WHERE子句用于限制哪一个行匹配下一个表或发送到客户。除非你专门从表中索取或检查所有行,如果Extra值不为Using
where并且表联接类型为ALL或index,查询可能会有一些错误。需要回表查询。
* Using temporary:mysql常建一个临时表来容纳结果,典型情况如查询包含可以按不同情况列出列的GROUP BY和ORDER BY子句时;
二十、其他SQL优化方式
1、设计表的时候,所有表和字段都添加相应的注释。
2、SQL书写格式,关键字大小保持一致,使用缩进。
3、修改或删除重要数据前,要先备份。
4、很多时候用 exists 代替 in 是一个好的选择
5、where后面的字段,留意其数据类型的隐式转换。
*
在SQL中,当在WHERE子句中指定条件时,如果条件中的字段类型与查询中指定的字段类型不匹配,SQL可能会进行隐式的类型转换,也称为“隐式转换”或“隐式类型转换”。
*
例如,如果一个字段是一个日期类型,而条件中指定的值是一个字符串类型,SQL可能会尝试将字符串值转换为日期类型。同样,如果一个字段是一个数值类型,而条件中指定的值是一个字符串类型,SQL可能会尝试将字符串值转换为数值类型。
*
这种隐式类型转换在某些情况下可能是有用的,但在其他情况下可能会导致错误的结果。因此,建议在编写查询时始终显式指定要比较的值和字段的类型,以避免出现意外的问题。
如:
SELECT * FROM emp WHERE NAME=12345
* 因为不加单引号时,是字符串跟数字的比较,它们类型不匹配
* MySQL会做隐式的类型转换,把它们转换为数值类型再做比较
6、尽量把所有列定义为NOT NULL
* NOT NULL列更节省空间,NULL列需要一个额外字节作为判断是否为 NULL的标志位。
* NULL列需要注意空指针问题,NULL列在计算和比较的时候,需要注意空指针问题。
7、伪删除设计
8、数据库和表的字符集尽量统一使用UTF8
* 可以避免乱码问题。
* 可以避免,不同字符集比较转换,导致的索引失效问题。
9、select count(*) from table;
* 这样不带任何条件的count会引起全表扫描,并且没有任何业务意义,是一定要杜绝的。
10、避免在where中对字段进行表达式操作
* SQL解析时,如果字段相关的是表达式就进行全表扫描
* 字段干净无表达式,索引生效
11、关于临时表
* 避免频繁创建和删除临时表,以减少系统表资源的消耗;
* 在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log;
* 如果数据量不大,为了缓和系统表的资源,应先create table,然后insert;
* 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除。先 truncate table ,然后 drop table
,这样可以避免系统表的较长时间锁定。
12、索引不适合建在有大量重复数据的字段上,比如性别,排序字段应创建索引
13、去重distinct过滤字段要少
* 带distinct的语句占用cpu时间高于不带distinct的语句
* 当查询很多字段时,如果使用distinct,数据库引擎就会对数据进行比较,过滤掉重复数据
* 然而这个比较、过滤的过程会占用系统资源,如cpu时间
14、尽量避免大事务操作,提高系统并发能力
15、所有表必须使用Innodb存储引擎
*
Innodb「支持事务,支持行级锁,更好的恢复性」,高并发下性能更好,所以呢,没有特殊要求(即Innodb无法满足的功能如:列存储,存储空间数据等)的情况下,所有表必须使用Innodb存储引擎。
16、尽量避免使用游标
* 因为游标的效率较差,如果游标操作的数据超过1万行,那么应该考虑改写。