如何對MySQL中的資料庫進行優化,如何優化數據表中的數據類型、刪除重覆、冗餘索引;如何設計資料庫,資料庫的範式要求。如何對數據表進行分析、檢查、優化、拆分。 ...
一、優化數據類型
在MySQL中不同的數據類型長度不同,在磁碟上所需要的存儲空間也不同,如果資料庫中使用不合理的數據類型,會造成很大的空間浪費,並且在數據插入與讀取時,也會造成MySQL的性能低下。
- 更小的數據類型更好
如果沒有特殊情況,儘量使用可以正確保存數據的最小數據類型,因為更小的數據類型在插入和讀取數據時更快,占用的記憶體更小,CPU處理的周期也會更短。
- 使用簡單的數據類型
在設計數據表時,儘量為欄位設計簡單的數據類型。例如能使用整型就不要使用字元串類型,因為字元串類型的比較規則更複雜,需要將字元串轉化為ANSI
碼後再進行比較。
- 避免使用NULL
在沒有特殊情況下,儘量將欄位的類型限製為NOT NULL。軟功欄位允許為NULL,會使得索引、插入與更新數據變得複雜。因為在可以為NULL的列建立索引時,在使用索引時,每個索引記錄都會使用一個額外的空間來記錄索引列是否為NULL,並且在InnoDB存儲引擎中,需要單獨使用一個位元組的存儲空間來存儲NULL值。在實際情況中可以設置預設值,例如為“”、0等。
二、刪除重覆索引和冗餘索引
重覆索引:索引名稱不同,索引欄位相同
冗餘索引:索引最左邊的部分列是重覆的
mysql> show create table t_goods \G;
*************************** 1. row ***************************
Table: t_goods
Create Table: CREATE TABLE `t_goods` (
`id` int NOT NULL AUTO_INCREMENT,
`t_category_id` int DEFAULT NULL,
`t_category` varchar(30) DEFAULT NULL,
`t_name` varchar(50) DEFAULT NULL,
`t_price` decimal(10,2) DEFAULT NULL,
`t_stock` int DEFAULT NULL,
`t_upper_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_category_name` (`t_category_id`,`t_name`),
KEY `category_part` (`t_category`(10)),
KEY `stock_index` (`t_stock`),
KEY `t_upper_time_index` (`t_upper_time`),
KEY `name_index` (`t_name`),
KEY `category_name_index` (`t_category`,`t_name`),
KEY `category_name_index2` (`t_category`,`t_name`),
KEY `name_stock_index` (`t_name`,`t_stock`),
KEY `category_name_index3` (`t_category` DESC,`t_name`),
CONSTRAINT `foreign_category` FOREIGN KEY (`t_category_id`) REFERENCES `t_goods_category` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.01 sec)
例如在這張數據表中,category_name_index
和category_name_index2
索引是重覆索引。這兩個索引的欄位完全相同。name_index
和name_stock_index
索引是冗餘索引,因為name_stock_index
索引中包含了name_index
索引的欄位。為什麼category_name_index3
不是重覆索引呢,因為category_name_index3
索引的t_category
欄位的順序不同。
三、反範式設計
資料庫設計中三大範式要求儘可能減少冗餘欄位,使資料庫設計看起來更簡單、優雅。
但是完全的遵循資料庫的三大範式來設計資料庫,會導致很多表之間產生很多的依賴關係,規範越高,表之間的依賴關係越多這樣會導致在查詢數據時,數據表之間的頻繁連接,造成數據查詢的性能低下。
在實際情況下,對於查詢較多的夏天來說,應根據實際業務對資料庫進行反範式化設計,適當的增加冗餘欄位,提高數據的查詢效率。
需要註意的是,在增加冗餘欄位時,需要考慮數據的一致性問題,也就是說,當數據表A中的某個欄位發生變化時,對應數據表B中也應該將相應的數據修改。
四、增加中間表
如果資料庫中存在經常需要關聯查詢的數據表,則可以為關聯查詢的數據表建立一個中間表,中間表中存儲多個數據表關聯查詢的結果數據,將對多個數據表的關聯查詢轉化為對中間表的查詢,提高查詢效率。
例如創建部門表和員工表
create table t_department(
id int not null primary key auto_increment,
name varchar(30) not null default ""
);
create table t_employee(
id int not null primary key auto_increment,
name varchar(30) not null default "",
join_data DATE,
bobby varchar(100),
department int not null
);
t_employee
數據表通過department
欄位與t_department
數據表之間進行關聯。
使用聯表查詢
mysql> explain select e.name as employee_name,d.name as department_name from t_employee e left join t_department d on e.department=d.id \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: e
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: d
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: goods.e.department
rows: 1
filtered: 100.00
Extra: NULL
2 rows in set, 1 warning (0.01 sec)
創建中間表,存儲連接查詢的信息
create table t_employee_tmp(
employee_id int not null,
employee_name varchar(30),
department_name varchar(30)
);
將聯表查詢信息導入中間表
insert into t_employee_tmp
(employee_id,employee_name,department_name)
select e.id as employee_id,e.name as employee_name,d.name as department_name
from t_employee as e left join t_department as d
on e.department =d.id;
查詢中間表中的數據集
mysql> explain select * from t_employee_tmp \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t_employee_tmp
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
這時候只需要查詢中間表的數據就可以了,不需要再進行聯表查詢,並且如果在中間表的查詢中,適當添加索引,會更明顯的提升效率。
五、分析數據表
當使用ANALYZE TAVLE
來分析數據表時,MYSQL會自動為數據表添加一個只讀的鎖,此時,只能對數據表中的數據進行讀取操作而不能進行寫入和更新操作。
mysql> analyze table t_goods;
+---------------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+---------------+---------+----------+----------+
| goods.t_goods | analyze | status | OK |
+---------------+---------+----------+----------+
1 row in set (0.05 sec)
mysql> analyze table t_goods \G;
*************************** 1. row ***************************
Table: goods.t_goods
Op: analyze
Msg_type: status
Msg_text: OK
1 row in set (0.01 sec)
Table | 當前分析的數據表的名稱 |
---|---|
Op | 當前執行的操作 |
Msg_type | 輸出結果信息的類型,包括status(狀態)、info(信息)、note(註意)、warning(警告)、erroe(錯誤) |
Msg_test | 結果信息 |
六、檢查數據表
當使用CHECK TABLE
語句檢查數據表時,MySQL會自動為數據表添加讀鎖。
check table t_goods\G;
+-------------+-------+----------+-----------------------------------+
| Table | Op | Msg_type | Msg_text |
+-------------+-------+----------+-----------------------------------+
| goods.goods | check | Error | Table 'goods.goods' doesn't exist |
| goods.goods | check | status | Operation failed |
+-------------+-------+----------+-----------------------------------+
2 rows in set (0.02 sec)
mysql> check table t_goods\G
*************************** 1. row ***************************
Table: goods.t_goods
Op: check
Msg_type: status
Msg_text: OK
1 row in set (0.01 sec)
七、優化數據表
OPTIMIZE TABLE
語句主要用來優化刪除和更新數據造成的文件碎片。使用時,會自動添加讀鎖。
mysql> optimize table t_goods \G;
*************************** 1. row ***************************
Table: goods.t_goods
Op: optimize
Msg_type: note
Msg_text: Table does not support optimize, doing recreate + analyze instead
*************************** 2. row ***************************
Table: goods.t_goods
Op: optimize
Msg_type: status
Msg_text: OK
2 rows in set (0.13 sec)
註意,只能優化數據表中的Varchar、Blob或Text類型欄位。
八、拆分數據表
如果一個表的欄位數量比較多,某些欄位的查詢效率非常低。這樣的欄位在數據量非常大時,會嚴重影響數據表的性能,可以將這些欄位分離出來形成新的表。
1、垂直拆分
mysql> show create table t_user \G;
*************************** 1. row ***************************
Table: t_user
Create Table: CREATE TABLE `t_user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(30) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL,
`phone` varchar(14) DEFAULT NULL,
`address` varchar(200) DEFAULT NULL,
`hobby` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
從分析可以看到,其中最常使用的是username和pasword,其他欄位數據查詢的頻率非常低,此時可以將表拆分為兩個表t_user、t_user_detail。
mysql> show create table t_user_puls \G;
*************************** 1. row ***************************
Table: t_user_puls
Create Table: CREATE TABLE `t_user_puls` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(30) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.01 sec)
mysql> show create table t_user_detail \G;
*************************** 1. row ***************************
Table: t_user_detail
Create Table: CREATE TABLE `t_user_detail` (
`user_id` int NOT NULL,
`phone` varchar(14) DEFAULT NULL,
`address` varchar(200) DEFAULT NULL,
`hobby` varchar(200) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
這裡使用索引欄位將兩個表進行關聯,如果只需要查詢用戶名和密碼,就可以大大提高效率。
2、水平拆分
主要拆分的數據。例如將10行數據拆分為5行5行。主要用於增加資料庫的存儲容量。例如,根據一定的規則將數據表中的一部分數據存儲到一張數據表中,另一部分存儲到其他數據表中。