1. 程式人生 > 其它 >sql優化,原理減少回表操作

sql優化,原理減少回表操作

優化規則:

-- 優化前SQL
SELECT  各種欄位
FROM `table_name`
WHERE 各種條件
LIMIT 0,10;

----->

-- 優化後SQL
SELECT  各種欄位
FROM `table_name` main_tale
RIGHT JOIN 
(
SELECT  子查詢只查主鍵
FROM `table_name`
WHERE 各種條件
LIMIT 0,10;
) temp_table ON temp_table.主鍵 = main_table.主鍵

優化案例

首先我們執行以下指令碼,創造測試資料;

CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET latin1 NOT NULL,
`sex` char(1) CHARACTER SET latin1 DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
`phone` varchar(16) CHARACTER SET latin1 DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_age` (`age`),
KEY `idx_sex` (`sex`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5000002 DEFAULT CHARSET=utf8;
-- 定義語法結束符號
delimiter //
-- 建立一個 名稱為 p2 的儲存過程
drop procedure if exists p2;
create procedure p2()
begin
    declare sex varchar(1) character set utf8;
		declare age,total int;
    -- 初始化 變數
    set total = 0;
    -- loop 迴圈
    xxx:
    loop
	IF mod(total,2) != 0 THEN
			    set sex = 'M';	
        ELSEIF mod(total,2) =0 THEN
			    set sex = 'F';
        END IF;
	set age = mod(total,100);
        INSERT INTO `krisDB`.`users`(`id`, `name`, `sex`, `age`, `phone`) VALUES (total+1, 'tom'+total, sex, age, '10086');
	if total = 5000000 then
            leave xxx;
        end if;		
        -- 累計
        set total = total + 1;
    end loop;
		SELECT total;
end //
delimiter;
call p2();

優化前:

優化後:

我們可以看到節省了很多時間了。

優化原理

select * from users where sex = 'M' limit 300000,5; 查詢到索引葉子節點資料。根據葉子節點上的主鍵值,回表操作,也就是去聚簇索引上查詢需要的全部欄位值。

像上面這樣,需要查詢300005次索引節點,查詢300005次聚簇索引的資料,最後再將結果過濾掉前300000條,取出最後5條。MySQL耗費了大量隨機I/O在查詢聚簇索引的資料上,而有300000次隨機I/O查詢到的資料是不會出現在結果集當中的,那麼這部分時間是浪費的。這一點我們可以證實,因為information_schema.INNODB_BUFFER_PAGE表中有uk_sex,primary索引查詢的快取記錄。

執行查詢select * from users where sex = 'M' limit 300000,5之前

執行查詢select * from users where sex = 'M' limit 300000,5之後

優化查詢語句為SELECT * from users a RIGHT JOIN (SELECT id FROM users where sex = 'M' limit 300000,5) b on a.id = b.id;

執行查詢語句之前

執行查詢語句之後