MySQL幾個簡單SQL的優化

最近在作項目的時候,遇到了一些大數據量的操做,有大批量的CRUD的操做,一開始的實現的方案通過性能測試,發現性能並非很好,而後開始審查代碼,對相關能夠提高性能的操做進行了優化,這裏分享給你們。sql

原則

首先我這裏不講索引相關的內容以及數據庫相應參數的優化,這裏假設你對索引已經有了相關的瞭解了,我總結了下我此次的優化,主要兩個原則:數據庫

  • 一些特定的場景,儘可能用批處理處理數據,好比批量添加數據,批量修改數據;
  • 結合業務儘可能減小SQL的執行次數和查詢沒必要要的數據;

場景實踐

爲模擬運行場景,我這裏建了一個表,並往裏面添加了300w條數據,表結構以下:網絡

CREATE TABLE `tb_big_data` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `weixin_id` varchar(64) NOT NULL,
 `openid` varchar(64) NOT NULL,
 `status` int(3) NOT NULL,
 `gmt_create` datetime NOT NULL,
 `gmt_modified` datetime NOT NULL,
 PRIMARY KEY (`id`),
 KEY `weixin_id_gmt_create_openid` (`weixin_id`,`gmt_create`,`openid`)
) ENGINE=InnoDB AUTO_INCREMENT DEFAULT CHARSET=utf8

1.分頁查詢小優化

分頁查詢老生常談,網上各類優化方法都不少,這裏就不說起了,這裏只是分享一個小技巧:性能

如何在使用最普通的limit的時候提升性能?測試

假設咱們如今有一條這樣的SQL:大數據

SELECT * FROM `tb_big_data` where weixin_id ='gh_266a30a8a1f6' and gmt_create > '2017-10-10 00:00:00' order by id asc limit 800000, 100;

執行時間:100 rows in set (1.53 sec)

假如咱們如今不能進行其餘優化,好比傳入最小id,分表查詢等策略,以及不進行SQL預熱,怎麼提升這條SQL的速度呢?
其實很簡單咱們只須要一個in操做便可:優化

SELECT * FROM `tb_big_data` t1 where t1.id in ( 
    SELECT tt.id FROM ( 
        SELECT id FROM `tb_big_data` t2 where weixin_id = 'gh_266a30a8a1f6' and gmt_create > '2017-10-10 00:00:00' order by t2.id asc limit 800100, 100
        ) as tt);

執行時間:100 rows in set (1.17 sec)

能夠看出只需稍加修改,SQL的效率能夠提升30%~40%,並且在單條數據記錄越大的狀況下效果越好,固然這不是最好的分頁方法,這只是一個小技巧;3d

2.減小SQL查詢

如今有一個需求咱們如今有一個用戶的列表(用戶的惟一標識爲openid)而後咱們須要判斷用戶在當天是否有過相應的記錄;code

這是問題其實很簡單,咱們首先一想到的操做就是循環這個列表一個一個判斷,很簡單也很好實現,可是真正測試的時候發現性能卻不好,尤爲在數據量大的狀況下,倍數級增加,這裏有有網絡數據傳輸消耗的時間和SQL自己的執行時間;索引

假設咱們如今執行一條如下的SQL:

SELECT * FROM `tb_big_data` WHERE weixin_id ='gh_266a30a8a1f6' and gmt_create > '2017-10-13 00:00:00' and openid='2n6bvynihm5bzgyx';

執行時間:1 row in set (0.95 sec)

如今若是咱們執行100次,不敢想象會是什麼狀況,慶幸本身發現了這個問題,由於在數據量少的狀況下,這個問題表現的並非那麼嚴重,其實咱們稍加改變就能以另外一種高效的方式解決這個問題:

SELECT * FROM `tb_big_data` WHERE weixin_id ='gh_266a30a8a1f6' and gmt_create > '2017-10-13 00:00:00' and openid in ('2n6bvynihm5bzgyx','1stbvdnl63de2q37','3z8552gxzfi3wy27'...);

執行時間:100 row in set (1.05 sec)

發現了沒有,仍是用in,並且執行時間幾乎與單條查詢的時間同樣,可見只是單一這一部分處理就能夠提高了很大的性能。

3.特定場景使用SQL的批處理

這個跟上一點有一個類似點,那就是減小SQL執行,上面只是查詢而已,而當出現大批量的CUD的操做時,執行每條SQL,數據庫都會進行事務處理,這將會消耗大量的時間,並且極端狀況下會引發大批量SQL等待沒法執行,致使業務出錯,正是由於這些緣由,咱們在一些適當的狀況下可使用批處理來解決這個問題。

(1)批量插入

批量插入比較簡單,也比較經常使用,這裏就給一下基本語法:

INSERT INTO table_name (field1,filed2,...) values (value11,value12,...),(value21,value22,...),...
(2)批量更新

我先舉個簡單的例子,咱們如今來根據一些條件來更新數據,具體SQL以下:

update `tb_big_data` set status = 2 WHERE weixin_id ='gh_266a30a8a1f6' and gmt_create > '2017-10-13 00:00:00' and openid = '2n6bvynihm5bzgyx';

Query OK, 1 row affected (2.28 sec)
Rows matched: 1  Changed: 1  Warnings: 0

很驚訝,咱們只是更新了一條記錄,並且更新條件上是有複合索引的,沒想到速度還那麼慢,能夠想象若是咱們批量更新數據,那得耗時多少;

可是咱們看一下另外一條SQL:

update `tb_big_data` set status = 1 WHERE id = 900098;

Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

上面的id值爲以前條件篩選出來的記錄的id,是否是很驚訝,怎麼這條SQL執行的時間幾乎不須要什麼時間,因此咱們能夠利用這個特色和批量查詢簡化批量更新,雖然這種方式不能讓性能到最優,可是也能提高很大了,我進行了一個測試,根據相應條件批量更新100條數據:

方式 直接批量更新 先批量查主鍵再批量更新
耗時 289.12s 1.342s

能夠看出這種方式相對對於普通方式來講,性能提高巨大,具體執行的時候咱們也能夠將這些SQL放在一個事務提交,減小數據庫事務次數,但只這是一種在代碼層面上的優化;

另外咱們能夠利用MySQL提供的特殊語法進行批量更新,具體語法爲:

#語法
INSERT INTO table_name (id,field1,field2,...) VALUES  (id1,value11,value12,...),(id1,value11,value12,...),... on duplicate key update  field = VAULES(field);

#使用例子

INSERT INTO `tb_big_data` (id,weixin_id,openid,gmt_create,status) values  (1,'gh_266a30a8a1f6','w9q8fmodytjgppsr','2017-10-13 12:00:00',3),(2,'gh_266a30a8a1f6','bu1flmch4i8eegzf','2017-10-13 12:00:00',3) on duplicate key update status = VAULES(status);

通過測試這種方式在數據量小的狀況下與上述方式效率差很少,可是隨着數據量愈來愈大,性能也愈來愈好,缺點的話主要傳輸的數據量很大,不須要更新的字段也須要傳輸。

另外也不推薦大量數據的批量更新,一次不要超過1000條爲好。

總結

總的來講,SQL優化是一門細心的學問,須要不斷去嘗試,測試,找到最優方式,另外還有一點就是要結合實際狀況,綜合考慮選擇合適的方式。

相關文章
相關標籤/搜索