關於“select count(*)”和“select count(col)”的效率比較(參考http://hi.baidu.com/wrjgg/item/f5b823b4e1dca79918469729):
1.任何情況下SELECT COUNT(*) FROM tablename是最優選擇;
2.儘量減少SELECT COUNT(*) FROM tablename WHERE COL = 'value’ 這種查詢;
3.杜絕SELECT COUNT(COL) FROM tablename的出現
1.關於多列合在一起作爲unique key的情況需要注意:如果columnA、columnB、columnC合在一起作爲unique key,那麼三項都不要“可爲空”,因爲如果columnA可爲空,則會出現多條columnA爲空、columnB和columnC分別相等的記錄(數據庫不報錯)
2.使用--log-slow-queries –-long-query-time=2查看查詢比較慢的語句。然後使用explain分析查詢,做出優化(log_queries_not_using_indexes
記錄未使用索引的查詢)
3.嘗試避免在頻繁更新的表上執行復雜的SELECT查詢,以避免與鎖定表有關的由於讀、寫衝突發生的問題
4.儘量不要讓列可爲null,不利於mysql優化
5.對於查詢佔主要的應用來說,索引顯得尤爲重要。很多時候性能問題很簡單的就是因爲我們忘了添加索引而造成的,或者說沒有添加更爲有效的索引導致。
如果不加索引的話,那麼查找任何哪怕只是一條特定的數據都會進行一次全表掃描,如果一張表的數據量很大而符合條件的結果又很少,那麼不加索引會引起致命的性能下降。
但是也不是什麼情況都非得建索引不可,比如性別可能就只有兩個值,建索引不僅沒什麼優勢,還會影響到更新速度,這被稱爲過度索引。
6.建索引的幾個要點:
1>複合索引
比如有一條語句是這樣的:select * from users where area=’beijing’ and age=22;
如果我們是在area和age上分別創建單個索引的話,由於mysql查詢每次只能使用一個索引,所以雖然這樣已經相對不做索引時全表掃描提高了很多效率,但是如果在area、age兩列上創建複合索引的話將帶來更高的效率。如果我們創建了(area,age,salary)的複合索引,那麼其實相當於創建了(area,age,salary)、(area,age)、(area)三個索引,這被稱爲最佳左前綴特性。因此我們在創建複合索引時應該將最常用作限制條件的列放在最左邊,依次遞減。
2>索引不會包含有NULL值的列
只要列中包含有NULL值都將不會被包含在索引中,複合索引中只要有一列含有NULL值,那麼這一列對於此複合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值爲NULL。
3>使用短索引
對串列進行索引,如果可能應該指定一個前綴長度。例如,如果有一個CHAR(255)的 列,如果在前10 個或20 個字符內,多數值是惟一的,那麼就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
4>排序的索引問題
mysql查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作;儘量不要包含多個列的排序,如果需要最好給這些列創建複合索引。
5>like語句操作
一般情況下不鼓勵使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引而like “aaa%”可以使用索引。
6>不要在列上進行運算
select * from users where YEAR(adddate) ...
7>不使用NOT IN
NOT IN不會使用索引將進行全表掃描。NOT IN可以NOT EXISTS代替。
7.主鍵和唯一索引的區別(django1.4還不支持composite primary key,有一些擴展包提供該功能,但是他們建立的所謂的“組合主鍵”到了數據庫層面居然是唯一索引,所以纔對這兩個概念有些迷惑)
主鍵是一種約束,唯一索引是一種索引,兩者在本質上是不同的。
主鍵創建後一定包含一個唯一性索引,唯一性索引並不一定就是主鍵。
唯一性索引列允許空值,而主鍵列不允許爲空值。
主鍵列在創建時,已經默認爲空值 + 唯一索引了。
主鍵可以被其他表引用爲外鍵,而唯一索引不能。
一個表最多隻能創建一個主鍵,但可以創建多個唯一索引。
主鍵更適合那些不容易更改的唯一標識,如自動遞增列、身份證號等。
在 RBO 模式下,主鍵的執行計劃優先級要高於唯一索引。 兩者可以提高查詢的速度。
8.innodb表修改索引(已驗證的是刪索引)會很慢,myisam表就很快(show processlist跟蹤到的過程:copy to tmp table; repair by sorting; rename table)
9.innodb表的數據不在mysql數據目錄下:mysql數據目錄下只有一個非常小(十幾KB左右)的frm文件,沒有MYD文件和MYI文件 (http://dev.mysql.com/doc/refman/5.0/en/innodb-configuration.html)
InnoDB
creates tablespace files in the MySQL data directory by default. To specify a location explicitly, use theinnodb_data_home_dir
option. For example, to use two files namedibdata1
andibdata2
but create them in the/ibdata
directory, configureInnoDB
like this:
[mysqld] innodb_data_home_dir = /ibdata innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend
10.select * from a left join b on a.id = b.id; 左聯接,結果以a.*,b.*顯示,左側(a.*)保留,右側(b.*)用NULL填充
select * from a right join b on a.id = b.id; 右聯接,結果以a.*,b.*顯示,左側(a.*)用NULL填充,右側(b.*)保留
select * from a inner join b on a.id = b.id; 內聯接(相等聯接),等價於select * from a,b where a.id = b.id;
11.select * from a where a.id > 1 and a.id < 10; select * from a where a.id < 10 and a.id > 1; 後者效率比較高,因爲id<10把範圍縮小了,整個執行會更快
12.mysql分區和分表(merge存儲引擎,或者業務層面實現)的側重點不同,分表重點是存取數據時,如何提高mysql併發能力上;而分區呢,如何突破磁盤的讀寫能力,從而達到提高mysql性能的目的。http://blog.51yip.com/mysql/1029.html
分表:業務層面控制分表、merge存儲引擎分表http://blog.51yip.com/mysql/949.html
分區:range分區、list分區、hash分區、key分區、子分區 http://blog.51yip.com/mysql/1013.html
- ./configure --help |grep -A 3 Partition
- mysql> show variables like "%part%";
- +-------------------+-------+
- | Variable_name | Value |
- +-------------------+-------+
- | have_partitioning | YES |
- +-------------------+-------+
- 1 row in set (0.00 sec)
- range分區:
- CREATE TABLE IF NOT EXISTS `user` (
- `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用戶ID',
- `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名稱',
- `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0爲男,1爲女',
- PRIMARY KEY (`id`)
- ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1
- PARTITION BY RANGE (id) (
- PARTITION p0 VALUES LESS THAN (3),
- PARTITION p1 VALUES LESS THAN (6),
- PARTITION p2 VALUES LESS THAN (9),
- PARTITION p3 VALUES LESS THAN (12),
- PARTITION p4 VALUES LESS THAN MAXVALUE
- );
- list分區:(如果有主銉的話,分區時主鍵必須在其中,不然就會報錯。一般情況下,一個張表肯定會有一個主鍵,這算是一個分區的侷限性吧。)
- CREATE TABLE IF NOT EXISTS `list_part` (
- `id` int(11) NOT NULL COMMENT '用戶ID',
- `province_id` int(2) NOT NULL DEFAULT 0 COMMENT '省',
- `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名稱',
- `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0爲男,1爲女'
- ) ENGINE=INNODB DEFAULT CHARSET=utf8
- PARTITION BY LIST (province_id) (
- PARTITION p0 VALUES IN (1,2,3,4,5,6,7,8),
- PARTITION p1 VALUES IN (9,10,11,12,16,21),
- PARTITION p2 VALUES IN (13,14,15,19),
- PARTITION p3 VALUES IN (17,18,20,22,23,24)
- );
13.like做模糊搜索效率非常低下(遍歷所有記錄),需要的話可用全文索引替換:http://db.090.net/2012/03/mysql%E5%85%A8%E6%96%87%E7%B4%A2%E5%BC%95/
14.查詢的時候只好準確提供符合列類型的數據做檢索,不要寄希望於Mysql很智能的檢查和轉換我們提供的數據
- mysql> show create table wom_account\G
- *************************** 1. row ***************************
- Table: wom_account
- Create Table: CREATE TABLE `wom_account` (
- ...
- `provider` smallint(6) NOT NULL,
- `uid` varchar(64) NOT NULL,
- UNIQUE KEY `provider` (`provider`,`uid`),
- ) ENGINE=MyISAM AUTO_INCREMENT=2518615 DEFAULT CHARSET=utf8
- 1 row in set (0.00 sec)
- mysql> explain * FROM wom_account WHERE provider=1 AND uid IN ('2840978914');
- +----+-------------+-------------+-------+---------------+----------+---------+-------------+------+-------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------------+-------+---------------+----------+---------+-------------+------+-------+
- | 1 | SIMPLE | wom_account | const | provider,uid | provider | 196 | const,const | 1 | |
- +----+-------------+-------------+-------+---------------+----------+---------+-------------+------+-------+
- 1 row in set (0.00 sec)
- mysql> explain SELECT * FROM wom_account WHERE provider=1 AND uid IN (2840978914);
- +----+-------------+-------------+------+---------------+----------+---------+-------+--------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------------+------+---------------+----------+---------+-------+--------+-------------+
- | 1 | SIMPLE | wom_account | ref | provider,uid | provider | 2 | const | 489995 | Using where |
- +----+-------------+-------------+------+---------------+----------+---------+-------+--------+-------------+
- 1 row in set (0.00 sec)
- mysql> explain SELECT * FROM wom_account WHERE provider=1 AND uid = 2840978914;
- +----+-------------+-------------+------+---------------+----------+---------+-------+--------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------------+------+---------------+----------+---------+-------+--------+-------------+
- | 1 | SIMPLE | wom_account | ref | provider,uid | provider | 2 | const | 489995 | Using where |
- +----+-------------+-------------+------+---------------+----------+---------+-------+--------+-------------+
- 1 row in set (0.00 sec)
- mysql> explain SELECT * FROM wom_account WHERE provider=1 AND uid = '2840978914';
- +----+-------------+-------------+-------+---------------+----------+---------+-------------+------+-------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------------+-------+---------------+----------+---------+-------------+------+-------+
- | 1 | SIMPLE | wom_account | const | provider,uid | provider | 196 | const,const | 1 | |
- +----+-------------+-------------+-------+---------------+----------+---------+-------------+------+-------+
- 1 row in set (0.00 sec)
下面參考http://www.tyhs.net.cn/article.asp?articleid=64
http://blog.csdn.net/maria57/article/details/2963410(這篇文章分析很詳細,innodb和myisam不同引擎的設置也都有講到)
索引
1) MySQL只會使用前綴,例如key(a, b) …where b=5 將使用不到索引。
2) 要選擇性的使用索引。在變化很少的列上使用索引並不是很好,例如性別列。
3) 在Unique列上定義Unique index。
4) 避免建立使用不到的索引。
5) 在Btree index中(InnoDB使用Btree),可以在需要排序的列上建立索引。6) 避免重複的索引。
7) 避免在已有索引的前綴上建立索引。例如:如果存在index(a,b)則去掉index(a)。
8) 控制單個索引的長度。使用key(name(8))在數據的前面幾個字符建立索引。
9) 越是短的鍵值越好,最好使用integer。
10) 在查詢中要使用到索引(使用explain查看),可以減少讀磁盤的次數,加速讀取數據。
11) 相近的鍵值比隨機好。Auto_increment就比uuid好。
12) Optimize table可以壓縮和排序index,注意不要頻繁運行。
13) Analyze table可以更新數據。
14)對於已建索引的字符串類型列如果用數值類型做查詢(where columnName = 100)索引就用不上,數據量大的時候查詢異常緩慢
服務器設置優化
MySQL默認的設置性能很差,所以要做一些參數的調整。這一節介紹一些通用的參數調整,不涉及具體的存儲引擎(主要指MyISAM,InnoDB)
--character-set:如果是單一語言使用簡單的character set例如latin1。儘量少用Utf-8,utf-8佔用空間較多。
--memlock:鎖定MySQL只能運行在內存中,避免 swapping,但是如果內存不夠時有可能出現錯誤。
--max_allowed_packet:要足夠大,以適應比較大的SQL查詢,對性能沒有太大影響,主要是避免出現packet錯誤。
--max_connections:server允許的最大連接。太大的話會出現out of memory。
--table_cache:MySQL在同一時間保持打開的table的數量。打開table開銷比較大。一般設置爲512。
如果你發現open_tables等於table_cache,並且opened_tables在不斷增長,那麼你就需要增加table_cache的值了(上述狀態值可以使用SHOW STATUS LIKE ‘Open%tables’獲得)
--query_cache_size: 用於緩存查詢的內存大小。
使用查詢緩衝,MySQL將SELECT語句和查詢結果存放在緩衝區中,今後對於同樣的SELECT語句(區分大小寫),將直接從緩衝區中讀取結果。
根據MySQL用戶手冊,使用查詢緩衝最多可以達到238%的效率。
通過檢查狀態值Qcache_*,可以知道query_cache_size設置是否合理(上述狀態值可以使用SHOW STATUS LIKE ‘Qcache%’獲得)。
如果Qcache_lowmem_prunes的值非常大,則表明經常出現緩衝不夠的情況,如果Qcache_hits的值也非常大,則表明查詢緩衝使用非常頻繁,此時需要增加緩衝大小;
如果Qcache_hits的值不大,則表明你的查詢重複率很低,這種情況下使用查詢緩衝反而會影響效率,那麼可以考慮不用查詢緩衝。
此外,在SELECT語句中加入SQL_NO_CACHE可以明確表示不使用查詢緩衝。
與查詢緩衝有關的參數還有query_cache_type、query_cache_limit、query_cache_min_res_unit。
query_cache_type指定是否使用查詢緩衝,可以設置爲0、1、2,該變量是SESSION級的變量。
query_cache_limit指定單個查詢能夠使用的緩衝區大小,缺省爲1M。
query_cache_min_res_unit是在4.1版本以後引入的,它指定分配緩衝區空間的最小單位,缺省爲4K。
檢查狀態值Qcache_free_blocks,如果該值非常大,則表明緩衝區中碎片很多,這就表明查詢結果都比較小,此時需要減小query_cache_min_res_unit。
--datadir:mysql存放數據的根目錄,和安裝文件分開在不同的磁盤可以提高一點性能。
--key_buffer_size:指定索引緩衝區的大小,它決定索引處理的速度,尤其是索引讀的速度。通過檢查狀態值Key_read_requests和Key_reads,可以知道key_buffer_size設置是否合理。
比例key_reads / key_read_requests應該儘可能的低,至少是1:100,1:1000更好(上述狀態值可以使用SHOW STATUS LIKE ‘key_read%’獲得)。
key_buffer_size只對MyISAM表起作用。即使你不使用MyISAM表,但是內部的臨時磁盤表是MyISAM表,也要使用該值。可以使用檢查狀態值created_tmp_disk_tables得知詳情。
對於1G內存的機器,如果不使用MyISAM表,推薦值是16M(8-64M)。
MyISAM優化要點
1) 聲明列爲NOT NULL,可以減少磁盤存儲。
2) 使用optimize table做碎片整理,回收空閒空間。注意僅僅在非常大的數據變化後運行。
3) Deleting/updating/adding大量數據的時候禁止使用index。使用ALTER TABLE t DISABLE KEYS。
4) 設置myisam_max_[extra]_sort_file_size足夠大,可以顯著提高repair table的速度。
項目中sql優化時的會議記錄:---> start
子查詢儘量要避免,因爲子查詢的結果會存成臨時表,而臨時表是無法加索引的。
臨時表只能逐行過濾
下面兩種寫法效果等同(“inner join”沒有"left"和“right”的語法,它就是“inner join”)
select * from a inner join b on a.y = b.y;
select * from a,b where a.y = b.y;
"left join"默認是"left outer join" (outer,不是out)
左外連接,如果 a left out join b,那麼如果在a中存在的記錄如果在b中不存在,則b中對應的列的值爲NULL,通過選擇b中列爲null的行就把結果選出來了
多個連接條件:http://dev.mysql.com/doc/refman/5.0/en/join.html
SELECT * FROM t1 LEFT JOIN (t2, t3, t4) ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)
查看數據庫版本:
SELECT version();
SELECT @@version;
《High.Performance.MySQL》(高性能MYSQL)需要閱讀的章節:1、4、5、6、7
1. client-server 結構
一個客戶連接在server端由一個線程處理(5.5的優化:線程池,一個線程可以服務於多個客戶端)
Query cache(sql語句緩存), Parser
讀寫鎖(寫鎖飢餓:寫操作因爲接連的讀操作而拿不到鎖,解決辦法是序列化請求鎖)
2. 事務,ACID(原子性、一致性、隔離性、持久性)
隔離級別:
READ UNCOMMITED, 可讀到沒提交的數據(髒數據),隔離級別最低
READ COMMITED, 只讀他人提交後的數據(同一個事務中兩次相同讀取的時間之間他人可以更新該數據)
REPEATABLE READ, 可重複讀(兩次讀取的數據一致,同一個事務中兩次相同讀取的時間之間他人不可以更新該行數據,但是可能會增加新的行)(幻讀,phantom read, range select, find new records)
T1:
read R1
... Tn: UPDATE R1(N) INSERT R2(Y)
read R1
SERIALIZABLE, 序列化
T1:
read R1
... Tn: UPDATE R1(N) INSERT R2(N)
read R1
3. MVCC
R1: data (insert version; delete version)
R1
R2
R3
...
Rn
insert version <= current_version
delete version == undifined OR delete version > current_version
4.SHOW TABLE STATUS LIKE 'user'; 查看錶user的狀態
5.select count(*) from a; 沒有where條件且引擎是MYISAM時速度很快,數據表有字段跟蹤行數
select count(id) from a; 統計的是id不爲空的行數
6.推薦用Oracle InnoDB替代開源社區的InnoDB(5.5後默認是前者)
7.varchar(5)是5個字符,不是5個字節(5個字符可能是15個字節...)
8.MYISAM修復比較慢,因爲沒有日誌要掃描整個文件;MYISAM索引是壓縮的,比較小一些;表級鎖
INNODB可以熱備、支持事務、恢復快,行級鎖
9.程序性能瓶頸:CPU 磁盤IO 內存 網絡IO
10.create table b like a; 創建和a一樣結構的新表b
11.用B+樹存索引而不是用二叉樹:優化磁盤讀寫
12.聚集索引(Clustered Indexes):數據和索引存在一起,找到索引就找到了數據
inno DB支持聚集索引,但只支持對primary key做聚集索引
InnoDB:(如果primary key很大的話索引文件就會很大、佔內存空間和磁盤空間;primary key需要儘量按照遞增的順序產生,不要隨機生成)
primary key: index + data
second index: index + primary key
MyISAM:
primary key: index + pointer to data
second index: index + pointer to data
13.對order by的列設置了索引,數據從磁盤讀出來的就會變成有序的,不需要再在內存裏面做排序
14. replace into
insert into t values (..) on duplicate key update ...
15.union 和 union all,再確定不重複的情況下最好用後者(無排序和排重操作,速度快)