你都是會點啥技術(四)— 數據庫
寫在前面的話:還記得2018年的時候開發的項目上線,經過大概一個月,因爲數據量增加,造成項目查詢頁面的延遲,因爲項目使用的羣體是固定的,所以當時提出來後並沒有着力解決,不過我一直對數據庫優化這塊耿耿於懷,抽出時間來基於MYSQL學習一下!
1.概念知識補充
數據庫系統圖:
2.查詢優化技術分類
(1.)查詢重用:①查詢結果重用;②查詢計劃重用。
(2.)查詢重寫規則:①基於關係型代數;②視圖重寫、子查詢優化、等價位置重寫、條件化簡、外聯接消除、聯接消除、嵌套聯接消除;③語義優化。
(3.)查詢優化算法(基於代價估算模型):①單表掃描算法(全表掃描、索引掃描、行定位掃描);②兩表連接算法(嵌套聯接算法、歸併連接算法、哈希連接算法);③多表連接算法(貪婪)。
(4.)並行查詢優化:利用分解查詢SQL,進行並行計算查詢,提高查詢目的。
(5.)分佈式查詢優化:利用分佈式系統優化查詢。
3.數據庫調優
數據庫調優包括數據庫管理系統優化和查詢優化,主要目的是使數據庫有更高的吞吐量和更高的響應時間。
4.差不多了,實際應用開始走起!
第一招
:explain:
顯示了mysql如何使用索引來處理語句以及連接表。可以幫助選擇更好的索引和寫出更優化的查詢語句!我們以select語句爲例子
詳情請參考:https://www.cnblogs.com/yycc/p/7338894.html
第二招
:子查詢優化:
1.子查詢合併:
原理:把多次表掃描、多次連接減少爲低次表掃描和低次連接。
/*
*優化前
*/
SELECT * FROM t_user WHERE zhuhuid < 20 AND (
EXISTS (SELECT id FROM t_zhuhu WHERE id<15 AND id=5) OR
EXISTS (SELECT id FROM t_zhuhu WHERE id<15 AND id=10));
/*
*優化後
*/
SELECT * FROM t_user WHERE zhuhuid < 20 AND (
EXISTS (SELECT id FROM t_zhuhu WHERE id<15 AND id=5 OR id=10))
2.子查詢展開:
原理:將子查詢上拉到父查詢,實質是把某些子查詢重寫爲等價的多表連接操作,這樣有關的的訪問路徑、連接方法、連接順序可能被有效使用、使得查詢語句的層次儘可能減少。
/*
*優化前
*/
SELECT * FROM t_user a,(SELECT * FROM t_zhuhu WHERE
t_zhuhu.id>10) b WHERE a.zhuhuid<10 AND b.id<20
/*
*優化後
*/
SELECT * FROM t_user a,t_zhuhu b WHERE a.zhuhuid<10
AND b.id<20 AND b.id>10
3.聚集子查詢消除
原理:利用SQL函數消除子查詢的優化技術。
SELECT * FROM t_user a WHERE a.zhuhuid > (SELECT AVG(b.id)
FROM t_zhuhu b)
能夠做子查詢優化格式要求:
1.簡單Select查詢中的子查詢,
2.帶有DISTINCT、ORDERBY、LIMIT操作的簡單SELECT查詢中的子查詢。
不能做子查詢優化的格式:
1.帶有UNION操作
2.帶有GROUPBY、HAVING、聚集函數
3.使用ORDERBY中帶有LIMIT
4.內表、外表的個數超過MySQL支持的最大表的連接數(mysql最大連接數63)。
第三招
:視圖重寫
定義:視圖是數據庫中基於表的一種對象,把對錶的查詢固化,這種固化是視圖。
原理:MySQL支持簡單視圖優化技術,MySQL把視圖轉爲對基表的查詢,然後進行類似子查詢的優化。但不支持複雜視圖(帶有GROUP BY、Oreder By 等操作稱爲複雜視圖)優化。
/*
* 創建視圖
*/
CREATE VIEW t_view AS SELECT * FROM t_user
/*
* 未使用視圖
*/
SELECT a.id FROM (SELECT id,zhuhuid FROM t_user) as a
WHERE a.zhuhuid>10
/*
* 使用視圖
*/
SELECT id FROM t_view WHERE zhuhuid>10
第四招
:等價謂詞重寫
原理:把邏輯表達式重寫成等價的且效率更高的形式,提高查詢執行效率。
1.LIKE規則重寫
如果name列上存在索引,則可以利用索引提高查詢效率。
/*
* 重寫前
*/
name LIKE 'abc'
/*
* 重寫後
*/
name = 'abc'
2.BETWEEN-AND規則
如果sno上建立了索引,則可以用索引掃描代替原來BETWEEN-AND謂詞限定的全表掃描,從而提高了查詢的效率。
/*
* 重寫前
*/
sno BETWEEN 10 AND 20
/*
* 重寫後
*/
sno >=10 AND sno <=20
3.IN轉換OR規則
看實際情況使用,如果age列上存在索引,則轉換後查詢效率會提高。
/*
* 重寫前
*/
age IN (8,12,21)
/*
* 重寫後
*/
age=8 OR age=12 OR age=21
4.NOT規則
如果col_1建立了索引,則可以用索引掃描代替原來的全表掃描,從而提高查詢的效率
/*
* 重寫前
*/
NOT (col_1 != col_2)
/*
* 重寫後
*/
col_1 = col_2
5.OR重寫並集規則
可以分別利用列sex和age上的索引,進程索引掃描,執行UNION操作獲得最終結果。
/*
* 重寫前
*/
SELECT * FROM student WHERE (sex='f' and age>15) OR age>18
/*
* 重寫後
*/
SELECT * FROM student WHERE sex='f' and age>15
UNION
SELECT * FROM student WHERE age>18
第五招
:條件化簡
1.把HAVING條件併入WHERE條件,前提是SQL中不存在GROUPBY條件或聚集函數的情況下,才能將HAVING條件與WHERE條件合併。
/*
*例子
*/
SELECT * FROM t1 WHERE a1>1 having a2=2
/*
*等價於
*/
SELECT * FROM t1 WHERE a1>1 and a2=2
2.去除表達式中冗餘的括號
減少語法分析時產生的AND和OR樹的層次。減少了CPU的消耗
/*
*例子
*/
((a AND b)AND (c AND d))
/*
*化簡爲
*/
a AND b AND c AND d
3.常量傳遞
/*
*例子
*/
col_1=col_2 AND col_2=3
/*
*化簡爲
*/
col_1=3 AND col_2=3
4.消除死碼,表達式計算,等式變換,不等式變換,布爾表達式變換
儘量減少數據庫去計算,把一些計算進行優化,儘量利用索引,提升數據庫查詢效率。
第六招
.外連接消除、嵌套連接消除、連接消除
連接消除:去掉的是被連接的某個對象。
外連接消除:去掉的事外連接的語義,變形爲內連接。
嵌套連接消除:就是消除嵌套的連接層次,把多個層次的連接減少爲較少層次的連接,儘量"扁平化"。
第七招
.8.語義優化
1.連接消除
2.連接引入
3.謂詞引入
4.檢測空回答集
5.排序優化
6.唯一性使用
9.非SPJ的優化
1.GROUP BY、ORDER BY優化,儘量利用索引。
物理優化
1.索引
優點:提高少量數據的獲取/檢索速度
缺點:佔用存儲空間、多個索引耗費索引的挑選時間、降低寫操作的性能,需要實時維護索引、併發情況下索引的維護高度複雜。
不使用索引:數據的重複度高、選擇率高於10%、表的數據量少
MYSQL高級教程
記錄一下學習過程!
1.觸發器
四個必要條件:監視地點(table)、監視事件(insert/update/delete)、觸發時間(after/before)、觸發事件(insert/update/delete)
觸發器使用語法練習
/*
* 聲明以;爲結尾符
*/
delemiter ;
/*
* 監聽table_1表在table_1表有insert事件之後更新table_2
*/
create trigger trigger_1
after
insert
on table_1
for each row
begin
update table_2 set num=num-1;
end;
/*
*new.num獲取table_1新插入的num值,相反old.num是獲取表刪除的num值
* insert 只能用 new
* delete 只能用 old
* update 可用 new/old
*/
create trigger trigger_2
after
insert
on table_1
for each row
begin
update table_2 set num=num-new.num;
end;
/*
*declare 聲明變量
*select num into rnum/set new.num = rnum;賦值
*if 條件 then 語句 end if; 判斷語句
*/
create trigger trigger_3
before
insert
on table_1
for each row
begin
declare rnum int;
select num into rnum from table_2 where id=new.id;
if new.num > runm then
set new.num = rnum;
end if;
update table_2 set num=num-new.num;
end;
2.存儲過程
/*
*創建存儲過程語法
*/
create procedure procedureName()
begin
--sql語句;
ends;
/*
*查看已有的procedure
*/
show procedure status;
/*
*調用存儲過程語法
*/
call procedureName()
/*
* 給存儲過程傳參數
*/
create procedure p1(width int,height int)
begin
select concat('你的面積是',width * height);
if width > height then
select '你挺胖';
elseif width<height then
select '你挺瘦';
else
select '你挺方';
end if;
end;
--調用
CALL p1(4,5)
/*
*in 輸入 out 輸出
*/
create procedure p2(in n int,out total int)
begin
declare num int default 0;
set total = 0;
while num < n do
set num = num + 1;
set total = total + num;
end while;
end;
call p2(100,@total)
SELECT @total
/*
*inout 輸入處理後輸出,即是輸入又是輸出
*/
create procedure p3(inout age int)
begin
SELECT CONCAT("20年後你的年齡是:",age+20);
end;
set @age =18;
call p3(@age)
/*
*repeat的用法
*/
--刪除存儲過程
drop procedure p4
create procedure p4()
begin
declare num int default 0;
declare total int default 0;
repeat
set num = num +1;
set total = total + num;
until num >= 100 end repeat;
select total;
end
call p4()
/*
*case的用法
*RAND()隨機0.1-1的數
*floor()取整
*/
create procedure p5()
begin
declare a int default 1;
set a = floor(RAND()*5);
case a
WHEN 0 then select "我是0";
WHEN 1 then select "我是1";
WHEN 2 then select "我是2";
else select "我不是0,1,2";
end case;
end;
call p5();
3.遊標
定義:sql查詢多條結果集,利用遊標可以一次取出一行
聲明:declare 遊標名 cursor for sql查詢結果集
打開:open 遊標名
取值:fetch 遊標名 into 值1,值2
關閉:close 遊標名
CREATE TABLE t_student ( sid INT PRIMARY KEY, sname VARCHAR ( 20 ), sage INT );
INSERT INTO t_student ( sid, sname, sage ) VALUES ( 1, "李一", 10 );
INSERT INTO t_student ( sid, sname, sage ) VALUES ( 2, "李二", 20 );
INSERT INTO t_student ( sid, sname, sage ) VALUES ( 3, "李三", 30 );
create procedure p6()
begin
declare row_sname varchar(20);
declare you int default 1;
declare c1 cursor for select sname from t_student;
declare exit handler for not found set you =0;
--如果句柄沒有值設置you爲0
--exit handler觸發後,後面的語句不再執行
--continue handler觸發後,後面的語句繼續執行
--undo handler觸發後,前面的語句撤銷 mysql不支持
open c1;
repeat
fetch c1 into row_sname;SELECT row_sname;
until you = 0 end repeat;
close c1;
end;
call p6
4.用戶權限設置
5.主從複製