MySQL基礎(chǔ)SQL優(yōu)化之查詢性能提升的4大技巧
引言
在MySQL數(shù)據(jù)庫(kù)開發(fā)與管理過(guò)程中,SQL語(yǔ)句的性能至關(guān)重要。即使是基礎(chǔ)的SQL語(yǔ)句,通過(guò)合理優(yōu)化也能顯著提升查詢效率,減少系統(tǒng)資源消耗。本文將圍繞避免使用SELECT *、合理使用WHERE條件、優(yōu)化ORDER BY和GROUP BY等常見基礎(chǔ)SQL優(yōu)化方法,結(jié)合實(shí)際案例,深入分析優(yōu)化前后的性能差異,助力開發(fā)者寫出高效的SQL語(yǔ)句。
一、避免使用SELECT *
在編寫SQL查詢語(yǔ)句時(shí),許多開發(fā)者習(xí)慣使用SELECT *,這種寫法雖然方便快捷,但在實(shí)際應(yīng)用中存在諸多性能問(wèn)題。
1.1 問(wèn)題分析
SELECT *會(huì)查詢出表中的所有列,這可能導(dǎo)致不必要的數(shù)據(jù)傳輸和內(nèi)存消耗。如果表結(jié)構(gòu)復(fù)雜,包含大量列,尤其是一些大字段(如文本、二進(jìn)制數(shù)據(jù)),查詢結(jié)果集將變得龐大,占用更多的網(wǎng)絡(luò)帶寬和內(nèi)存空間,進(jìn)而影響查詢性能。此外,當(dāng)表結(jié)構(gòu)發(fā)生變化(新增或刪除列)時(shí),使用SELECT *的查詢可能會(huì)受到影響,導(dǎo)致應(yīng)用程序出現(xiàn)異常。
1.2 優(yōu)化方法
明確所需列,只查詢實(shí)際需要的列。例如,有一個(gè)employees表,包含employee_id、first_name、last_name、department_id、salary、hire_date等列,若我們僅需查詢員工的姓名和部門ID,正確的寫法如下:
-- 僅查詢員工姓名和部門ID SELECT first_name, last_name, department_id FROM employees;
1.3 性能對(duì)比案例
為了直觀展示使用SELECT *和指定列查詢的性能差異,我們創(chuàng)建一個(gè)測(cè)試表并插入大量數(shù)據(jù)。
-- 創(chuàng)建測(cè)試表 CREATE TABLE test_table ( id INT AUTO_INCREMENT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100), col3 VARCHAR(100), col4 VARCHAR(100), col5 VARCHAR(100), col6 VARCHAR(100), col7 VARCHAR(100), col8 VARCHAR(100), col9 VARCHAR(100), col10 VARCHAR(100) ); -- 插入10萬(wàn)條測(cè)試數(shù)據(jù) DELIMITER // CREATE PROCEDURE insert_test_data() BEGIN DECLARE i INT DEFAULT 1; WHILE i <= 100000 DO INSERT INTO test_table (col1, col2, col3, col4, col5, col6, col7, col8, col9, col10) VALUES (CONCAT('value', i), CONCAT('value', i), CONCAT('value', i), CONCAT('value', i), CONCAT('value', i), CONCAT('value', i), CONCAT('value', i), CONCAT('value', i), CONCAT('value', i), CONCAT('value', i)); SET i = i + 1; END WHILE; END // DELIMITER ; CALL insert_test_data();
接下來(lái),分別執(zhí)行SELECT *和指定列的查詢,并記錄執(zhí)行時(shí)間。
-- 使用SELECT *查詢 SELECT * FROM test_table; -- 指定列查詢 SELECT id, col1, col2 FROM test_table;
通過(guò)實(shí)際測(cè)試發(fā)現(xiàn),在相同的測(cè)試環(huán)境下,SELECT *的查詢耗時(shí)明顯高于指定列查詢。在一個(gè)擁有眾多列的實(shí)際業(yè)務(wù)表中,這種性能差異可能會(huì)更加顯著。
二、合理使用WHERE條件
WHERE子句用于篩選滿足特定條件的行,合理使用WHERE條件能大幅減少查詢返回的數(shù)據(jù)量,從而提升查詢性能。
2.1 問(wèn)題分析
如果WHERE條件書寫不當(dāng),可能導(dǎo)致MySQL無(wú)法有效利用索引,進(jìn)行全表掃描。例如,在WHERE條件中對(duì)列使用函數(shù)、表達(dá)式,或者使用不當(dāng)?shù)谋容^運(yùn)算符,都可能使索引失效。另外,模糊查詢LIKE如果使用不當(dāng),也會(huì)影響性能。
2.2 優(yōu)化方法
避免在列上使用函數(shù)或表達(dá)式:將函數(shù)或表達(dá)式應(yīng)用到查詢條件的值上,而不是列上。例如,查詢某個(gè)日期之后的記錄,正確的寫法如下:
-- 錯(cuò)誤寫法,索引可能失效 SELECT * FROM orders WHERE YEAR(order_date) = 2024; -- 正確寫法,使用索引 SELECT * FROM orders WHERE order_date >= '2024-01-01';
合理使用LIKE:如果使用LIKE進(jìn)行模糊查詢,避免以通配符開頭(如LIKE '%value'),因?yàn)檫@種方式無(wú)法使用索引。若確實(shí)需要以通配符開頭的查詢,可以考慮使用全文索引。例如:
-- 錯(cuò)誤寫法,全表掃描 SELECT * FROM products WHERE product_name LIKE '%book'; -- 正確寫法,使用索引 SELECT * FROM products WHERE product_name LIKE 'book%';
使用覆蓋索引:盡量讓W(xué)HERE條件中的列包含在索引中,這樣MySQL可以直接從索引中獲取數(shù)據(jù),無(wú)需回表查詢。例如,有一個(gè)customers表,包含customer_id、customer_name、email等列,并且在email列上創(chuàng)建了索引。如果查詢條件為WHERE email = 'example@example.com',則可以利用該索引快速定位數(shù)據(jù)。
2.3 性能對(duì)比案例
我們以一個(gè)students表為例,表中包含student_id、student_name、age、gender、score等列,并在age列上創(chuàng)建了索引。
-- 創(chuàng)建students表 CREATE TABLE students ( student_id INT AUTO_INCREMENT PRIMARY KEY, student_name VARCHAR(50), age INT, gender ENUM('male', 'female'), score DECIMAL(5, 2) ); -- 在age列上創(chuàng)建索引 CREATE INDEX idx_age ON students(age); -- 插入測(cè)試數(shù)據(jù) INSERT INTO students (student_name, age, gender, score) VALUES ('Alice', 18, 'female', 85.5), ('Bob', 19, 'male', 90.0), ('Charlie', 18, 'male', 88.0);
首先執(zhí)行一個(gè)可能導(dǎo)致索引失效的查詢:
-- 索引可能失效的查詢 SELECT * FROM students WHERE age + 1 = 19;
然后執(zhí)行優(yōu)化后的查詢:
-- 優(yōu)化后的查詢 SELECT * FROM students WHERE age = 18;
通過(guò)執(zhí)行計(jì)劃分析可以發(fā)現(xiàn),第一個(gè)查詢由于在age列上使用了表達(dá)式,導(dǎo)致索引無(wú)法使用,進(jìn)行了全表掃描;而第二個(gè)查詢成功利用了age列的索引,查詢效率大幅提升。
三、優(yōu)化ORDER BY
ORDER BY子句用于對(duì)查詢結(jié)果進(jìn)行排序,優(yōu)化ORDER BY可以提高排序效率,減少資源消耗。
3.1 問(wèn)題分析
如果ORDER BY使用不當(dāng),可能會(huì)導(dǎo)致文件排序(FileSort),這是一種比較消耗資源的操作。當(dāng)ORDER BY的列沒(méi)有使用索引,或者排序順序與索引順序不一致時(shí),就可能觸發(fā)文件排序。此外,如果排序的數(shù)據(jù)量過(guò)大,還可能導(dǎo)致臨時(shí)表的創(chuàng)建和內(nèi)存不足等問(wèn)題。
3.2 優(yōu)化方法
確保ORDER BY的列使用索引:創(chuàng)建包含ORDER BY列的合適索引,并且索引的順序應(yīng)與排序順序一致。例如,查詢按order_date升序排列的訂單記錄,并且需要篩選出特定用戶的訂單,可以創(chuàng)建一個(gè)復(fù)合索引。
-- 創(chuàng)建復(fù)合索引 CREATE INDEX idx_user_order_date ON orders(user_id, order_date); -- 查詢語(yǔ)句 SELECT * FROM orders WHERE user_id = 123 ORDER BY order_date;
避免不必要的排序:如果查詢結(jié)果不需要排序,就不要使用ORDER BY子句。因?yàn)榕判虿僮鲿?huì)消耗額外的CPU和內(nèi)存資源。
3.3 性能對(duì)比案例
我們以orders表為例,表中包含order_id、user_id、order_date、total_amount等列。
-- 創(chuàng)建orders表 CREATE TABLE orders ( order_id INT AUTO_INCREMENT PRIMARY KEY, user_id INT, order_date DATETIME, total_amount DECIMAL(10, 2) ); -- 插入測(cè)試數(shù)據(jù) INSERT INTO orders (user_id, order_date, total_amount) VALUES (1, '2024-01-01 10:00:00', 100.00), (1, '2024-01-02 11:00:00', 120.00), (2, '2024-01-01 12:00:00', 80.00);
首先執(zhí)行一個(gè)沒(méi)有合適索引的ORDER BY查詢:
-- 沒(méi)有合適索引的ORDER BY查詢 SELECT * FROM orders WHERE user_id = 1 ORDER BY order_date;
通過(guò)執(zhí)行計(jì)劃可以看到,該查詢觸發(fā)了文件排序。然后創(chuàng)建合適的索引并再次執(zhí)行查詢:
-- 創(chuàng)建索引 CREATE INDEX idx_user_order_date ON orders(user_id, order_date); -- 執(zhí)行查詢 SELECT * FROM orders WHERE user_id = 1 ORDER BY order_date;
這次查詢成功利用了索引,避免了文件排序,查詢性能得到顯著提升。
四、優(yōu)化GROUP BY
GROUP BY子句用于對(duì)數(shù)據(jù)進(jìn)行分組統(tǒng)計(jì),優(yōu)化GROUP BY可以提高分組聚合的效率。
4.1 問(wèn)題分析
與ORDER BY類似,不當(dāng)?shù)?span>GROUP BY使用也可能導(dǎo)致文件排序和臨時(shí)表的創(chuàng)建。如果GROUP BY的列沒(méi)有合適的索引,或者分組字段與查詢的其他條件不匹配,就可能影響性能。
4.2 優(yōu)化方法
創(chuàng)建合適的索引:在GROUP BY的列上創(chuàng)建索引,有助于快速分組數(shù)據(jù)。例如,對(duì)products表按category_id進(jìn)行分組統(tǒng)計(jì),可以在category_id列上創(chuàng)建索引。
-- 創(chuàng)建索引 CREATE INDEX idx_category_id ON products(category_id); -- 查詢語(yǔ)句 SELECT category_id, COUNT(*) FROM products GROUP BY category_id;
避免不必要的分組:如果不需要對(duì)數(shù)據(jù)進(jìn)行分組統(tǒng)計(jì),就不要使用GROUP BY子句,減少不必要的計(jì)算。
4.3 性能對(duì)比案例
我們以sales表為例,表中包含sale_id、product_id、quantity、price等列。
-- 創(chuàng)建sales表 CREATE TABLE sales ( sale_id INT AUTO_INCREMENT PRIMARY KEY, product_id INT, quantity INT, price DECIMAL(10, 2) ); -- 插入測(cè)試數(shù)據(jù) INSERT INTO sales (product_id, quantity, price) VALUES (1, 5, 10.00), (1, 3, 12.00), (2, 2, 8.00);
首先執(zhí)行一個(gè)沒(méi)有索引的GROUP BY查詢:
-- 沒(méi)有索引的GROUP BY查詢 SELECT product_id, SUM(quantity) FROM sales GROUP BY product_id;
通過(guò)執(zhí)行計(jì)劃可知,該查詢觸發(fā)了文件排序。然后在product_id列上創(chuàng)建索引并再次執(zhí)行查詢:
-- 創(chuàng)建索引 CREATE INDEX idx_product_id ON sales(product_id); -- 執(zhí)行查詢 SELECT product_id, SUM(quantity) FROM sales GROUP BY product_id;
這次查詢利用索引完成分組操作,避免了文件排序,查詢性能得到明顯改善。
五、總結(jié)
通過(guò)以上對(duì)避免使用SELECT *、合理使用WHERE條件、優(yōu)化ORDER BY和GROUP BY等基礎(chǔ)SQL優(yōu)化方法的介紹及案例演示,可以看出,即使是基礎(chǔ)的SQL語(yǔ)句,通過(guò)合理優(yōu)化也能在性能上有顯著提升。在實(shí)際開發(fā)中,開發(fā)者應(yīng)深入理解這些優(yōu)化技巧,并結(jié)合具體業(yè)務(wù)場(chǎng)景靈活運(yùn)用,以打造高效的數(shù)據(jù)庫(kù)應(yīng)用。
到此這篇關(guān)于MySQL基礎(chǔ)SQL優(yōu)化之查詢性能提升的4大技巧的文章就介紹到這了,更多相關(guān)MySQL SQL查詢優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql8.0.0 winx64.zip解壓版安裝配置教程
這篇文章主要為大家詳細(xì)介紹了mysql8.0.0 winx64.zip解壓版安裝配置教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05mysql中Table is read only的解決方法小結(jié)
本文章總結(jié)了關(guān)于在linux與windows中 mysql出現(xiàn)Table is read only解決辦法總結(jié),有需要的朋友可參考一下2013-01-01mysql中GROUP_CONCAT函數(shù)使用及遇到問(wèn)題詳解
這篇文章主要給大家介紹了關(guān)于mysql中GROUP_CONCAT函數(shù)使用及遇到問(wèn)題的相關(guān)資料,GROUP_CONCAT是用于將分組后的多個(gè)行連接為一個(gè)字符串的聚合函數(shù),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09mysql 8.0.18 壓縮包安裝及忘記密碼重置所遇到的坑
這篇文章主要介紹了mysql 8.0.18 壓縮包安裝及忘記密碼重置所遇到的坑,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12Mysql 遠(yuǎn)程連接配置實(shí)現(xiàn)的兩種方法
這篇文章主要介紹了Mysql 遠(yuǎn)程連接配置實(shí)現(xiàn)的兩種方法的相關(guān)資料,需要的朋友可以參考下2017-07-07