Mysql 優化方案
運行狀態查詢
瞭解mysql數據庫的一些運行狀態如何查詢(比如想知道當前mysql運行的時間/一共執行了多少次select/update/delete.. / 當前連接)
show [seesion| gloable] status like ….
如:
//查詢運行時間
show status like ‘uptime’;
//select | insert |delete 語句執行次數
show status like ‘com_select’
show status like ‘com_insert’
show status like ‘com_delete’
// 查詢鏈接次數
show status like ‘connections’
// 查詢慢查詢語句
show status like 'slow_queries';
//默認慢查詢時間10秒
show variables like ‘long_query_time’;
set long_query_time=1 ;//可以修改慢查詢時間
// 慢查詢設置my.ini
log-slow-queries = D:/Mysql/mysql-5.6.27-winx64/slowquery.log
long_query_time = 2
字段優化
儘量使用TINYINT、SMALLINT、MEDIUM_INT作爲整數類型而非INT,如果非負則加上UNSIGNED
VARCHAR的長度只分配真正需要的空間
儘量使用TIMESTAMP而非DATETIME,
單表不要有太多字段,建議在20以內
避免使用NULL字段,很難查詢優化且佔用額外索引空間 default ”
用整型來存IP
索引使用優化
- 查詢是否使用索引 關鍵字:explain select …
- like 避免使用 %key(前匹配)
- 避免使or,適當用in 代替
- 字段類型爲字符串 必須用” 引起來,否則索引失效
- group by colm 默認會排序,建議使用 後面加order by null
- 能用鏈接的不用子查詢
- 避免在where 子句中使用is null 判斷 可以使用is not null
- 值很少的字段不適合建立索引
- 字符字段最好不要做主鍵
- 使用多列索引時主意順序和查詢條件保持一致,同時刪除不必要的單列索引
- 強制使用某索引 select * from table
force index(indexname)
或use - 前綴索引,當字段長度超過255需要定義截取 255
- 索引是表達式或函數的參數,索引被破壞
- 避免where 子句使用 != 和<> ,索引被破壞
- 對於連續數字,使用between a and b 代替 in
補充:索引使用定位
id
查詢順序標識
如:mysql> explain select * from (select nid,name from tb1 where nid < 10) as B;
+----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 9 | NULL |
| 2 | DERIVED | tb1 | range | PRIMARY | PRIMARY | 8 | NULL | 9 | Using where |
+----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
特別的:如果使用union連接氣值可能爲null
select_type
查詢類型
SIMPLE 簡單查詢
PRIMARY 最外層查詢
SUBQUERY 映射爲子查詢
DERIVED 子查詢
UNION 聯合
UNION RESULT 使用聯合的結果
...
table
正在訪問的表名
type
查詢時的訪問方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
ALL 全表掃描,對於數據表從頭到尾找一遍
select * from tb1;
特別的:如果有limit限制,則找到之後就不在繼續向下掃描
select * from tb1 where email = '[email protected]'
select * from tb1 where email = '[email protected]' limit 1;
雖然上述兩個語句都會進行全表掃描,第二句使用了limit,則找到一個後就不再繼續掃描。
INDEX 全索引掃描,對索引從頭到尾找一遍
select nid from tb1;
RANGE 對索引列進行範圍查找
select * from tb1 where name < 'alex';
PS:
between and
in
> >= < <= 操作
注意:!= 和 > 符號
INDEX_MERGE 合併索引,使用多個單列索引搜索
select * from tb1 where name = 'alex' or nid in (11,22,33);
REF 根據索引查找一個或多個值
select * from tb1 where name = 'seven';
EQ_REF 連接時使用primary key 或 unique類型
select tb2.nid,tb1.name from tb2 left join tb1 on tb2.nid = tb1.nid;
CONST 常量
表最多有一個匹配行,因爲僅有一行,在這行的列值可被優化器剩餘部分認爲是常數,const表很快,因爲它們只讀取一次。
select nid from tb1 where nid = 2 ;
SYSTEM 系統
表僅有一行(=系統表)。這是const聯接類型的一個特例。
select * from (select nid from tb1 where nid = 1) as A;
possible_keys
可能使用的索引
key
真實使用的
key_len
MySQL中使用索引字節長度
rows
mysql估計爲了找到所需的行而要讀取的行數 ------ 只是預估值
extra
該列包含MySQL解決查詢的詳細信息
“Using index”
此值表示mysql將使用覆蓋索引,以避免訪問表。不要把覆蓋索引和index訪問類型弄混了。
“Using where”
這意味着mysql服務器將在存儲引擎檢索行後再進行過濾,許多where條件裏涉及索引中的列,當(並且如果)它讀取索引時,就能被存儲引擎檢驗,因此不是所有帶where子句的查詢都會顯示“Using where”。有時“Using where”的出現就是一個暗示:查詢可受益於不同的索引。
“Using temporary”
這意味着mysql在對查詢結果排序時會使用一個臨時表。
“Using filesort”
這意味着mysql會對結果使用一個外部索引排序,而不是按索引次序從表裏讀取行。mysql有兩種文件排序算法,這兩種排序方式都可以在內存或者磁盤上完成,explain不會告訴你mysql將使用哪一種文件排序,也不會告訴你排序會在內存裏還是磁盤上完成。
“Range checked for each record(index map: N)”
分表優化
` 數據大 且訪問量大 建議使用分表(也可以聯合搭配分區)
除非單表數據未來會一直不斷上漲,否則不考慮拆分,拆分會帶來 邏輯部署等複雜問題,分表和表分區的目的就是減少數據庫的負擔,提高數據庫的效率,通常點來講就是提高表的增刪改查效率。
分表的特點:
1. 將一個大表按一定規則分解成多張具有 獨立的存儲空間實體子表
2. 獨立的數據文件、索引文件、表結構文件
3. 可以分佈到不同機器、磁盤上
4. 按照規則訪問定義好的子表
使用: 分表需要創建表,使用和維護比較麻煩
分區
訪問量不大,但是數據量大建議使用分區
1. 分區的好處是:
表面看還是一張表,是將數據分段存儲到多個位置
訪問還是一張表
分區表的數據更容易維護,可以通過清楚整個分區批量刪除大量數據,也可以增加新的分區來支持新插入的數據。另外,還可以對一個獨立分區進行優化、檢查、修復等操作
部分查詢能夠從查詢條件確定只落在少數分區上,速度會很快
分區表的數據還可以分佈在不同的物理設備上
可以使用分區表賴避免某些特殊瓶頸,例如InnoDB單個索引的互斥訪問、ext3文件系統的inode鎖競爭
可以備份和恢復單個分區
四種分區 使用方法:
查詢是否支持分區:SHOW VARIABLES LIKE ‘%partition%’;
1. Range 分區
-- 創建分區
DROP TABLE IF EXISTS t_range_text;
CREATE TABLE t_range_text(
id INT(64) NOT NULL auto_increment ,
`name` VARCHAR(64) NOT NULL DEFAULT '',
-- 這裏需要設置爲datatime 不能設置timesimple
createTime datetime NOT NULL,
-- 分區的字段需要建立索引
primary KEY(id,`createTime`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY RANGE (year(createTime))
(
PARTITION 2000_year VALUES less than(2000),
PARTITION 2005_year VALUES less than(2005),
PARTITION 2010_year VALUES less than(2010),
PARTITION 2015_year VALUES less than(2015),
PARTITION other_year VALUES less than MAXVALUE
);
-- 查看PARTITIONS字段是否使用分區
EXPLAIN PARTITIONS SELECT * FROM t_range_text;
-- 刪除數據或者刪除分區
ALTER TABLE t_range_text DROP PARTITION 2000_year;
-- 增加分區(有maxvalue分區無法加,且只範圍參數只能變大不能變小)
ALTER TABLE t_range_text ADD PARTITION (PARTITION 2000_year VALUES LESS THAN (2000));
-- 合併分區
ALTER TABLE t_range_text
REORGANIZE PARTITION 2005_year,2010_year,2015_year INTO
(
PARTITION 2015_year VALUES LESS THAN (2015)
)
2 . LIST 分區
CREATE TABLE t_list_text(
id INT NOT NULL,
fname VARCHAR(30),
store_id INT
)
PARTITION BY LIST(store_id)
PARTITION one VALUES IN (3,5,6,9,17),
PARTITION two VALUES IN (1,2,10,11,19,20),
PARTITION three VALUES IN (4,12,13,14,18),
PARTITION fore VALUES IN (7,8,15,16)
);
-- 插入數據需注意 store_id 必須在列表中
insert into t_list_text values(1,'11',33);X
3 HASH分區
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
store_id INT
)
PARTITION BY HASH(store_id)
PARTITIONS 4;
-- PARTITION BY LINER HASH(store_id) 線性hash是2的冪運算(普通的是取模運算)
-- 優點:在於增加、刪除、合併和拆分分區將變得更加快捷
-- 缺點:常規HASH分區得到的數據分佈相比,各個分區間數據的分佈不大可能均衡。
5 key 分區
create table t_key(
a int(11),
b datetime
)
partition by key (b)
partitions 4;
-- PARTITION BY LINER key(store_id) 線性hash是2的冪運算(普通的是取)
-- 優點:在於增加、刪除、合併和拆分分區將變得更加快捷
-- 缺點:常規HASH分區得到的數據分佈相比,各個分區間數據的分佈不大可能均衡。
讀寫分離
主-從讀寫分離在該文章中實現:
http://blog.csdn.net/u012442381/article/details/77141781