MariaDB Optimization and Indexes 中文版
Posted ACMUG
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MariaDB Optimization and Indexes 中文版相关的知识,希望对你有一定的参考价值。
作者: MariaDB官方授权资词翻译组翻译
资词翻译组,由数名数据库技术爱好者组成,旨在传播技术,帮助他人,提升自我。目前的成员有:田丽芳,强昌金,王竹峰,侯军伟,吕智超,刘启荣,周彦伟。
译事三难:信、达、雅。信者,真也,真者,不伪也;达者,至也,至者,无过无不及也;雅者,文学性也,文学性者,当雅则雅当俗则俗也。我们深知能达成此三事,绝非一日之功,亦非常人所能。然,苟利国家生死以,岂因祸福避趋之。我们还是希望竭尽所能,不遗余力,做一点微小的工作,提高姿势水平。由于能力有限,水平一般,有错误不妥之处,还请批评指正,希望能得到大家的资词。
资词翻译组获得mysql Server团队和MariaDB官方授权,翻译相关技术文章。
什么是索引?
试想你已经有了如下的一张表(该表与 More Advanced Joins (https://mariadb.com/kb/en/more-advanced-joins/)教程中使用的表相同)。
+----+------------+-----------+-------------------------+---------------------------+--------------+
| ID | First_Name | Last_Name | Position | Home_Address | Home_Phone |
+----+------------+-----------+-------------------------+---------------------------+--------------+
| 1 | Mustapha | Mond | Chief Executive Officer | 692 Promiscuous Plaza | 326-555-3492 |
| 2 | Henry | Foster | Store Manager | 314 Savage Circle | 326-555-3847 |
| 3 | Bernard | Marx | Cashier | 1240 Ambient Avenue | 326-555-8456 |
| 4 | Lenina | Crowne | Cashier | 281 Bumblepuppy Boulevard | 328-555-2349 |
| 5 | Fanny | Crowne | Restocker | 1023 Bokanovsky Lane | 326-555-6329 |
| 6 | Helmholtz | Watson | Janitor | 944 Soma Court | 329-555-2478 |
+----+------------+-----------+-------------------------+---------------------------+--------------+
解决这个问题的方法是给记录排序。如果记录按last name列的字母顺序排序存储的话,即使普通的人类也可以在大量数据中很快的找到一条记录。但是我们不能靠last name来排序全部记录。如果我们想要按照ID检索纪录,或者按照first name检索呢?答案就是为我们希望进行排序的每一列都创建单独的索引。一个索引只是很简单的包含了已经被排序了的数据(比如surname)和对应指向原始纪录的指针。
例如,一个基于Last_Name字段的索引:
+-----------+----+
| Last_Name | ID |
+-----------+----+
| Crowne | 4 |
| Crowne | 5 |
| Foster | 2 |
| Marx | 3 |
| Mond | 1 |
| Watson | 6 |
+-----------+----+
和一个基于Position字段的索引
+-------------------------+----+
| Position | ID |
+-------------------------+----+
| Cashier | 3 |
| Cashier | 4 |
| Chief Executive Officer | 1 |
| Janitor | 6 |
| Restocker | 5 |
| Store Manager | 2 |
+-------------------------+----+
索引入门
索引的类型主要有四种:主键(索引),唯一索引,一般索引和全文索引
术语‘KEY’和‘INDEX’一般可以通用,语句可以使用两个关键字的任意一个。
主键
主键是唯一且永不能为空(null)的。主键总是指向一条且仅一条纪录,并且每条记录都必须被代表(each record must be represented)。每个表只能有一个主键列。
在 XtraDB/InnoDB (https://mariadb.com/kb/en/mariadb/xtradb-and-innodb/)表中,所有的索引都含有主键作为后缀。因此,在使用这个存储引擎时,保持主键尽可能的小是尤其重要的。如果一个主键列不存在,同时也没有唯一索引列,InnoDB会创建一个用户不可见的6个字节大小的聚簇索引。
很多表都采用一个数字ID列作为主键。
主键通常是在使用 CREATE TABLE (https://mariadb.com/kb/en/create-table/#indexes)语句建表时被添加的。例如,下例就是在ID列上建了一个主键。注意ID列必须要定义为NOT NULL,否则索引就不能被创建。
CREATE TABLE `Employees` (
`ID` TINYINT(3) UNSIGNED NOT NULL AUTO_INCREMENT,
`First_Name` VARCHAR(25) NOT NULL,
`Last_Name` VARCHAR(25) NOT NULL,
`Position` VARCHAR(25) NOT NULL,
`Home_Address` VARCHAR(50) NOT NULL,
`Home_Phone` VARCHAR(12) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=Aria;
你不能使用 CREATE INDEX (https://mariadb.com/kb/en/mariadb/create-index/)命令创建主键。如果你想要在表被建立后添加一个主键的话,可以使用 ALTER TABLE (https://mariadb.com/kb/en/alter-table/),例如:
ALTER TABLE Employees ADD PRIMARY KEY(ID);
唯一索引
一个唯一索引必须是唯一的,但是它可以为null。故每个索引的值只标识一条记录,但并不是每条记录都需要被代表(not each record needs to be represented)。
例如,可以像创建主键索引那样在Employee_Code列上创建一个唯一索引:
CREATE TABLE `Employees` (
`ID` TINYINT(3) UNSIGNED NOT NULL,
`First_Name` VARCHAR(25) NOT NULL,
`Last_Name` VARCHAR(25) NOT NULL,
`Position` VARCHAR(25) NOT NULL,
`Home_Address` VARCHAR(50) NOT NULL,
`Home_Phone` VARCHAR(12) NOT NULL,
`Employee_Code` VARCHAR(25) NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY (`Employee_Code`)
) ENGINE=Aria;
唯一索引也可以在一个表被创建后使用CREATE INDEX或ALTER TABLE命令来添加,例如:
ALTER TABLE Employees ADD UNIQUE `EmpCode`(`Employee_Code`);
和
CREATE UNIQUE INDEX HomePhone ON Employees(Home_Phone);
事实上UNIQUE
约束可以为NULL
经常会被忽略掉。在SQL中任何NULL
都从不等于任何东西,即使是与其他的NULL
也不相等。因此,UNIQUE
约束并不妨碍存储含有null值的重复行:
CREATE TABLE Table_1 (a int not null, b int, unique (a,b));
insert into Table_1 values (1,1),(2,NULL),(2,NULL);
select * from Table_1;
+---+------+
| a | b |
+---+------+
| 1 | 1 |
| 2 | NULL |
| 2 | NULL |
+---+------+
的确是这样的,在SQL的最后两行中,即使它们看起来一样,但彼此间并不相等:
SELECT (2, NULL) = (2, NULL);
+---------------------- +
| (2, NULL) = (2, NULL) |
+---------------------- +
| 0 |
+---------------------- +
在MariaDB中,你可以将该特征和 virtual columns (https://mariadb.com/kb/en/virtual-columns/)结合起来以强制使表中诸多行的一个子集满足唯一性:
create table Table_1 (
user_name varchar(10),
status enum(**'Active'**, **'On-Hold'**, **'Deleted'**),
del char(0) as (if(status in (**'Active'**, **'On-Hold'**),**''**, NULL)) persistent,
unique(user_name,del)
)
这个表结构确保所有active或on-hold用户能有不同的名字,但是只要有用户被删除,他的名字就不再是唯一性约束的一部分,其他用户就可以使用这个相同的名字了。
如果一个唯一索引存在于一个后面的填充字符被去除或忽略的列中,向这个列插入只有后面填充字符不同的数据时会导致一个duplicate-key错误。
联合索引
索引可以包含不止一个的列。即使不能使用整个索引,MariaDB也可以使用在索引最左侧部分上的一个或多个列。
索引的选择
一般情况下你应该只添加与你的应用的query 相匹配的索引。任何额外的索引都会浪费资源。在一个有着很小的表的应用中,索引不会造成什么差别,但一旦你的表大小大于buffer size,索引就会开始发挥很大的加速效用。
在你的query中使用 EXPLAIN (https://mariadb.com/kb/en/mariadb/explain/)语句可以帮助你决定那个列需要索引。
如果你的query中含有类似LIKE '%word%'
这样的语句,如果没有全文索引,那么其实你每次使用的都是全表扫描,很慢的全表扫描。
如果你的表有大量的读和写,考虑使用延迟写。这种方式以一种”批处理”写模式来使用数据库引擎,这样就可以减少磁盘IO,故而提高了性能。
使用CREATE INDEX命令创建索引。
如果你在构建一张很大的表的话,那么为了最好的性能请在表被填充数据之后再添加索引。这样做可以提高插入性能,消除插入时的索引开销。
查看索引
你可以用 SHOW INDEX (https://mariadb.com/kb/en/show-index/)来查看一个表中有哪些索引以及这些索引的细节。
如果你想知道如何重建一个索引,运行SHOW CREATE TABLE 。
何时移除一个索引
如果一个索引很少被使用(或者根本不被使用),那么应删除该索引以提高INSERT和UPDATE操作的性能。
如果 user statistics (https://mariadb.com/kb/en/user-statistics/)已经被开启, Information Schema (https://mariadb.com/kb/en/information-schema/)的 INDEX_STATISTICS (https://mariadb.com/kb/en/mariadb/information-schema-index_statistics-table/)表会存储索引使用信息。
如果 慢查询日志 (https://mariadb.com/kb/en/mariadb/slow-query-log/)被开启并且 log_queries_not_using_indexes (https://mariadb.com/kb/en/server-system-variables/#log_queries_not_using_indexes)系统参数设为`ON`,没有使用到索引query就会被纪录。
以上是关于MariaDB Optimization and Indexes 中文版的主要内容,如果未能解决你的问题,请参考以下文章
FDO - Feedback directed optimization with GCC and Perf
FDO - Feedback directed optimization with GCC and Perf
Improving DNNs Hyperparameter tuning-Regularization and Optimization(week2)Optimization Methods(示例代码