慢查询+SQL语句优化
-
-
- 1.什么是慢查询
- 2.优化慢查询
- 3.插入数据优化
- 5.插入数据底层是什么
- 6.页分裂
- 7.页合并
- 8.主键优化方式
- 10.count 优化
- 11.order by优化
- 12.group by 优化
- 13.limit优化
- 14.update 优化
- 15.innodb 三大特征
1.什么是慢查询
慢查询是指执行SQL查询语句所需要的时间较长,超过了一定阀值,从而导致服务器性能下降。通常情况下,当SQL查询语句执行时间超过1秒以上时就会被任务是慢查询。
慢查询可能导致服务器性能下降,甚至直接导致系统宕机,具体原因有以下几种可能:
- 未使用索引:如果查询没有使用索引或者使用的索引不合理,那么MySQL就不得不全表扫描,这将降低查询速度和对系统资源的占用率。
- 对于大型数据库的操作、尤其是 left join/ left outer join 操作,也可能使查询缓慢甚至崩溃。
- 数据量太大:如果返回的结果集非常大,即时查询本身很快,也很占用大量系统资源,降低服务器性能。这可能导致网络传输泛滥,用户界面延迟,而电脑库存优先,难以处理大量数据。
- 非优化查询语句:写SQL查询时要注意以下亮点:首先,选择需要查询的列;然后,添加 WHERE 条件,能够让 MySQL 快速过滤出所需的记录。如果没有正确配置查询条件,那么查询语句可能会非常耗费时间和资源。
因此,保证 MySQL 的性能和稳定性,优化 SQL 查询是很重要的一步,包括对查询语句、索引和硬件的优化,以预防慢查询问题。
2.优化慢查询
- 使用索引:索引是一种高效的查找数据的方法,如果某个列经常用于查询、排序等操作,可以为该列简历索引来提高查询效率。但是要注意不要过度使用索引,不然可能会降低写入数据的速度。
- 限制结果集大小:如果一个查询返回的结果集非常大,即使查询本身很快,也会对服务器产生负面影响,尤其是在网络传输数据时效率更加明显。可以通过 LIMIT 关键字限制结果集的大小,避免查询超出需求范围的无用数据集。
- 减少连接和数据读取:尽量减少连接和数据读取次数。例如,使用 JOIN 操作可以将多个表合并成一个查询,而不是每个表单独查询。同时,避免使用 select * 这样的语句,只选取需要的字段来减少网络传输的数据量。
- 优化SQL语句:优化查询语句是最直接最有效的方法之一,通过修改 SQL 语句可以达到节约资源,提升效率的目的。可能还需要重组查询策略以确保优化后的查询充分利用索引和其他存储优化策略。
- 分析慢日志:MySQL 在执行某些SQL语句时会讲查询信息记录在 slow log 中,其中包括该查询运行时间、使用的索引和查询方式等。可以通过分析该日志来找出哪些数据库操作比较耗时,在进行针对性优化。需要注意,开启部署缓慢日志不要影响线上性能。
- 优化硬件:如果系统有一定的资金,还可以通过扩大内存、换用更快的CPU和硬盘等方式来提高 MySQL 的性能。这种方法通常是最后采取的,因为往往要花费较高的成本。
综合来看,慢查询优化需要结合实际情况,通过以上措施的一个或多个来解决问题。需要衡量查询的结果集大小、压力、延迟等情况,找到最适合自己数据库服务器的方案。
3.插入数据优化
插入数据优化通常涉及到以下几个方面:
- 批量插入:批量插入是将多条记录一次性插入数据库,这样可以减少与数据库的交互次数,优化插入操作。为此我们可以使用 INSERT INTO … VALUES (value1, value2), (value3, value4), … 等语法格式。
- 事务控制:在数据量较大时,每次执行一条插入语句就要开启一个事务,频繁提交事务的话内存池很可能无法完全对所有等待写入硬盘的数据进行管理,在向数据库提交操作之前,最好开启事务管理机制,它有助于保持数据一致性,同时还能防止记录受并发操作的干扰。
a. 在数据库管理系统中,事务是指一系列的操作被视为单个工作单元并要么全部完成要么全部撤销的操作集合。在插入大量数据时,开启一个事务可以保证所有操作的原子性并提高整个批处理的效率。如果没有事务的支持,当插入数据失败或者发生错误时,需要重新从头开始插入数据,这极大地浪费时间和资源。而使用事务机制可以更好地控制和管理大规模数据的插入,确保数据在系统中完整、一致和可靠,并且可以通过回滚操作来实现数据的恢复和错误处理。因此,在插入大量数据时开始事务是一种常见的最佳实践。
- 使用LOAD DATA进行数据倒入:如果要导入的数据非常庞大时,这种方法可以显著提高导入性能。 LOAD DATA 仅仅是将文件读为对外内存然后直接写入硬盘;相反,INSERT语句首先“(1)从客户端到服务器上读取数据;(2)执行sql,并将数据写入撤离池;(3)用InnoDB日志格式写入到服务器硬盘中”。
- 主键顺序插入可以提高数据库的插入性能,尤其是在使用自增长主键时更加明显。
a. 优点如下:
ⅰ. 减少磁盘IO操作:顺序插入会让新数据紧密地分布在磁盘上,降低表空间内部碎片,提高读写效率。
ⅱ. 减少索引维护开销:数据库在插入数据时需要对数据进行排序并更新相应的索引,而顺序插入则不需要进行额外的操作以维护索引,因此使得插入数据时的开销更小,减少了数据库在维护索引上的负载。
ⅲ. 可降低锁竞争:当多个线程同时往同一个空闲页插入记录时,如果采用主键顺序插入策略,则他们不会发送竞争而导致阻塞,从而提高并发性能。
b. 缺点如下:
ⅰ. 可能会造成页分裂:当一页满了之后,在插入新记录会导致页分裂,可能影响磁盘IO速度和系统性能;
ⅱ. 限制并发性能:如果并发性能很高,多个事务试图同时添加记录,可能会等待其他线程对磁盘表的顺序插入操作完成。
5.插入数据底层是什么
在关系型数据库中,插入操作的底层实现是由数据库系统内部使用各种数据结构和算法来读取、写入和维护数据。具体来说,当执行插入操作时,数据库会首先根据已定义的表结构进行验证,以确保要插入的数据与表结构的约束条件相符。
接下来,系统会为新的数据记录分配空间,并将数据写入到磁盘上的数据库文件中。此时,根据数据库的实现方式,可能涉及到索引、锁、缓存等技术。
其中,索引是用于优化查询性能的重要手段,通过为表的其中一列建立索引,在查询时可以快速地定位到目标记录,从而加速查询效率。插入操作对索引的影响主要是需要对索引进行更新或重建,从而效率较低。因此,在使用索引的情况下,插入操作应该尽可能批量执行。
锁是用于控制数据库并发访问的一种机制,当一个事务对数据进行修改时,需要获得排他锁,防止其他事务同时对同样的数据进行修改。在插入数据时也会加锁,以防止多个插入操作同时运行并导致数据不一致的问题。
缓存则是一种提高数据库I/O效率的手段,通过将热点数据、索引等信息缓存到内存中,可以降低磁盘I/O操作的频率,从而提高系统整体效率。在插入操作时,可能需要将新写入的数据放入缓存中以提高后续查询效率。
总之,数据库的底层实现是一个非常复杂的过程,涉及到许多技术和算法。对于普通用户来说,使用SQL语句进行数据操作已经足够简便,无需深入理解底层实现过程。
6.页分裂
页分裂是指在B树或B+树索引结构中,一次插入操作将导致一个数据页上的数据行数量超过预设值从而需要分裂成两个数据页的过程。当执行插入操作时,如果当前叶子节点已经满了,就需要调整树的结构,使其新增加的数据可以被存储到正确的位置。其中,页面分裂也叫做“节点分裂”。
在B+树中,每个非叶子节点都会包含多个指向子节点的指针,一个块或者一个物理磁盘的大小一般固定不变,而每个页面最大的元素数量是有限的。因此,在节点的插入过程中,可能会造成某个叶子节点元素数量达到节点大小上限,无法再容纳新的元素。
为了应对这种情况,数据库系统会执行一个页面分裂操作:即把当前叶子节点按照一定规则分裂成两个节点。在分裂的过程中,原来的节点和新节点之间以某种方式建立联系,使得新插入的元素能够正确地插入到适当的位置。同时,由于不断更新、删除数据的同时,B+树索引的数据页数目也可以动态合并或拆分,以保持高效率的访问性能
需要注意的是,节点分裂过多会导致磁盘空间利用率降低,同时频繁的页面分裂也会影响查询性能。因此,在建立B+树索引时应尽量预先确定要存储的数据大小并合理地设置树的节点大小,以减少页面分裂的次数。
主键顺序插入会导致页分裂吗
主键顺序插入可能会导致页分裂,尤其是在自增主键的情况下。原因是,在主键顺序插入的情况下,在自增主键的情况下,新插入的数据总是会在当前页的最后一行,这就会导致当前页被填满,无法再插入新数据而发生页面分裂。
例如,当使用MySQL的InnoDB引擎创建一个自增主键时,系统将按照顺序将数据插入到B+树中,每个新记录将被插入到当前页的末尾。如果当前页已经不能再容纳多个新条目,那么InnoDB会执行一个页面分裂,即创建一个新的数据页并重新输出前一页和新的数据条目。
为避免频繁的页面分裂,可以采用以下几种方法:
-
合理设置每个页面存储的数据数量上限。此侧适用于不同类型的数据库、表等,主要是要避免页面大小过小或者过大。
-
使用B+树索引方式,避免B-树索引的扩张和压缩操作。
-
以任意顺序插入新记录。这样,新条目将被嵌入到当前页和相邻的页面之间,并且不太可能出现绕过它们的空间浪费状况,并导致页面分裂。
总之,在设计主键时,需要综合考虑数据存储、访问性能等因素,并进行适当的设置和优化以避免出现不必要的页面分裂情况。
ⅰ. 可能会造成页分裂:当一页满了之后,在插入新记录会导致页分裂,可能影响磁盘IO速度和系统性能;
- 在MySQL数据库中,数据存储和访问的单位为「页面」,每个页面通常包含多行数据记录。当我们进行插入或更新操作时,如果新插入或更新的数据所在的页面以及满了,就需要将该页面拆分成两个更小的页面,并且将之后的所有数据都向后移动,从而保证所有数据仍然有序地存储在磁盘上。当按照主键顺序插入大量数据时,由于所有数据都是按照相同的顺序逐渐填充到磁盘的几个连续块中,这些连续块就会容易变得非常大。当数据块变得越来越大时,就会导致频繁的分裂时间发生,即新的数据无法应用到已经存在的块中,它们只能被加入到最后一个块中,导致不断生成新的空闲页和页分裂,从而减缓系统的响应速度。因此,在实际的数据库设计和应用中,我们需要根据不同的存储引擎、数据量和访问模式等因素来选择最合适的主键方案和数据分布方式,以保证系统能够获得优秀的性能和稳定性。
- 为啥插入的页面满了就要分裂:
a. 数据库中采用的是B+树索引结构来存储数据,而基于磁盘存储的B+树索引结构将整个索引树分成了多层,并且每一层都划分成多个页面进行存储。其中叶子节点是实际存储数据记录的节点。
b. 当我们往数据库表中插入新的记录时,如果所要插入的页面已经满了,就需要将该页面拆分成两个更小的页面,并且将之后的所有数据都向后移动,从而保证所有数据仍然有序地存储在磁盘上。
c. 因为每一个页面都是固定大小的,如果不对已满的页面进行分裂操作,新的记录就没有办法被存储到磁盘中。同时,如果只是简单地添加新的页面,则会使得查询效率下降,因为每次查找都需要遍历整个表。因此,对于已满的页面,分裂操作是必须执行的。
d. 在分裂一个页面时,数据库系统通常会选取被分裂节点的中间位置,将该位置前面的记录划分到左边的页面中,将该位置后面的记录划分到右边的页面中,从而使每个页面中的数据量大致相等。这样可以保证索引的平衡性和查询效率,同时也能够防止数据重复和数据丢失等问题的发生。
e. 总之,磁盘中对应B+树的叶子节点已满时,为保证新记录能被存储到磁盘,并提高索引查询效率,需要对该节点进行分裂操作。
- 页面不是固定大小吗,为啥可以拆分成两个更小的页面?
a. 在数据库中,为了提高磁盘I/O的效率并减少空间的浪费,通常会将数据划分成大小相等的页面进行存储。但是,在实际运行过程中,我们往往无法预知每个页面存储的具体记录数量和字节数量,因此可能会出现页面存储的数据量超出页面的容量限制。
b. 当一个页面存储满了数据后,如果想要再往该页面中添加新的数据,就必须将已有的数据删除或移动到其他页面中,腾出一部分空间以便存放新的数据。由于B+树索引结构的特点,为了保持叶子节点之间都是有序的,常见的做法是将该页的中间位置处的数据移到新创建的页面(右兄弟节点)。这个过程称之为页面“分裂”。
c. 例如,假设有一个包含4个记录的页面,且每个记录占用20个字节,而该页面只能存放80个字节的数据,即可以存放4个记录。如果我们向该页面插入第5条记录时,则需要先将原来的4号记录移动到一个新页面上,然后将新的记录插入到原来的页面中,最后更新父节点的索引信息。
d. 换句话说,虽然页面大小是固定的,但页面中存储的数据量却可能不稳定,当页面存储的数据量超出页面的容量限制时,就需要将这些数据进行合并或者分裂操作。此外,如果某些页面空间利用率很低,系统也可能会在磁盘上将多个相邻的页面进行“合并”,从而形成更大的页面,减少对磁盘I/O资源的消耗。
- 那分裂的两个页面怎么链接呢?
a. 在数据库中采用的B+树索引结构中,节点之间通过指针(或者称为“地址”)互相链接起来,形成一个链表。当一个页面进行分裂操作时,系统通常会选取原页面的中间位置,将该位置前面的数据分配到左半部分的页面中,将该位置后面的数据分配到右半部分的页面中。然后,这两个页面之间需要设置相应的指针信息,才能维护树形结构的层次关系和有序性。
b. 具体来说,在B+树的叶子节点中,每个节点都包含指向上一级节点和下一级节点的指针,从而保证节点之间不会因为分裂或合并等操作而失去联系。这些指针信息记录在父节点和兄弟节点中,以便查询和更新时可以快速定位到所需的节点。
c. 例如,在对叶子节点进行分裂操作时,可以将数据划分为两个集合,并且设置相应的指针信息使得它们能够连成一个链表。如果已经存在兄弟节点,则需要将其指向新创建的左半部分节点。如果没有兄弟节点,则需要将父节点中与原页面连接的指针同时指向新创建的两个节点,并将这两个节点用指向彼此的指针互相链接。
d. 图示例:
±----------+ ±----------+
| Page 1 | | Page 3 |
±----------+ ±----------+
| Key1,Data1 | ==> | Key4,Data4 |
| Key2,Data2 | | Key5,Data5 |
| Key3,Data3 | | Key6,Data6 |
±----------+ ±----------+
(left sibling) (split) (right sibling)
如上图所示,在进行分裂操作时,原叶子节点被分成了两个大小相等的部分(左半部分为Page 1,右半部分为Page 3),这两个页面之间通过指针信息进行连接。左边的页面与原来的左兄弟节点相连,右边的页面与右兄弟节点相连,而它们本身则通过自己的指针信息进行连接。
总之,在B+树的节点分裂或者合并操作中,节点内部元素进行重新排列后,必须更新各个受影响节点的指针信息,使之能够通过树的往下向叶子节点方向查找、遍历和修改数据。同时,这些指针信息的更改也需要被及时地保存到磁盘上,以确保系统崩溃或异常关闭时可以恢复到操作前的状态。
7.页合并
在B+树索引结构中,页合并是指将树上相邻的两个部分合并为一个部分的操作。当节点的数据行数量下降到可接受范围之下时,可以考虑触发页合并操作来减少磁盘占用和提高查询效率。
一般情况下,在B+树索引结构中,页合并有两种可能的场景:
- 删除操作:在删除某些数据行后,当前叶子节点上可能会存在大量空余位置,如果过多的空间浪费会降低查询效率。当节点空余空间超过一定比例时(如50%),则可以通过执行页合并操作,将其与相邻的兄弟节点合并以释放掉不必要的磁盘空间。
- 树平衡:在插入新记录时,如果当前B+树索引结构非常不平衡,即某些叶子节点比其它节点包含的数据行数量更多,就可能需要执行页面合并操作。当存在相邻两个节点存储数据行数很小的情况下,并且使用合并后的新节点比原来的节点更加紧密地包括所有数据行,则可以合并这两个节点以减少访问节点的数量,提高查询效率。
需要注意的是,频繁的页合并操作也会影响查询性能,因为合并过程需要消耗大量的CPU时间和磁盘I/O操作。因此,在数据库索引设计时,应注意调整合理的索引树结构、策略等,以避免不必要的页合并,并优化页合并的效率和性能。
8.主键优化方式
主键是数据库中非常重要的一个概念,它被用于标识数据表中的每一行记录,通常是一个唯一的标识符。在设计数据库时,主键是需要进行优化的一个方面。
- 选择适当的数据类型:主键应该选择适当的数据类型来节省存储空间和查询开销。例如,整型主键通常比字符主键更快。
- 索引优化:创建主键索引可以提高查询效率。考虑到查询操作比比插入操作更加频繁,因此可以使用聚集索引或者非聚集索引的方式创建主键索引以加快查询速度。
- 自增主键:使用自增主键可以避免主键值冲突和手动维护主键值的麻烦。自增主键能够自动生成不重复、连续、有序的主键字段值。
- 复合主键:在某些情况下,并非所有列都适合作为单独的主键,存在多列组合形成主键的复合主键,可优化查询效率。
- 避免使用UUID作为主键:UUID有许多优点,但作为数据库中的主键,它会降低查询效率。因为UUID是随机生成的,在数据库中使用可能导致数据散布在不同的磁盘块上。
- 业务操作时,避免对主键进行修改。
数据组织方式:在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为「索引组织表」
10.count 优化
select count(*) 和 select count(1) 的区别
SELECT COUNT(*)
和 SELECT COUNT(1)
的执行结果是相同的,都会返回查询结果集中的行数。
COUNT(*)
和 COUNT(1)
的区别在于它们的实现方式。COUNT(*)
是统计所有列的行数,需要把数据一行一行地从引擎里面读出来,包括NULL值和重复的值,然后累计,因此效率比较慢;而 COUNT(1)
只是统计结果集的行数,但不取值,因为SQL引擎发现有一个常量值无需返回,所以稍微快一点 。
一般来说,如果只是查询结果集的行数,建议使用 SELECT COUNT(1)
,可以提高一定的查询性能。
count(字段):一行一行的检查该字段的值,如果该值为NULL,则不计数
在数据库中,count() 函数常常用于计算表格中符合条件的记录数。当数据量很大时,count() 操作会非常耗时,影响整个系统的效率和稳定性。为此,可以进行以下一些优化:
- 使用索引:尽量在查询语句中包含 WHERE 子句,这样就可以利用索引避免全表扫描,从而减少查询时间。
- 缓存结果集:如果需要频繁使用 count() 相同的操作结果,可以将结果缓存到内存中,这样能够提高查询速度。
- 分析执行计划:通过分析 SQL 的执行计划来发现瓶颈,然后对相关字段进行索引、调整数据类型等操作。
- 分区表:通过对大型表格进行分区,可以提高查询效率,并使 count() 操作只针对特定的数据分区进行处理。
- 使用计数器:对某个事件或操作进行统计并将结果保存在一个单独的表格中,这种方式会更快且不会占用大量资源。
- 异步处理:可以使用异步处理方法,在后台计算统计值并把结果维护在缓存中。
总之,count() 优化是一个比较具有挑战性的工作,应根据实际情况灵活选择适合的优化策略。
count 的几种用法
在 SQL 中,COUNT() 函数用于统计表格中符合特定条件的行数。以下是 COUNT 函数常见的使用方法:
- 统计全部数据行数:这是最基本的用法,示例如下:
SELECT COUNT(*) FROM table_name;
这个查询语句将返回表格 table_name 中所有行的总数。
2. 统计特定字段中不为空(或非 NULL)的行数:在实际应用中,经常需要针对特定的数据列进行计数,如下所示:
SELECT COUNT(column_name) FROM table_name WHERE condition;
这个查询语句将返回具有非空值的特定字段 column_name 的行数,通过添加 WHERE 子句可以进一步筛选包含特定条件的行。
3. 使用 DISTINCT 关键字进行去重统计:若要计算某个列中唯一条目的数量,可以使用 DISTINCT 关键字来确保单个计数,示例如下:
SELECT COUNT(DISTINCT column_name) FROM table_name WHERE condition;
这个查询语句将返回与指定条件匹配且具有唯一值的特定字段 column_name 的行数。
4. 对数据分组计数:查询结果根据 GROUP BY 子句中指定的一个或多个数据列的值进行分组,然后对每个组内的数据行数进行计数,示例如下:
SELECT column_name, COUNT(*) FROM table_name GROUP BY column_name;
这个查询语句将返回表格中按 column_name 分组后的行数统计结果。
总之,COUNT() 函数根据具体需求有多种使用方法,可以根据实际应用选择不同的方式。
11.order by优化
什么是文件排序
MySQL中的文件排序是一种在「无法使用索引进行排序」时的排序方法。当MySQL执行一个ORDER BY语句但不能使用索引时,它会尝试使用该方法。基本上将会创建一个临时文件并将结果集写入该文件,然后再对文件内容进行排序。MySQL可以使用系统内存来优化这个过程,并且可以指定具体的目录来存储临时文件(系统变量tmpdir)。
以下是一些可能导致文件排序的情况:
- 未正确使用索引(索引失效):MySQL 在执行 ORDER BY 和 GROUP BY 操作时,可以使用索引来加速查询。但是,如果没有正确使用索引,则 MySQL 可能会选择使用文件排序来处理数据。
- 大量数据:如果表中存在大量数据,则可能会在执行 ORDER BY 或 GROUP BY 操作时使用文件排序。这是因为内存不足以包含所有数据,MySQL 必须将数据写入磁盘以释放内存。
- 文本数据类型:当使用文本数据类型时,MySQL 可能会选择使用文件排序。这是因为对于文本数据,排序方式往往比较复杂,所需的操作占用内存较大。
如何避免出现文件排序
- 正确使用索引:优化 ORDER BY 或 GROUP BY 操作前,应该考虑要使用的索引并明确指定使用策略。
- 增加系统内存:增加系统内存可以在一定程度上避免使用文件排序。可以尝试增加操作系统分配给 MySQL 的内存或者提高服务器性能以改善查询效率。
- 优化语句:优化查询语句中的 ORDER BY 或 GROUP BY 子句,避免使用复杂计算或文本数据类型,并且将结果集限制到较小的数据量所在的范围内。
创建了一个覆盖索引 index(age,phone),然后执行 select * from table order by phone,age; 依然使用了覆盖索引?
好的,我再结合具体的例子给你详细地解释一下。
假设我们有一张员工表 employees,包含 id、name、age 和 phone 四个字段,同时创建了一个非唯一联合索引 (age, phone)。
如果我们执行以下查询语句:
SELECT * FROM employees ORDER BY phone, age;
MySQL 存储引擎会先扫描联合索引 (age, phone),并以聚簇索引的方式返回所有符合条件的ID。因为使用联合索引,MySQL 不需要回访数据行便能生成有序结果集。这意味着 MySQL 对 ID 做二次查询不是必要的,从而减少了IO操作。
注意,即使查询语句没有显式使用联合索引的存在,MySQL 也会考虑使用它来提高查询效率。
接着,MySQL 会将所有满足查询条件的记录按照联合索引顺序读入到内存中,并根据 ORDER BY 中指定的字段进行排序。在完成排序后,MySQL 会将结果返回给用户。
所以,在这个例子中,尽管查询语句未显式调用联合索引的位置,但 MySQL 仍然使用了联合索引加速查询,并生成了一个有序结果集。
简单来说,使用联合索引可以让数据库能够更快地获取和排序数据,因此在对大数据量表的数据排序时尤其重要。
12.group by 优化
对于 GROUP BY 的优化,可以从以下几个方面入手:
- 确保使用合适的索引:为 GROUP BY 的字段建立索引,能够大幅提升查询速度。特别是在 GROUP BY 列与 WHERE 子句的列相同时需采用覆盖索引,无需回避聚集索引或非覆盖索引,这一点在查询语句设计阶段应该被明确了解。
- 避免重复计算:使用子查询、 WITH AS 模块等方法在源数据表(也就是 GROUP BY 之前)完成筛选、排序和聚合操作后,再进行分组求和等操作。
- 减少数据传输和磁盘 IO:使用EXISTS 子查询代替 IN 子查询, 并使用 EXISTS 加上 limit 1 或 “not exists”“is null” 条件削减不必要的行扫描以及对磁盘 IO 压力的处理。
- 合理配置数据库缓存:通过增加适当的 cache_size 和 tmp_table_size 参数值使 MySQL 建立内部缓存,尽量减少磁盘访问。
- 使用优化器:MySQL 优化器能够根据 SQL 执行计划自动开启 GROUP BY 查询相关的参数来提高性能,例如设置 max_length_for_sort_data 参数、max_sort_length 参数等。
- 减少 GROUP BY 字段数量:尽量减少 GROUP BY 的字段数量,以便减少排序的时间和计算复杂度。
需要注意的是,在进行 GROUP BY 优化时,需要根据具体情况采取不同的方案,并综合考虑 SQL 查询语句本身、数据表结构、索引情况、物理机器配置等多种因素来选择最佳优化策略。
13.limit优化
MySQL 中的 LIMIT 子句用于限制查询结果集中返回的行数。优化 LIMIT 子句的主要目标是减少查询响应时间和提高性能。下面是一些 MySQL 中 LIMIT 优化的方法:
- 使用索引:在查询语句中包含 LIMIT 子句时,MySQL 会执行顺序扫描整个表来返回所需的行。
如果在表上创建了合适的索引,则可以使查询更加快速和可伸缩。
- 分页查询:对于较大的数据集,仅仅使用 LIMIT 来请求完整的结果集不是最优解决方案。可以像分页一样分段查询数据(例如查询1000条记录中的第11-20条)。
- 使用 buffered 查询:buffered 查询将从服务器获取的数据缓存在本地,避免反复向服务器发出请求,可以提高查询效率。
- 避免 ORDER BY:如果在查询语句中不需要使用 ORDER BY 子句,则无需告诉 MySQL 在返回数据之前对其进行排序。
- 指定 fields:有时候我们只需要查询表中某几列,而并非全部列,指定需要的列可以减少查询返回的数据量,优化查询效率。
如果想要查全部字段有想提高效率,可以先使用索引查询出所有的主键字段,然后将结果集和查询的表结合:
select s.* from tb_user s,(select id from tb_user order by id limit 10000000,10) a
where s.id = a.id
总之,针对具体的业务场景和数据库架构,需要灵活选择合适的优化方法。
14.update 优化
在数据库中,UPDATE 语句用于修改表格中的记录,而在数据量较大且同时需要更新的行数也比较多时,UPDATE 操作可能会非常耗时,影响整个系统的效率和稳定性。下面是一些优化 UPDATE 语句的方法:
- 设计适当的索引: 索引可以加快查找和匹配操作的速度。因此,对于需要频繁执行的 UPDATE 查询语句,应该确定哪些列或组合需要创建索引,在 WHERE 子句中使用这些列或组合。 因为update 是写操作,会给锁,如果没有使用索引,那么就会走全表扫描加表级锁,如果此时有其他事务想要修改数据,就会被阻塞。
- 分批次处理: 将要更新的数据分成较小的批次进行,每次只处理部分数据,减少锁表时间,提高并发性能。通常可以根据业务需求、数据分布特点等因素设置每批数据的大小。
- 关闭或减少触发器:如果有触发器或其他事件处理程序与 UPDATE 查询关联,则应考虑关闭它们或者减少它们产生的影响,以最大程度地提高运行速度。
- 启用查询缓存: 数据库查询结果的缓存可以帮助在相同查询重新执行时避免重复扫描和计算。启用查询缓存后,在相同查询被重新执行时,可以从缓存中直接获取结果集。
- 根据实际需求更新表结构: 对于某些数据可能不太重要的字段,可以将它们通过 UPDATE 语句更改为不接受 NULL 值来加快 UPDATE 操作速度。
- 优化查询语句:优化查询语句是提高 SQL 性能最重要的一步。应该对查询使用正确的索引、避免恶意查询等良好习惯进行优化。
总之,UPDATE 优化需要根据实际情况客观分析,并采取适当的措施进行,以确保系统性能和稳定性。
15.innodb 三大特征
InnoDB 是 MySQL 中的一种事务性存储引擎,它的三大特征是:
- 事务支持:InnoDB 支持 ACID 的事务处理,即事务具有原子性、一致性、隔离性和持久性。在事务中,多个操作要么全部执行,要么全部未执行,保证了数据的完整性和可靠性。
- 行级锁定:InnoDB 支持行级锁定,能够保证在并发情况下对数据的访问具有高度的灵活性,可以最大程度地避免不必要的锁等待过程。当数据库在执行查询或更新操作时,只会锁住被查询或更新的那一行数据,其他行数据不会被锁住,从而减少了数据访问瓶颈。
- 外键约束:InnoDB 支持外键约束,用于实现关系型数据库中的数据完整性保护。外键可以保证数据的一致性性和可靠性,防止出现部分数据错误,例如,在一个订单表中,存储着包含客户信息的另一个表,如客户名称、邮寄地址等,倘若没有外键约束,则可能发生客户信息和订单信息不匹配的结果。
综上所述,InnoDB 强调事务、一致性和高效性,通过优化自身的数据管理、锁定机制和约束规则等方面,提高了对于并发处理的支持能力,为 MySQL 数据库的可靠性和稳定性打下了坚实的基础。