日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

从建表到SQL优化

發布時間:2024/10/5 数据库 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从建表到SQL优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、寬表還是窄表?怎么做選擇?一張表多大合適?

??? 寬表

字段比較多的表,包含的維度層次比較多,造成冗余也比較多,毀范式設計,但是利于取數統計。適合做數據倉庫、大數據等

? ? 窄表

往往對于OLTP比較合適,符合范式設計原則;就性能角度來講,一般窄表優于寬表,而且窄表邏輯更加清晰。所以一般推薦用面向業務一般用窄表來實現。

那么究竟一張表多少字段合適呢?有沒有一個界限來判斷寬表還是窄表呢?

mysql的物理存儲結構如下:

在計算機中磁盤存儲數據最小單元是扇區,一個扇區的大小是 512 字節,而文件系統(例如XFS/EXT4)他的最小單元是塊,一個塊的大小是 4k,而對于我們的 InnoDB 存儲引擎也有自己的最小儲存單元——頁(Page),一個頁的大小是 16K。

mysql物理存儲的結構,由段-區-頁-行組成:

一個區是1M大小,由連續的64個16k的頁組成,每個頁又由N行組成。

在mysql內存加載過程中,數據加載的最小單位是頁。所以每個頁中存儲的行越多,則數據加載的頁會越少,查找性能越高。

假設一頁16k=160行,則一行=100字節,100字節=10個字段=>1個字段=10字節,所以這里看下1行存儲10個10字節的字段,這樣一頁能存儲160行數據。所以到這里,可以根據實際的業務場景預估你單表所需要的實際字段數量及每個字段的長度,合理進行字段設計;數據量越大越不適合寬表設計,字段應該盡可能的少。對于互聯網公司來說,基本是要告別寬表,使用者窄表,一方面是為了性能,一方面也是方便了維護。

innodb關于數據行大小的限制

我們知道innodb的頁塊大小默認為16kb,表中數據是存放在B-tree node的頁塊中,但如果表中一行的數據長度超過了16k,這時候就會出現行溢出,溢出的行是存放在另外的地方,存放該溢出數據的頁叫uncompresse blob page。

innodb采用聚簇索引的方式把數據存放起來,即B+樹結構,因此每個頁塊中至少有兩行數據,否則就失去了B+樹的意義(每一個頁中只有一條數據,整個樹成為了一條雙向鏈表),這樣就得出了一行數據的最大長度就限制為了8k。

? ? 當插入的一行數據不能在一個數據頁塊中存放時,為了保證該頁至少能存放兩行數據,innodb將會自動部分數據溢出到另外頁中,一部分數據將存放在數據頁塊中,其大小為該列的前768字節,同時接著還有偏移指向溢出頁。

? ? 如上面所說大字段的前768字節會存放在數據頁塊中,那么如果有10個大字段(如varchar(1000),text,blob同varchar同樣存儲前768字節),同樣會超過一行數據8k的限制(10*768<8000,11*768>8000)。如果插入的值超過8000字節,則會報錯(BLOB或TEXT同理)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?innodb的表數據結構(B+樹)

回顧一下Innodb是如何查找數據的?

要查找一條數據,怎么查?

如 select * from user where id=5;

這里 id 是主鍵,我們通過這棵 B+ 樹來查找,首先找到根頁(每個頁都有一個編號),你怎么知道 user 表的根頁在哪呢?

其實每張表的根頁位置在表空間文件中是固定的,即 page number=3 的頁,找到根頁后通過二分查找法,定位到 id=5 的數據應該在指針 P5 指向的頁中,那么進一步去 page number=5 的頁中查找,同樣通過二分查詢法即可找到 id=5 的記錄

總結一下:

1、InnoDB 存儲引擎的最小存儲單元是頁,頁可以用于存放數據也可以用于存放鍵值 + 指針,在 B+ 樹中葉子節點存放數據,非葉子節點存放鍵值 + 指針。

2、索引組織表通過非葉子節點二分查找法以及指針確定數據在哪個頁中,進而在去數據頁中查找到需要的數據;

如果每個節點就一個數據,那么整個葉節點就是一個雙向鏈表(圖中是單向鏈表,實際是雙向的)了,就失去了樹的意義。

2、SQL優化

sql的優化的關鍵是是要確定優化的方向,也就是定位性能的缺陷:

一、分析SQL語句的執行計劃

什么是執行計劃呢?

簡單來說,就是SQL在數據庫中執行時的表現情況,通常用于SQL性能分析、優化等場景。

MySQL邏輯架構分為三層,如下圖:

客戶端:

????如,連接處理、授權認證、安全等功能;

核心服務:

????MySQL大多數核心服務均在這一層,包括查詢解析、分析、優化、緩存、內置函數(如,時間、數學、加密等),所有的跨存儲引擎的功能也在這一層,如,存儲過程、觸發器、視圖等;

存儲引擎

????負責MySQL中的數據存儲和讀取中間的服務層通過API與存儲引擎通信,這些API屏蔽了不同存儲引擎間的差異;

查詢緩存:對于select語句,MySQL查詢緩存保存查詢返回的完整結果。當查詢命中該緩存,MySQL會like返回結果,跳過了解析、優化和執行截斷。MySQL查詢緩存保存查詢返回的完整結果。并不是什么情況下查詢緩存都會提高系統性能的。緩存和失效都會帶來額外的消耗,所以只有當緩存帶來的資源節約大于其本身的資源消耗時才會給系統帶來性能提升。這根具體的服務器壓力模型有關。關于是否開啟查詢詳見這篇文章:https://blog.csdn.net/yongqi_wang/article/details/86674088

注意?:1. 查詢緩存不返回舊的數據。當表更改后,查詢緩存值的相關條目被清空。

? ? ? ? ? ? ? 2.?如果你有許多mysql的?服務器更新相同的MyISAM?表,在這種情況下查詢緩存不起作用。

? ? ? ? ? ? ??3.?查詢緩存不適用于服務器方編寫的語句。

explain

顯示了mysql如何使用索引來處理select語句以及連接表。可以幫助選擇更好的索引和寫出更優化的查詢語句。

使用方法,在select語句前加上explain就可以了:如:explain select * from test1

EXPLAIN列的解釋:

table:

  • 顯示這一行的數據是關于哪張表的

type:

  • 這是重要的列,顯示連接使用了何種類型。從最好到最差的連接類型為const、eq_reg、ref、range、indexhe和ALL。

各個連接的含義:

const,通過索引一次命中,匹配一行數據;
eq_ref,唯一性索引掃描,對于每個索引鍵,表中只有一條記錄與之匹配,常用語主鍵或唯一索引掃描;

ref,非唯一性索引掃描,返回匹配某個單獨值的所有行,用于=、<或>操作符帶索引的列;

range,只檢索給定范圍的行,使用一個索引來選擇行,一般用于between、<、>;

index,只遍歷索引樹;
all,全表掃描;
system,表中只有一行記錄,相當于系統表;
  • 通常優化至少到range級別,最好能優化到ref

possible_keys:

  • 指出 MySQL 使用哪個索引在該表找到行記錄。如果該值為 NULL,說明沒有使用索引,可以建立索引提高性能;

key

  • 實際使用的索引。如果為NULL,則沒有使用索引。很少的情況下,MYSQL會選擇優化不足的索引。這種情況下,可以在SELECT語句中使用USE INDEX(indexname)來強制使用一個索引或者用IGNORE INDEX(indexname)來強制MYSQL忽略索引

key_len

  • 使用的索引的長度。在不損失精確性的情況下,長度越短越好

ref

  • 顯示索引的哪一列被使用了,如果可能的話,是一個常數

rows

  • MYSQL認為必須檢查的用來返回請求數據的行數

Extra

  • 關于MYSQL如何解析查詢的額外信息。將在表4.3中討論,但這里可以看到的壞的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,結果是檢索會很慢。
  • using filesort,MySQL會對數據使用一個外部索引排序,而不是按照表內索引順序進行讀取,若出現改值,則應優化SQL語句;

    using temporary,使用臨時表緩存中間結果,比如,MySQL在對查詢結果排序時使用臨時表,常見于order by和group by,若出現該值,則應優化SQL;
    using index,表示select操作使用了覆蓋索引,避免了回表查詢;簡單來說:使用到了索引 , 并且所取的數據完全在索引中就能拿到
    using where,where子句用于限制哪一行;簡單來說:表示使用到了索引 , 但是也進行了where過濾
    using join buffer,使用連接緩存;
    distinct,發現第一個匹配后,停止為當前的行組合搜索更多的行;

二:show profiles

定義:顯示sql執行過程中各個環節的消耗情況,例如cpu使用情況,打開表、檢查權限、執行優化器、返回數據等分別用了多長時間,可以分析語句執行慢的瓶頸在哪。如果要使用這個命令首先要設置profiling為on,mysql默認設置為off;

1、查看當前profiling的值:

? ? ? select @@profiling

2、設置打開profiling

? ? ? set profiling =1;
? ? ? or
? ? ? set profiling=on;

3、設置關閉profiling

? ? ? set profiling=0;
? ? ? or
? ? ? set profiling=off;

4、show profiles 默認顯示最近15條的sql執行情況,15這個數字由profiling_history_size常量決定,你可以配置為0到100的數字最? ? ? ? ? 大支持100,如果設置為0則類似于關閉profiling選項。除了show profile 和show profiles 兩個命令不會被記錄之外,其余的語句? ? ? ? 都會被記錄,即使是語法錯誤的sql也會被記錄。show profile 命令默認獲取最新一條執行的sql的消耗分析,如果想指定獲取某? ? ? ? 一條sql的profile 用如下命令:

5、show profiles 先獲取要分析的query_id,然后執行show profile for query query_id;

? ? ? show profiles以及show profile 命令同時也支持 limit語句。show profile 命令 默認只顯示Duration 列總消耗時間,如果要顯示更? ? ? ? 多可以設置 all參數
6、show profile [type];

type 參數支持如下:
| ALL :表示所有的列
| BLOCK IO :數據塊的輸出出入操作次數
| CONTEXT SWITCHES : 上下文切換次數
| CPU:cpu使用時長 包括系統時長和用使用時長
| IPC :發送和接收數據的 次數
| PAGE FAULTS:重要的和次要的錯誤次數
| SOURCE:執行相應操作的mysql源文件,包括源文件方法名,地址、所在行;
| SWAPS:swap次數

提示:實際上這些數據全都存在mysql的表里,你可以執行如下查詢一樣可以獲取相同的信息。
SELECT * FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID = query_id;

二、SQL語句

避免全表掃描:盡量避免用in、not in、is null、or、like、左值運算、函數運算

1、對查詢進行優化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。?? ?
2、應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:select id from t where num is null??可以在num上設置默認值0,確保表中num列沒有null值,然后這樣查詢:?? ?
select id from t where num=0???

3、應盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。?? ?
4、應盡量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:?? ?
select id from t where num=10 or num=20?? ?
可以這樣查詢:?? ?
select id from t where num=10?? ?
union all?? ?
select id from t where num=20

5、in 和 not in 也要慎用,否則會導致全表掃描,如:?? ?
select id from t where num in(1,2,3)?? ?
對于連續的數值,能用 between 就不要用 in 了:?? ?
select id from t where num between 1 and 3??

6、下面的查詢也將導致全表掃描:?? ?
select id from t where name like '%abc%'?? ?

7、7.應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:?? ?
select id from t where num/2=100?? ?
應改為:?? ?
select id from t where num=100*2?? ?
?8、應盡量避免在where子句中對字段進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:?? ?
select id from t where substring(name,1,3)='abc'--name以abc開頭的id?? ?
應改為:?? ?
select id from t where name like 'abc%'?? ?

9、不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。

10、在使用索引字段作為條件時,如果該索引是復合索引,那么必須使用到該索引中的第一個字段作為條件時才能保證系統使用該索引,否則該索引將不會被使用,并且應盡可能的讓字段順序與索引順序相一致。

11、 不要寫一些沒有意義的查詢,如需要生成一個空表結構:?? ?
select col1,col2 into #t from t where 1=0?? ?
這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:?? ?
create table #t(...)?? ?
?? ?
12、很多時候用 exists 代替 in 是一個好的選擇:?? ?
select num from a where num in(select num from b)?? ?
用下面的語句替換:?? ?
select num from a where exists(select 1 from b where num=a.num)??

13、并不是所有索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重復時,SQL查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那么即使在sex上建了索引也對查詢效率起不了作用。?? ?

14、索引并不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,?? ?
因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。?? ?
一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

15、盡量使用數字型字段,若只含數值信息的字段盡量不要設計為字符型,這會降低查詢和連接的性能,并會增加存儲開銷。?? ?
這是因為引擎在處理查詢和連接時會逐個比較字符串中每一個字符,而對于數字型而言只需要比較一次就夠了。

16、盡可能的使用 varchar 代替 char ,因為首先變長字段存儲空間小,可以節省存儲空間,?? ?
其次對于查詢來說,在一個相對較小的字段內搜索效率顯然要高些。?

17、任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。

18、避免頻繁創建和刪除臨時表,以減少系統表資源的消耗

19、臨時表并不是不可使用,適當地使用它們可以使某些例程更有效,例如,當需要重復引用大型表或常用表中的某個數據集時。但是,對于一次性事件,最好使用導出表。

20、在新建臨時表時,如果一次性插入數據量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,?? ?
以提高速度;如果數據量不大,為了緩和系統表的資源,應先create table,然后insert。

21、如果使用到了臨時表,在存儲過程的最后務必將所有的臨時表顯式刪除,先 truncate table ,然后 drop table ,這樣可以避免系統表的較長時間鎖定。?

22、盡量避免使用游標,因為游標的效率較差,如果游標操作的數據超過1萬行,那么就應該考慮改寫。

23、使用基于游標的方法或臨時表方法之前,應先尋找基于集的解決方案來解決問題,基于集的方法通常更有效。

24、與臨時表一樣,游標并不是不可使用。對小型數據集使用 FAST_FORWARD 游標通常要優于其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數據時。
在結果集中包括“合計”的例程通常要比使用游標執行的速度快。如果開發時間允許,基于游標的方法和基于集的方法都可以嘗試一下,看哪一種方法的效果更好。

25、盡量避免大事務操作,提高系統并發能力;

26、盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。

27、使用復合索引

28、避免索引失效

29、避免回表查詢

30、不要索引上左任何運算操作

31、小表驅動達標。小表查詢條件放在where子句的左邊

總結

以上是生活随笔為你收集整理的从建表到SQL优化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。