㈠ mysql sql優化之 優化GROUP BY 和 DISTINCT
創建表tb_point 表
准備空的tb_box表
函數
編寫存儲過程,給tb_box表添加100萬條數據
修改關聯數據
好於
優於
在執行以下語句時會報錯:
前面在 https://www.jianshu.com/p/95e50fd017ea 文章中有提到這個問題,是直接修改sql_mode將 ONLY_FULL_GROUP_BY直接幹掉。但是在《高性能mysql》中有一段話是這樣的:
那麼既然指出不要直接修改 sql_mode,那麼我們應該如何讓沖突的GRUOPBY語句正確執行呢?
文中有提到,可以使用max()和min()函數來實現;但是這種方式使用max和min函數較真的人可能會說這樣寫的分組查詢有問題,確實如此。但是如果更加在乎查詢效率,這樣做也無可厚非。
如果,實在無法接受使用上面那種方式的話,可以這樣使用子查詢的方式來進行查詢:
書上對於這種方式有描述如下:
這樣寫更滿足關系理論,但是成本有點高,因為子查詢需要填充臨時表,而子查詢中創建的臨時表是沒有任何索引的。
作者認為這樣寫對性能有影響。
但是從我測得結果來看,子查詢的耗時反而更少。性能反而更佳。這個子查詢耗時0.4秒。而使用max方式耗時0.8秒。幾乎一倍。我的mysql版本是 5.7.22-log
為了解其中的原因,我們查看它的執行計劃:
可見,因為子查詢而產生了一層 DERIVED 臨時表,但是這個臨時表的Extra欄位有顯示 Using index、key裡面顯示自建索引。說明用到了索引。這是查詢性能可觀的一個重要原因吧;
另外我分別使用 SHOW PROFILE命令查看各部分耗時,對比之下。沒看到有哪部分耗時差別特別大,使用JOIN、MAX 耗時比上子查詢耗時都差不多是1倍
有些時候對一沒有建立索引的欄位,進行GRUOP BY時。會產生Using filesort 文件內排序。因為GRUOP BY是在排序的基礎上進行分組的。
如下面sql:
如果業務上不對排序有要求。那麼就可以禁止GRUOP BY的排序:
這樣就把Using filesort給幹掉了! 執行時間 1.237
當然,多數情況是多排序有要求的。此時也可以在GRUOP BY後面使用DESC和ASC關鍵字,使分組的結果集按需要的方向排序。如下:
分組查詢的一個變種就是要求mysql對分組結果再進行一次超級聚合。可以使用GROUP BY WITH ROLLUP 來實現這種邏輯,但可能性能不佳。因為通過查詢計劃分析出它是使用 Using temporary; Using filesort 來實現的。
使用WITH ROLLUP,查詢時間2.531秒。不使用0.774 秒。
1、所以,很多時候。我們在應用程序中做超級聚合是最好的!
2、當然也可使用UNION ALL 來實現:
3、還可以通過FROM子句嵌套使用子查詢:
㈡ 如何進行SQL性能優化
這里分享下mysql優化的幾種方法。
1、首先在打開的軟體中,需要分別為每一個表創建 InnoDB FILE的文件。
㈢ Mysql某個表有近千萬數據,CRUD比較慢,如何優化
數據千萬級別之多,佔用的存儲空間也比較大,可想而知它不會存儲在一塊連續的物理空間上,而是鏈式存儲在多個碎片的物理空間上。可能對於長字元串的比較,就用更多的時間查找與比較,這就導致用更多的時間。
可以做表拆分,減少單表欄位數量,優化表結構。
在保證主鍵有效的情況下,檢查主鍵索引的欄位順序,使得查詢語句中條件的欄位順序和主鍵索引的欄位順序保持一致。
主要兩種拆分 垂直拆分,水平拆分。
垂直分表
也就是「大表拆小表」,基於列欄位進行的。一般是表中的欄位較多,將不常用的, 數據較大,長度較長(比如text類型欄位)的拆分到「擴展表「。 一般是針對 那種 幾百列的大表,也避免查詢時,數據量太大造成的「跨頁」問題。
垂直分庫針對的是一個系統中的不同業務進行拆分,比如用戶User一個庫,商品Proct一個庫,訂單Order一個庫。 切分後,要放在多個伺服器上,而不是一個伺服器上。為什麼? 我們想像一下,一個購物網站對外提供服務,會有用戶,商品,訂單等的CRUD。沒拆分之前, 全部都是落到單一的庫上的,這會讓資料庫的單庫處理能力成為瓶頸。按垂直分庫後,如果還是放在一個資料庫伺服器上, 隨著用戶量增大,這會讓單個資料庫的處理能力成為瓶頸,還有單個伺服器的磁碟空間,內存,tps等非常吃緊。 所以我們要拆分到多個伺服器上,這樣上面的問題都解決了,以後也不會面對單機資源問題。
資料庫業務層面的拆分,和服務的「治理」,「降級」機制類似,也能對不同業務的數據分別的進行管理,維護,監控,擴展等。 資料庫往往最容易成為應用系統的瓶頸,而資料庫本身屬於「有狀態」的,相對於Web和應用伺服器來講,是比較難實現「橫向擴展」的。 資料庫的連接資源比較寶貴且單機處理能力也有限,在高並發場景下,垂直分庫一定程度上能夠突破IO、連接數及單機硬體資源的瓶頸。
水平分表
針對數據量巨大的單張表(比如訂單表),按照某種規則(RANGE,HASH取模等),切分到多張表裡面去。 但是這些表還是在同一個庫中,所以庫級別的資料庫操作還是有IO瓶頸。不建議採用。
水平分庫分表
將單張表的數據切分到多個伺服器上去,每個伺服器具有相應的庫與表,只是表中數據集合不同。 水平分庫分表能夠有效的緩解單機和單庫的性能瓶頸和壓力,突破IO、連接數、硬體資源等的瓶頸。
水平分庫分表切分規則
1. RANGE
從0到10000一個表,10001到20000一個表;
2. HASH取模
一個商場系統,一般都是將用戶,訂單作為主表,然後將和它們相關的作為附表,這樣不會造成跨庫事務之類的問題。 取用戶id,然後hash取模,分配到不同的資料庫上。
3. 地理區域
比如按照華東,華南,華北這樣來區分業務,七牛雲應該就是如此。
4. 時間
按照時間切分,就是將6個月前,甚至一年前的數據切出去放到另外的一張表,因為隨著時間流逝,這些表的數據 被查詢的概率變小,所以沒必要和「熱數據」放在一起,這個也是「冷熱數據分離」。
分庫分表後面臨的問題
事務支持
分庫分表後,就成了分布式事務了。如果依賴資料庫本身的分布式事務管理功能去執行事務,將付出高昂的性能代價; 如果由應用程序去協助控制,形成程序邏輯上的事務,又會造成編程方面的負擔。
跨庫join
只要是進行切分,跨節點Join的問題是不可避免的。但是良好的設計和切分卻可以減少此類情況的發生。解決這一問題的普遍做法是分兩次查詢實現。在第一次查詢的結果集中找出關聯數據的id,根據這些id發起第二次請求得到關聯數據。
跨節點的count,order by,group by以及聚合函數問題
這些是一類問題,因為它們都需要基於全部數據集合進行計算。多數的代理都不會自動處理合並工作。解決方案:與解決跨節點join問題的類似,分別在各個節點上得到結果後在應用程序端進行合並。和join不同的是每個結點的查詢可以並行執行,因此很多時候它的速度要比單一大錶快很多。但如果結果集很大,對應用程序內存的消耗是一個問題。
數據遷移,容量規劃,擴容等問題
來自淘寶綜合業務平台團隊,它利用對2的倍數取余具有向前兼容的特性(如對4取余得1的數對2取余也是1)來分配數據,避免了行級別的數據遷移,但是依然需要進行表級別的遷移,同時對擴容規模和分表數量都有限制。總得來說,這些方案都不是十分的理想,多多少少都存在一些缺點,這也從一個側面反映出了Sharding擴容的難度。
ID問題
一旦資料庫被切分到多個物理結點上,我們將不能再依賴資料庫自身的主鍵生成機制。一方面,某個分區資料庫自生成的ID無法保證在全局上是唯一的;另一方面,應用程序在插入數據之前需要先獲得ID,以便進行SQL路由.
一些常見的主鍵生成策略
UUID
使用UUID作主鍵是最簡單的方案,但是缺點也是非常明顯的。由於UUID非常的長,除佔用大量存儲空間外,最主要的問題是在索引上,在建立索引和基於索引進行查詢時都存在性能問題。
Twitter的分布式自增ID演算法Snowflake
在分布式系統中,需要生成全局UID的場合還是比較多的,twitter的snowflake解決了這種需求,實現也還是很簡單的,除去配置信息,核心代碼就是毫秒級時間41位 機器ID 10位 毫秒內序列12位。
跨分片的排序分頁
一般來講,分頁時需要按照指定欄位進行排序。當排序欄位就是分片欄位的時候,我們通過分片規則可以比較容易定位到指定的分片,而當排序欄位非分片欄位的時候,情況就會變得比較復雜了。為了最終結果的准確性,我們需要在不同的分片節點中將數據進行排序並返回,並將不同分片返回的結果集進行匯總和再次排序,最後再返回給用戶。
㈣ MySQL性能調優 – 你必須了解的15個重要變數
前言:
MYSQL 應該是最流行了 WEB 後端資料庫。雖然 NOSQL 最近越來越多的被提到,但是相信大部分架構師還是會選擇 MYSQL 來做數據存儲。本文作者總結梳理MySQL性能調優的15個重要變數,又不足需要補充的還望大佬指出。
1.DEFAULT_STORAGE_ENGINE
如果你已經在用MySQL 5.6或者5.7,並且你的數據表都是InnoDB,那麼表示你已經設置好了。如果沒有,確保把你的表轉換為InnoDB並且設置default_storage_engine為InnoDB。
為什麼?簡而言之,因為InnoDB是MySQL(包括Percona Server和MariaDB)最好的存儲引擎 – 它支持事務,高並發,有著非常好的性能表現(當配置正確時)。這里有詳細的版本介紹為什麼
2.INNODB_BUFFER_POOL_SIZE
這個是InnoDB最重要變數。實際上,如果你的主要存儲引擎是InnoDB,那麼對於你,這個變數對於MySQL是最重要的。
基本上,innodb_buffer_pool_size指定了MySQL應該分配給InnoDB緩沖池多少內存,InnoDB緩沖池用來存儲緩存的數據,二級索引,臟數據(已經被更改但沒有刷新到硬碟的數據)以及各種內部結構如自適應哈希索引。
根據經驗,在一個獨立的MySQL伺服器應該分配給MySQL整個機器總內存的80%。如果你的MySQL運行在一個共享伺服器,或者你想知道InnoDB緩沖池大小是否正確設置,詳細請看這里。
3.INNODB_LOG_FILE_SIZE
InnoDB重做日誌文件的設置在MySQL社區也叫做事務日誌。直到MySQL 5.6.8事務日誌默認值innodb_log_file_size=5M是唯一最大的InnoDB性能殺手。從MySQL 5.6.8開始,默認值提升到48M,但對於許多稍繁忙的系統,還遠遠要低。
根據經驗,你應該設置的日誌大小能在你伺服器繁忙時能存儲1-2小時的寫入量。如果不想這么麻煩,那麼設置1-2G的大小會讓你的性能有一個不錯的表現。這個變數也相當重要,更詳細的介紹請看這里。
當然,如果你有大量的大事務更改,那麼,更改比默認innodb日誌緩沖大小更大的值會對你的性能有一定的提高,但是你使用的是autocommit,或者你的事務更改小於幾k,那還是保持默認的值吧。
4.INNODB_FLUSH_LOG_AT_TRX_COMMIT
默認下,innodb_flush_log_at_trx_commit設置為1表示InnoDB在每次事務提交後立即刷新同步數據到硬碟。如果你使用autocommit,那麼你的每一個INSERT, UPDATE或DELETE語句都是一個事務提交。
同步是一個昂貴的操作(特別是當你沒有寫回緩存時),因為它涉及對硬碟的實際同步物理寫入。所以如果可能,並不建議使用默認值。
兩個可選的值是0和2:
* 0表示刷新到硬碟,但不同步(提交事務時沒有實際的IO操作)
* 2表示不刷新和不同步(也沒有實際的IO操作)
所以你如果設置它為0或2,則同步操作每秒執行一次。所以明顯的缺點是你可能會丟失上一秒的提交數據。具體來說,你的事務已經提交了,但伺服器馬上斷電了,那麼你的提交相當於沒有發生過。
顯示的,對於金融機構,如銀行,這是無法忍受的。不過對於大多數網站,可以設置為innodb_flush_log_at_trx_commit=0|2,即使伺服器最終崩潰也沒有什麼大問題。畢竟,僅僅在幾年前有許多網站還是用MyISAM,當崩潰時會丟失30s的數據(更不要提那令人抓狂的慢修復進程)。
那麼,0和2之間的實際區別是什麼?性能明顯的差異是可以忽略不計,因為刷新到操作系統緩存的操作是非常快的。所以很明顯應該設置為0,萬一MySQL崩潰(不是整個機器),你不會丟失任何數據,因為數據已經在OS緩存,最終還是會同步到硬碟的。
5.SYNC_BINLOG
已經有大量的文檔寫到sync_binlog,以及它和innodb_flush_log_at_trx_commit的關系,下面我們來簡單的介紹下:
a) 如果你的伺服器沒有設置從伺服器,而且你不做備份,那麼設置sync_binlog=0將對性能有好處。
b) 如果你有從伺服器並且做備份,但你不介意當主伺服器崩潰時在二進制日誌丟失一些事件,那麼為了更好的性能還是設置為sync_binlog=0.
c) 如果你有從伺服器並且備份,你非常在意從伺服器的一致性,以及能及時恢復到一個時間點(通過使用最新的一致性備份和二進制日誌將資料庫恢復到特定時間點的能力),那麼你應該設置innodb_flush_log_at_trx_commit=1,並且需要認真考慮使用sync_binlog=1。
問題是sync_binlog=1代價比較高 – 現在每個事務也要同步一次到硬碟。你可能會想為什麼不把兩次同步合並成一次,想法正確 – 新版本的MySQL(5.6和5.7,MariaDB和Percona Server)已經能合並提交,那麼在這種情況下sync_binlog=1的操作也不是這么昂貴了,但在舊的mysql版本中仍然會對性能有很大影響。
6.INNODB_FLUSH_METHOD
將innodb_flush_method設置為O_DIRECT以避免雙重緩沖.唯一一種情況你不應該使用O_DIRECT是當你操作系統不支持時。但如果你運行的是Linux,使用O_DIRECT來激活直接IO。
不用直接IO,雙重緩沖將會發生,因為所有的資料庫更改首先會寫入到OS緩存然後才同步到硬碟 – 所以InnoDB緩沖池和OS緩存會同時持有一份相同的數據。特別是如果你的緩沖池限制為總內存的50%,那意味著在寫密集的環境中你可能會浪費高達50%的內存。如果沒有限制為50%,伺服器可能由於OS緩存的高壓力會使用到swap。
簡單地說,設置為innodb_flush_method=O_DIRECT。
7.INNODB_BUFFER_POOL_INSTANCES
MySQL 5.5引入了緩沖實例作為減小內部鎖爭用來提高MySQL吞吐量的手段。
在5.5版本這個對提升吞吐量幫助很小,然後在MySQL 5.6版本這個提升就非常大了,所以在MySQL5.5中你可能會保守地設置innodb_buffer_pool_instances=4,在MySQL 5.6和5.7中你可以設置為8-16個緩沖池實例。
你設置後觀察會覺得性能提高不大,但在大多數高負載情況下,它應該會有不錯的表現。
對了,不要指望這個設置能減少你單個查詢的響應時間。這個是在高並發負載的伺服器上才看得出區別。比如多個線程同時做許多事情。
8.INNODB_THREAD_CONCURRENCY
InnoDB有一種方法來控制並行執行的線程數 – 我們稱為並發控制機制。大部分是由innodb_thread_concurrency值來控制的。如果設置為0,並發控制就關閉了,因此InnoDB會立即處理所有進來的請求(盡可能多的)。
在你有32CPU核心且只有4個請求時會沒什麼問題。不過想像下你只有4CPU核心和32個請求時 – 如果你讓32個請求同時處理,你這個自找麻煩。因為這些32個請求只有4 CPU核心,顯然地會比平常慢至少8倍(實際上是大於8倍),而然這些請求每個都有自己的外部和內部鎖,這有很大可能堆積請求。
下面介紹如何更改這個變數,在mysql命令行提示符執行:
對於大多數工作負載和伺服器,設置為8是一個好開端,然後你可以根據伺服器達到了這個限制而資源使用率利用不足時逐漸增加。可以通過show engine innodb statusG來查看目前查詢處理情況,查找類似如下行:
9.SKIP_NAME_RESOLVE
這一項不得不提及,因為仍然有很多人沒有添加這一項。你應該添加skip_name_resolve來避免連接時DNS解析。
大多數情況下你更改這個會沒有什麼感覺,因為大多數情況下DNS伺服器解析會非常快。不過當DNS伺服器失敗時,它會出現在你伺服器上出現「unauthenticated connections」 ,而就是為什麼所有的請求都突然開始慢下來了。
所以不要等到這種事情發生才更改。現在添加這個變數並且避免基於主機名的授權。
10.INNODB_IO_CAPACITY, INNODB_IO_CAPACITY_MAX
* innodb_io_capacity:用來當刷新臟數據時,控制MySQL每秒執行的寫IO量。
* innodb_io_capacity_max: 在壓力下,控制當刷新臟數據時MySQL每秒執行的寫IO量
首先,這與讀取無關 – SELECT查詢執行的操作。對於讀操作,MySQL會盡最大可能處理並返回結果。至於寫操作,MySQL在後台會循環刷新,在每一個循環會檢查有多少數據需要刷新,並且不會用超過innodb_io_capacity指定的數來做刷新操作。這也包括更改緩沖區合並(在它們刷新到磁碟之前,更改緩沖區是輔助臟頁存儲的關鍵)。
第二,我需要解釋一下什麼叫「在壓力下」,MySQL中稱為」緊急情況」,是當MySQL在後台刷新時,它需要刷新一些數據為了讓新的寫操作進來。然後,MySQL會用到innodb_io_capacity_max。
那麼,應該設置innodb_io_capacity和innodb_io_capacity_max為什麼呢?
最好的方法是測量你的存儲設置的隨機寫吞吐量,然後給innodb_io_capacity_max設置為你的設備能達到的最大IOPS。innodb_io_capacity就設置為它的50-75%,特別是你的系統主要是寫操作時。
通常你可以預測你的系統的IOPS是多少。例如由8 15k硬碟組成的RAID10能做大約每秒1000隨機寫操作,所以你可以設置innodb_io_capacity=600和innodb_io_capacity_max=1000。許多廉價企業SSD可以做4,000-10,000 IOPS等。
這個值設置得不完美問題不大。但是,要注意默認的200和400會限制你的寫吞吐量,因此你可能偶爾會捕捉到刷新進程。如果出現這種情況,可能是已經達到你硬碟的寫IO吞吐量,或者這個值設置得太小限制了吞吐量。
11.INNODB_STATS_ON_METADATA
如果你跑的是MySQL 5.6或5.7,你不需要更改innodb_stats_on_metadata的默認值,因為它已經設置正確了。
不過在MySQL 5.5或5.1,強烈建議關閉這個變數 – 如果是開啟,像命令show table status會立即查詢INFORMATION_SCHEMA而不是等幾秒再執行,這會使用到額外的IO操作。
從5.1.32版本開始,這個是動態變數,意味著你不需要重啟MySQL伺服器來關閉它。
12.INNODB_BUFFER_POOL_DUMP_AT_SHUTDOWN & INNODB_BUFFER_POOL_LOAD_AT_STARTUP
innodb_buffer_pool_mp_at_shutdown和innodb_buffer_pool_load_at_startup這兩個變數與性能無關,不過如果你偶爾重啟mysql伺服器(如生效配置),那麼就有關。當兩個都激活時,MySQL緩沖池的內容(更具體地說,是緩存頁)在停止MySQL時存儲到一個文件。當你下次啟動MySQL時,它會在後台啟動一個線程來載入緩沖池的內容以提高預熱速度到3-5倍。
兩件事:
第一,它實際上沒有在關閉時復制緩沖池內容到文件,僅僅是復製表空間ID和頁面ID – 足夠的信息來定位硬碟上的頁面了。然後它就能以大量的順序讀非常快速的載入那些頁面,而不是需要成千上萬的小隨機讀。
第二,啟動時是在後台載入內容,因為MySQL不需要等到緩沖池內容載入完成再開始接受請求(所以看起來不會有什麼影響)。
從MySQL 5.7.7開始,默認只有25%的緩沖池頁面在mysql關閉時存儲到文件,但是你可以控制這個值 – 使用innodb_buffer_pool_mp_pct,建議75-100。
這個特性從MySQL 5.6才開始支持。
13.INNODB_ADAPTIVE_HASH_INDEX_PARTS
如果你運行著一個大量SELECT查詢的MySQL伺服器(並且已經盡可能優化),那麼自適應哈希索引將下你的下一個瓶頸。自適應哈希索引是InnoDB內部維護的動態索引,可以提高最常用的查詢模式的性能。這個特性可以重啟伺服器關閉,不過默認下在mysql的所有版本開啟。
這個技術非常復雜,在大多數情況下它會對大多數類型的查詢直到加速的作用。不過,當你有太多的查詢往資料庫,在某一個點上它會花過多的時間等待AHI鎖和閂鎖。
如果你的是MySQL 5.7,沒有這個問題 – innodb_adaptive_hash_index_parts默認設置為8,所以自適應哈希索引被切割為8個分區,因為不存在全局互斥。
不過在mysql 5.7前的版本,沒有AHI分區數量的控制。換句話說,有一個全局互斥鎖來保護AHI,可能導致你的select查詢經常撞牆。
所以如果你運行的是5.1或5.6,並且有大量的select查詢,最簡單的方案就是切換成同一版本的Percona Server來激活AHI分區。
14.QUERY_CACHE_TYPE
如果人認為查詢緩存效果很好,肯定應該使用它。好吧,有時候是有用的。不過這個只在你在低負載時有用,特別是在低負載下大多數是讀取,小量寫或者沒有。
如果是那樣的情況,設置query_cache_type=ON和query_cache_size=256M就好了。不過記住不能把256M設置更高的值了,否則會由於查詢緩存失效時,導致引起嚴重的伺服器停頓。
如果你的MySQL伺服器高負載動作,建議設置query_cache_size=0和query_cache_type=OFF,並重啟伺服器生效。那樣Mysql就會停止在所有的查詢使用查詢緩存互斥鎖。
15.TABLE_OPEN_CACHE_INSTANCES
從MySQL 5.6.6開始,表緩存能分割到多個分區。
表緩存用來存放目前已打開表的列表,當每一個表打開或關閉互斥體就被鎖定 – 即使這是一個隱式臨時表。使用多個分區絕對減少了潛在的爭用。
從MySQL 5.7.8開始,table_open_cache_instances=16是默認的配置。
歡迎做Java的工程師朋友們私信我資料免費獲取免費的Java架構學習資料(裡面有高可用、高並發、高性能及分布式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)
其中覆蓋了互聯網的方方面面,期間碰到各種產品各種場景下的各種問題,很值得大家借鑒和學習,擴展自己的技術廣度和知識面。
㈤ MySQL資料庫性能優化有哪些技巧
1.存儲引擎的選擇如果數據表需要事務處理,應該考慮使用InnoDB,因為它完全符合ACID特性。如果不需要事務處理,使用默認存儲引擎MyISAM是比較明智的。並且不要嘗試同時使用這兩個存儲引擎。思考一下:在一個事務處理中,一些數據表使用InnoDB,而其餘的使用MyISAM.結果呢?整個subject將被取消,只有那些在事務處理中的被帶回到原始狀態,其餘的被提交的數據轉存,這將導致整個資料庫的沖突。然而存在一個簡單的方法可以同時利用兩個存儲引擎的優勢。目前大多數MySQL套件中包括InnoDB、編譯器和鏈表,但如果你選擇MyISAM,你仍然可以單獨下載InnoDB,並把它作為一個插件。很簡單的方法,不是嗎?
2.計數問題如果數據表採用的存儲引擎支持事務處理(如InnoDB),你就不應使用COUNT(*)計算數據表中的行數。這是因為在產品類資料庫使用COUNT(*),最多返回一個近似值,因為在某個特定時間,總有一些事務處理正在運行。如果使用COUNT(*)顯然會產生bug,出現這種錯誤結果。
3.反復測試查詢查詢最棘手的問題並不是無論怎樣小心總會出現錯誤,並導致bug出現。恰恰相反,問題是在大多數情況下bug出現時,應用程序或資料庫已經上線。的確不存在針對該問題切實可行的解決方法,除非將測試樣本在應用程序或資料庫上運行。任何資料庫查詢只有經過上千個記錄的大量樣本測試,才能被認可。
4.避免全表掃描通常情況下,如果MySQL(或者其他關系資料庫模型)需要在數據表中搜索或掃描任意特定記錄時,就會用到全表掃描。此外,通常最簡單的方法是使用索引表,以解決全表掃描引起的低效能問題。然而,正如我們在隨後的問題中看到的,這存在錯誤部分。
5.使用「EXPLAIN」進行查詢當需要調試時,EXPLAIN是一個很好的命令,下面將對EXPLAIN進行深入探討。
㈥ MySQL資料庫性能優化之分區分表分庫
分表是分散資料庫壓力的好方法。
分表,最直白的意思,就是將一個表結構分為多個表,然後,可以再同一個庫里,也可以放到不同的庫。
當然,首先要知道什麼情況下,才需要分表。個人覺得單表記錄條數達到百萬到千萬級別時就要使用分表了。
分表的分類
**1、縱向分表**
將本來可以在同一個表的內容,人為劃分為多個表。(所謂的本來,是指按照關系型資料庫的第三範式要求,是應該在同一個表的。)
分表理由:根據數據的活躍度進行分離,(因為不同活躍的數據,處理方式是不同的)
案例:
對於一個博客系統,文章標題,作者,分類,創建時間等,是變化頻率慢,查詢次數多,而且最好有很好的實時性的數據,我們把它叫做冷數據。而博客的瀏覽量,回復數等,類似的統計信息,或者別的變化頻率比較高的數據,我們把它叫做活躍數據。所以,在進行資料庫結構設計的時候,就應該考慮分表,首先是縱向分表的處理。
這樣縱向分表後:
首先存儲引擎的使用不同,冷數據使用MyIsam 可以有更好的查詢數據。活躍數據,可以使用Innodb ,可以有更好的更新速度。
其次,對冷數據進行更多的從庫配置,因為更多的操作時查詢,這樣來加快查詢速度。對熱數據,可以相對有更多的主庫的橫向分表處理。
其實,對於一些特殊的活躍數據,也可以考慮使用memcache ,redis之類的緩存,等累計到一定量再去更新資料庫。或者mongodb 一類的nosql 資料庫,這里只是舉例,就先不說這個。
**2、橫向分表**
字面意思,就可以看出來,是把大的表結構,橫向切割為同樣結構的不同表,如,用戶信息表,user_1,user_2等。表結構是完全一樣,但是,根據某些特定的規則來劃分的表,如根據用戶ID來取模劃分。
分表理由:根據數據量的規模來劃分,保證單表的容量不會太大,從而來保證單表的查詢等處理能力。
案例:同上面的例子,博客系統。當博客的量達到很大時候,就應該採取橫向分割來降低每個單表的壓力,來提升性能。例如博客的冷數據表,假如分為100個表,當同時有100萬個用戶在瀏覽時,如果是單表的話,會進行100萬次請求,而現在分表後,就可能是每個表進行1萬個數據的請求(因為,不可能絕對的平均,只是假設),這樣壓力就降低了很多很多。
延伸:為什麼要分表和分區?
日常開發中我們經常會遇到大表的情況,所謂的大表是指存儲了百萬級乃至千萬級條記錄的表。這樣的表過於龐大,導致資料庫在查詢和插入的時候耗時太長,性能低下,如果涉及聯合查詢的情況,性能會更加糟糕。分表和表分區的目的就是減少資料庫的負擔,提高資料庫的效率,通常點來講就是提高表的增刪改查效率。
什麼是分表?
分表是將一個大表按照一定的規則分解成多張具有獨立存儲空間的實體表,我們可以稱為子表,每個表都對應三個文件,MYD數據文件,.MYI索引文件,.frm表結構文件。這些子表可以分布在同一塊磁碟上,也可以在不同的機器上。app讀寫的時候根據事先定義好的規則得到對應的子表名,然後去操作它。
什麼是分區?
分區和分表相似,都是按照規則分解表。不同在於分表將大表分解為若干個獨立的實體表,而分區是將數據分段劃分在多個位置存放,可以是同一塊磁碟也可以在不同的機器。分區後,表面上還是一張表,但數據散列到多個位置了。app讀寫的時候操作的還是大表名字,db自動去組織分區的數據。
**MySQL分表和分區有什麼聯系呢?**
1、都能提高mysql的性高,在高並發狀態下都有一個良好的表現。
2、分表和分區不矛盾,可以相互配合的,對於那些大訪問量,並且表數據比較多的表,我們可以採取分表和分區結合的方式(如果merge這種分表方式,不能和分區配合的話,可以用其他的分表試),訪問量不大,但是表數據很多的表,我們可以採取分區的方式等。
3、分表技術是比較麻煩的,需要手動去創建子表,app服務端讀寫時候需要計運算元表名。採用merge好一些,但也要創建子表和配置子表間的union關系。
4、表分區相對於分表,操作方便,不需要創建子表。
我們知道對於大型的互聯網應用,資料庫單表的數據量可能達到千萬甚至上億級別,同時面臨這高並發的壓力。Master-Slave結構只能對資料庫的讀能力進行擴展,寫操作還是集中在Master中,Master並不能無限制的掛接Slave庫,如果需要對資料庫的吞吐能力進行進一步的擴展,可以考慮採用分庫分表的策略。
**1、分表**
在分表之前,首先要選中合適的分表策略(以哪個字典為分表欄位,需要將數據分為多少張表),使數據能夠均衡的分布在多張表中,並且不影響正常的查詢。在企業級應用中,往往使用org_id(組織主鍵)做為分表欄位,在互聯網應用中往往是userid。在確定分表策略後,當數據進行存儲及查詢時,需要確定到哪張表裡去查找數據,
數據存放的數據表 = 分表欄位的內容 % 分表數量
**2、分庫**
分表能夠解決單表數據量過大帶來的查詢效率下降的問題,但是不能給資料庫的並發訪問帶來質的提升,面對高並發的寫訪問,當Master無法承擔高並發的寫入請求時,不管如何擴展Slave伺服器,都沒有意義了。我們通過對資料庫進行拆分,來提高資料庫的寫入能力,即所謂的分庫。分庫採用對關鍵字取模的方式,對資料庫進行路由。
數據存放的資料庫=分庫欄位的內容%資料庫的數量
**3、即分表又分庫**
資料庫分表可以解決單表海量數據的查詢性能問題,分庫可以解決單台資料庫的並發訪問壓力問題。
當資料庫同時面臨海量數據存儲和高並發訪問的時候,需要同時採取分表和分庫策略。一般分表分庫策略如下:
中間變數 = 關鍵字%(資料庫數量*單庫數據表數量)
庫 = 取整(中間變數/單庫數據表數量)
表 = (中間變數%單庫數據表數量)
實例:
1、分庫分表
很明顯,一個主表(也就是很重要的表,例如用戶表)無限制的增長勢必嚴重影響性能,分庫與分表是一個很不錯的解決途徑,也就是性能優化途徑,現在的案例是我們有一個1000多萬條記錄的用戶表members,查詢起來非常之慢,同事的做法是將其散列到100個表中,分別從members0到members99,然後根據mid分發記錄到這些表中,牛逼的代碼大概是這樣子:
復制代碼 代碼如下:
<?php
for($i=0;$i< 100; $i++ ){
//echo "CREATE TABLE db2.members{$i} LIKE db1.members
";
echo "INSERT INTO members{$i} SELECT * FROM members WHERE mid%100={$i}
";
}
?>
2、不停機修改mysql表結構
同樣還是members表,前期設計的表結構不盡合理,隨著資料庫不斷運行,其冗餘數據也是增長巨大,同事使用了下面的方法來處理:
先創建一個臨時表:
/*創建臨時表*/
CREATE TABLE members_tmp LIKE members
然後修改members_tmp的表結構為新結構,接著使用上面那個for循環來導出數據,因為1000萬的數據一次性導出是不對的,mid是主鍵,一個區間一個區間的導,基本是一次導出5萬條吧,這里略去了
接著重命名將新表替換上去:
/*這是個頗為經典的語句哈*/
RENAME TABLE members TO members_bak,members_tmp TO members;
就是這樣,基本可以做到無損失,無需停機更新表結構,但實際上RENAME期間表是被鎖死的,所以選擇在線少的時候操作是一個技巧。經過這個操作,使得原先8G多的表,一下子變成了2G多。