C++面试题(陆)-数据库(一)

目录

数据库

1.1SQL

1.1.1 介绍一下数据库分页

1.1.2 介绍一下SQL中的聚合函数

1.1.3 表跟表是怎么关联的?

1.1.4 说一说你对外连接的了解

1.1.6 SQL中怎么将行转成列?

1.1.7 谈谈你对SQL注入的理解

1.1.8 将一张表的部分数据更新到另一张表,该如何操作呢?

1.1.9 WHERE和HAVING有什么区别?

1.2 索引

1.2.1 说一说你对MySQL索引的理解

1.2.2 索引有哪几种?

1.2.3 如何创建及保存MySQL的索引?

1.2.4 MySQL怎么判断要不要加索引?

1.2.5 只要创建了索引,就一定会走索引吗?

1.2.6 如何判断数据库的索引有没有生效?

1.2.7 如何评估一个索引创建的是否合理?

1.2.8 索引是越多越好吗?

1.2.9 数据库索引失效了怎么办?

1.2.10 所有的字段都适合创建索引吗?

1.2.11 说一说索引的实现原理

1.2.12 介绍一下数据库索引的重构过程

1.2.13 MySQL的索引为什么用B+树?

 1.2.15 MySQL的Hash索引和B树索引有什么区别?

1.2.16 聚簇索引和非聚簇索引有什么区别?

1.2.17 什么是联合索引?

1.2.18 select in语句中如何使用索引?

1.2.19 模糊查询语句中如何使用索引?


数据库

1.1SQL

1.1.1 介绍一下数据库分页

MySQL 的分页语法:
MySQL 中, SELECT 语句默认返回所有匹配的行,它们可能是指定表中的每个行。为了返回第一行或前几行,可使用LIMIT 子句,以实现分页查询。 LIMIT 子句的语法如下:
-- 在所有的查询结果中,返回前 5 行记录。
SELECT prod_name FROM products LIMIT 5 ;
-- 在所有的查询结果中,从第 5 行开始,返回 5 行记录。
SELECT prod_name FROM products LIMIT 5 , 5 ;
总之,带一个值的 LIMIT 总是从第一行开始,给出的数为返回的行数。带两个值的 LIMIT 可以指定从行号为第一个值的位置开始。优化LIMIT分页:在偏移量非常大的时候,例如 LIMIT 10000,20 这样的查询,这时 MySQL 需要查询 10020 条记录然后只返回最后20 条,前面的 10000 条记录都将被抛弃,这样的代价是非常高的。如果所有的页面被访问的频率都相同,那么这样的查询平均需要访问半个表的数据。要优化这种查询,要么是在页面中限制分页的数量,要么是优化大偏移量的性能。优化此类分页查询的一个最简单的办法就是尽可能地使用索引覆盖扫描,而不是查询所有的列,然后根据需要做一次关联操作再返回所需的列。对于偏移量很大的时候,这样做的效率会提升非常大。考虑下面的查询:
SELECT film_id,description FROM sakila .film ORDER BY title LIMIT 50 , 5 ;

 如果这个表非常大,那么这个查询最好改写成下面的样子:

SELECT film .film_id ,film .description
FROM sakila .film
INNER JOIN (
SELECT film_id FROM sakila .film ORDER BY title LIMIT 50 , 5
) AS lim USING ( film_id ) ;

        这里的“延迟关联将大大提升查询效率,它让MySQL扫描尽可能少的页面,获取需要访问的记录后再根据关联列回原表查询需要的所有列。这个技术也可以用于优化关联查询中的LIMIT子句。有时候也可以将LIMIT查询转换为已知位置的查询,让MySQL通过范围扫描获得对应的结果。例如,如果在一个位置列上有索引,并且预先计算出了边界值,上面的查询就可以改写为:

SELECT film_id,description FROM skila .film
WHERE position BETWEEN 50 AND 54 ORDER BY position;
对数据进行排名的问题也与此类似,但往往还会同时和 GROUP BY 混合使用,在这种情况下通常都需要预先计算并存储排名信息。 LIMIT OFFSET 的问题,其实是 OFFSET 的问题,它会导致 MySQL 扫描大量不需要的行然后再抛弃掉。如果可以使用书签记录上次取数的位置,那么下次就可以直接从该书签记录的位置开始扫描,这样就可以避免使用OFFSET。例如,若需要按照租赁记录做翻页,那么可以根据最新一条租赁记录向后追溯,这种做法可行是因为租赁记录的主键是单调增长的。首先使用下面的查询获得第一组结果:
SELECT * FROM sakila .rental ORDER BY rental_id DESC LIMIT 20 ;

假设上面的查询返回的是主键1604916030的租赁记录,那么下一页查询就可以从16030这个点开始:

        假设上面的查询返回的是主键1604916030的租赁记录,那么下一页查询就可以从16030这个点开始: 

SELECT * FROM sakila .rental
WHERE rental_id < 16030 ORDER BY rental_id DESC LIMIT 20 ;

 该技术的好处是无论翻页到多么后面,其性能都会很好。

1.1.2 介绍一下SQL中的聚合函数

参考答案
常用的聚合函数有 COUNT() AVG() SUM() MAX() MIN() ,下面以 MySQL 为例,说明这些函数的作
用。
COUNT() 函数:
COUNT() 函数统计数据表中包含的记录行的总数 ,或者根据查询结果返回列中包含的数据行数,它有两
种用法:
COUNT(*) 计算表中总的行数,不管某列是否有数值或者为空值。
COUNT( 字段名 ) 计算指定列下总的行数,计算时将忽略空值的行。
COUNT() 函数可以与 GROUP BY 一起使用来计算每个分组的总和。
AVG() 函数 ()
AVG() 函数通过计算返回的行数和每一行数据的和,求得指定列数据的平均值。
AVG() 函数可以与 GROUP BY 一起使用,来计算每个分组的平均值。
SUM() 函数:
SUM() 是一个求总和的函数,返回指定列值的总和。
SUM() 可以与 GROUP BY 一起使用,来计算每个分组的总和。
MAX() 函数:
MAX() 返回指定列中的最大值。
MAX() 也可以和 GROUP BY 关键字一起使用,求每个分组中的最大值。
MAX() 函数不仅适用于查找数值类型,也可应用于字符类型。
MIN() 函数:
MIN() 返回查询列中的最小值。
MIN() 也可以和 GROUP BY 关键字一起使用,求出每个分组中的最小值。
MIN() 函数与 MAX() 函数类似,不仅适用于查找数值类型,也可应用于字符类型。

1.1.3 表跟表是怎么关联的?

参考答案
表与表之间常用的 关联方式 有两种:内连接、外连接,下面以 MySQL 为例来说明这两种连接方式。
内连接:
内连接通过 INNER JOIN 来实现,它将返回两张表中满足连接条件的数据,不满足条件的数据不会查询出来。
外连接:
外连接通过 OUTER JOIN 来实现,它会返回两张表中满足连接条件的数据,同时返回不满足连接条件的数据。外连接有两种形式:左外连接(LEFT OUTER JOIN )、右外连接( RIGHT OUTER JOIN )。
左外连接:可以简称为左连接( LEFT JOIN ),它会返回左表中的所有记录和右表中满足连接条件
的记录。右外连接:可以简称为右连接(RIGHT JOIN ),它会返回右表中的所有记录和左表中满足连接条件的记录。
除此之外,还有一种常见的连接方式:等值连接。这种连接是通过 WHERE 子句中的条件,将两张表连接在一起,它的实际效果等同于内连接。出于语义清晰的考虑,一般更建议使用内连接,而不是等值连接。
以上是从语法上来说明表与表之间关联的实现方式,而从表的关系上来说,比较常见的关联关系有:一对多关联、多对多关联、自关联。
一对多关联:这种关联形式最为常见,一般是两张表具有主从关系,并且以主表的主键关联从表的
外键来实现这种关联关系。另外,以从表的角度来看,它们是具有多对一关系的,所以不再赘述多
对一关联了。
多对多关联:这种关联关系比较复杂,如果两张表具有多对多的关系,那么它们之间需要有一张中
间表来作为衔接,以实现这种关联关系。这个中间表要设计两列,分别存储那两张表的主键。因
此,这两张表中的任何一方,都与中间表形成了一对多关系,从而在这个中间表上建立起了多对多
关系。
自关联:自关联就是一张表自己与自己相关联,为了避免表名的冲突,需要在关联时通过别名将它
们当做两张表来看待。一般在表中数据具有层级(树状)时,可以采用自关联一次性查询出多层级
的数据。

1.1.4 说一说你对外连接的了解

参考答案
外连接通过 OUTER JOIN 来实现,它会返回两张表中满足连接条件的数据,同时返回不满足连接条件的数据。常见的外连接有两种形式:左外连接(LEFT OUTER JOIN )、右外连接( RIGHT OUTER JOIN )。
左外连接:可以简称为左连接( LEFT JOIN ),它会返回左表中的所有记录和右表中满足连接条件
的记录。
右外连接:可以简称为右连接( RIGHT JOIN ),它会返回右表中的所有记录和左表中满足连接条件的记录。
实际上,外连接还有一种形式:完全外连接( FULL OUTER JOIN ),但 MySQL 不支持这种形式。

1.1.5 说一说数据库的左连接和右连接

参考答案
外连接通过 OUTER JOIN 来实现,它会返回两张表中满足连接条件的数据,同时返回不满足连接条件的数据。常见的外连接有两种形式:左外连接(LEFT OUTER JOIN )、右外连接( RIGHT OUTER JOIN )。 左外连接:可以简称为左连接( LEFT JOIN ),它会返回左表中的所有记录和右表中满足连接条件的记录。
右外连接:可以简称为右连接( RIGHT JOIN ),它会返回右表中的所有记录和左表中满足连接条件的记录。

1.1.6 SQL中怎么将行转成列?

参考答案
我们以 MySQL 数据库为例,来说明行转列的实现方式。
首先,假设我们有一张分数表( tb_score ),表中的数据如下图:

 然后,我们再来看一下转换之后需要得到的结果,如下图:

        

可以看出,这里行转列是将原来的 subject 字段的多行内容选出来,作为结果集中的不同列,并根据userid进行分组显示对应的 score 。通常,我们有两种方式来实现这种转换。
1. 使用 CASE...WHEN...THEN 语句实现行转列,参考如下代码:

SELECT userid,
SUM ( CASE `subject` WHEN ' 语文 ' THEN score ELSE 0 END ) as ' 语文 ' ,
SUM ( CASE `subject` WHEN ' 数学 ' THEN score ELSE 0 END ) as ' 数学 ' ,
SUM ( CASE `subject` WHEN ' 英语 ' THEN score ELSE 0 END ) as ' 英语 ' ,
SUM ( CASE `subject` WHEN ' 政治 ' THEN score ELSE 0 END ) as ' 政治 '
FROM tb_score
GROUP BY userid
注意, SUM() 是为了能够使用 GROUP BY 根据 userid 进行分组,因为每一个 userid 对应的subject="语文 " 的记录只有一条,所以 SUM() 的值就等于对应那一条记录的 score 的值。假如 userid
='001' and subject=' 语文 ' 的记录有两条,则此时 SUM() 的值将会是这两条记录的和,同理,使用
Max() 的值将会是这两条记录里面值最大的一个。但是正常情况下,一个 user 对应一个 subject 只有
一个分数,因此可以使用 SUM() MAX() MIN() AVG() 等聚合函数都可以达到行转列的效果。

 2. 使用 IF() 函数实现行转列,参考如下代码:

SELECT userid,
SUM ( IF ( `subject` = ' 语文 ' ,score, 0 )) as ' 语文 ' ,
SUM ( IF ( `subject` = ' 数学 ' ,score, 0 )) as ' 数学 ' ,
SUM ( IF ( `subject` = ' 英语 ' ,score, 0 )) as ' 英语 ' ,
SUM ( IF ( `subject` = ' 政治 ' ,score, 0 )) as ' 政治 '
FROM tb_score
GROUP BY userid

        注意, IF(subject='语文',score,0) 作为条件,即对所有subject='语文'的记录的score字段进

SUM() MAX() MIN() AVG() 操作,如果 score 没有值则默认为 0

1.1.7 谈谈你对SQL注入的理解

参考答案
SQL 注入的原 理是将 SQL 代码伪装到输入参数中,传递到服务器解析并执行的一种攻击手法 。也就是说,在一些对SERVER 端发起的请求参数中植入一些 SQL 代码, SERVER 端在执行 SQL 操作时,会拼接对应参数,同时也将一些SQL 注入攻击的 “SQL” 拼接起来,导致会执行一些预期之外的操作。
举个例子: 比如我们的登录功能,其登录界面包括用户名和密码输入框以及提交按钮,登录时需要输入用户名和密码,然后提交。此时调用接口/user/login/ 加上参数 username password ,首先连接数据库,然后后台对请求参数中携带的用户名、密码进行参数校验,即SQL 的查询过程。假设正确的用户名和密码为 ls 和123456,输入正确的用户名和密码、提交,相当于调用了以下的 SQL 语句。
SELECT * FROM user WHERE username = 'ls' AND password = '123456'
SQL 中会将 # -- 以后的字符串当做注释处理,如果我们使用 ' or 1=1 # 作为用户名参数,那么服务端构建的SQL 语句就如下:
select * from user where username = '' or 1 = 1 #' and password='123456'

# 会忽略后面的语句,而 1=1 属于常等型条件,因此这个 SQL 将查询出所有的登录用户。其实上面的SQL注入只是在参数层面做了些手脚,如果是引入了一些功能性的 SQL 那就更危险了,比如上面的登录功能,如果用户名使用这个 ' or 1=1 ; delete * from users ; # ,那么在 ";" 之后相当于是另外一条新的SQL ,这个 SQL 是删除全表,是非常危险的操作,因此 SQL 注入这种还是需要特别注意的。如何解决SQL 注入。
1. 严格的参数校验
参数校验就没得说了,在一些不该有特殊字符的参数中提前进行特殊字符校验即可。
2. SQL 预编译
在知道了 SQL 注入的原理之后,我们同样也了解到 MySQL 有预编译的功能,指的是在服务器启动 时,MySQL Client SQL 语句的模板(变量采用占位符进行占位)发送给 MySQL 服务器, MySQL
服务器对 SQL 语句的模板进行编译,编译之后根据语句的优化分析对相应的索引进行优化,在最终
绑定参数时把相应的参数传送给 MySQL 服务器,直接进行执行,节省了 SQL 查询时间,以及MySQL服务器的资源,达到一次编译、多次执行的目的,除此之外,还可以防止 SQL 注入。具体是怎样防止SQL 注入的呢?实际上当将绑定的参数传到 MySQL 服务器, MySQL 服务器对参数进行编译,即填充到相应的占位符的过程中,做了转义操作。我们常用的JDBC 就有预编译功能,不仅提升性能,而且防止SQL 注入。

1.1.8 将一张表的部分数据更新到另一张表,该如何操作呢?

参考答案
可以采用关联更新的方式,将一张表的部分数据,更新到另一张表内。参考如下代码:
update b set b .col = a .col from a,b where a .id = b .id ;
update b set col = a .col from b inner join a on a .id = b .id ;
update b set b .col = a .col from b left Join a on b .id = a .id ;

1.1.9 WHEREHAVING有什么区别?

参考答案
WHERE 是一个约束声明,使用 WHERE 约束来自数据库的数据, WHERE 是在结果返回之前起作用的,WHERE中不能使用聚合函数。HAVING是一个过滤声明,是在查询返回结果集以后对查询结果进行的过滤操作,在 HAVING 中可以使用聚合函数。另一方面,HAVING 子句中不能使用除了分组字段和聚合函数之外的其他字段。从性能的角度来说,HAVING 子句中如果使用了分组字段作为过滤条件,应该替换成 WHERE 子句。因为WHERE可以在执行分组操作和计算聚合函数之前过滤掉不需要的数据,性能会更好。

1.2 索引

1.2.1 说一说你对MySQL索引的理解

参考答案
索引是一个单独的、存储在磁盘上的数据库结构, 包含着对数据表里所有记录的引用指针 。使用索引可以 快速找出在某个或多个列中有一特定值的行 ,所有 MySQL 列类型都可以被索引,对相关列使用索引是提高查询操作速度的最佳途径。索引是在存储引擎中实现的,因此,每种存储引擎的索引都不一定完全相同,并且每种存储引擎也不一定支持所有索引类型。MySQL 中索引的存储类型有两种,即 BTREE HASH ,具体和表的存储引擎相关。MyISAM InnoDB 存储引擎只支持 BTREE 索引; MEMORY/HEAP 存储引擎可以支持 HASH BTREE索引。
索引的优点主要有以下几条:
1. 通过 创建唯一索引,可以保证数据库表中每一行数据的唯一性。
2. 可以 大大加快数据的查询速度 ,这也是创建索引的主要原因。
3. 在实现数据的参考完整性方面,可以加速表和表之间的连接。
4. 在使用分组和排序子句进行数据查询时,也可以显著减少查询中分组和排序的时间。
增加索引也有许多不利的方面,主要表现在如下几个方面:
1. 创建索引和维护索引要耗费时间 ,并且随着数据量的增加所耗费的时间也会增加。
2. 索引需要占磁盘空间 ,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果有大量的索引,索引文件可能比数据文件更快达到最大文件尺寸。
3. 当对表中的数据进行增加、删除和修改的时候,索引也要动态地维护 ,这样就降低了数据的维护速度。

1.2.2 索引有哪几种?

参考答案
MySQL 的索引可以分为以下几类:
1. 普通索引和唯一索引
普通索引是 MySQL 中的基本索引类型,允许在定义索引的列中插入重复值和空值。唯一索引要求索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。主键索引是一种特殊的唯一索引,不允许有空值。
2. 单列索引和组合索引
单列索引即一个索引只包含单个列,一个表可以有多个单列索引。组合索引是指在表的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用。使用组合索引时遵循最左前缀集合。
3. 全文索引
全文索引类型为 FULLTEXT ,在定义索引的列上支持值的全文查找,允许在这些索引列中插入重复
值和空值。全文索引可以在 CHAR VARCHAR 或者 TEXT 类型的列上创建。 MySQL 中只有 MyISAM存储引擎支持全文索引。
4. 空间索引
空间索引是对空间数据类型的字段建立的索引, MySQL 中的空间数据类型有 4 种,分别是
GEOMETRY POINT LINESTRING POLYGON MySQL 使用 SPATIAL 关键字进行扩展,使得能够用创建正规索引类似的语法创建空间索引。创建空间索引的列,必须将其声明为NOT NULL ,空间索引只能在存储引擎为MyISAM 的表中创建。

1.2.3 如何创建及保存MySQL的索引?

参考答案
MySQL 支持多种方法在单个或多个列上创建索引:
在创建表的时候创建索引:使用CREATE TABLE 创建表时,除了可以定义列的数据类型,还可以定义主键约束、外键约束或者唯一性约束,而不论创建哪种约束,在定义约束的同时相当于在指定列上创建了一个索引。创建表时创建索引的基本语法如下:
CREATE TABLE table_name [ col_name data_type ]
[ UNIQUE | FULLTEXT | SPATIAL ] [ INDEX | KEY ] [ index_name ] ( col_name [ length ])
[ ASC | DESC ]

        其中,UNIQUEFULLTEXTSPATIAL为可选参数,分别表示唯一索引、全文索引和空间索引;INDEX与KEY为同义词,两者作用相同,用来指定创建索引。例如,可以按照如下方式,在id字段上使用UNIQUE关键字创建唯一索引:

CREATE TABLE t1 (
id INT NOT NULL ,
name CHAR ( 30 ) NOT NULL ,
UNIQUE INDEX UniqIdx ( id )
) ;

在已存在的表上创建索引

在已经存在的表中创建索引,可以使用 ALTER TABLE 语句或者 CREATEINDEX 语句。
ALTER TABLE 创建索引的基本语法如下:
ALTER TABLE table_name ADD
[ UNIQUE | FULLTEXT | SPATIAL ] [ INDEX | KEY ] [ index_name ] ( col_name [ length ] ,... )
[ ASC | DESC ]

 例如,可以按照如下方式,在bookId字段上建立名称为UniqidIdx的唯一索引:

ALTER TABLE book ADD UNIQUE INDEX UniqidIdx (bookId);

CREATE INDEX创建索引的基本语法如下: 

CREATE [ UNIQUE | FULLTEXT | SPATIAL ] INDEX index_name
ON table_name ( col_name [ length ] ,... ) [ ASC | DESC ]

例如,可以按照如下方式,在bookId字段上建立名称为UniqidIdx的唯一索引: 

CREATE UNIQUE INDEX UniqidIdx ON book ( bookId ) ;

1.2.4 MySQL怎么判断要不要加索引?

参考答案
建议按照如下的原则来创建索引:
1. 当唯一性是某种数据本身的特征时,指定唯一索引。使用唯一索引需能确保定义的列的数据完整
性,以提高查询速度。
2. 在频繁进行排序或分组(即进行 group by order by 操作)的列上建立索引,如果待排序的列有多个,可以在这些列上建立组合索引。

1.2.5 只要创建了索引,就一定会走索引吗?

参考答案
不一定。
比如,在使用组合索引的时候,如果没有遵从 最左前缀 的原则进行搜索,则索引是不起作用的。
举例,假设在 id name age 字段上已经成功建立了一个名为 MultiIdx 的组合索引。索引行中按 id 、 name、 age 的顺序存放,索引可以搜索 id 、( id,name )、( id, name, age )字段组合。如果列不构成索引最左面的前缀,那么MySQL 不能使用局部索引,如( age )或者( name,age )组合则不能使用该索引查询。

1.2.6 如何判断数据库的索引有没有生效?

参考答案
可以使用 EXPLAIN 语句查看索引是否正在使用。
举例,假设已经创建了 book 表,并已经在其 year_publication 字段上建立了普通索引。执行如下语句:
EXPLAIN SELECT * FROM book WHERE year_publication = 1990 ;

EXPLAIN语句将为我们输出详细的SQL执行信息,其中:  

possible_keys 行给出了 MySQL 在搜索数据记录时可选用的各个索引。
key 行是 MySQL 实际选用的索引。
如果 possible_keys 行和 key 行都包含 year_publication 字段,则说明在查询时使用了该索引。

1.2.7 如何评估一个索引创建的是否合理?

建议按照如下的原则来设计索引:
1. 避免对经常更新的表进行过多的索引,并且索引中的列要尽可能少。应该经常用于查询的字段创建索引,但要避免添加不必要的字段。
2. 数据量小 的表最好不要使用索引,由于数据较少,查询花费的时间可能比遍历索引的时间还要短,索引可能不会产生优化效果。
3. 在条件表达式中经常用到的不同值较多的列上建立索引,在不同值很少的列上不要建立索引。比如在学生表的“ 性别 字段上只有 两个不同值,因此就无须建立索引,如果建立索引不但不会提高查询效率,反而会严重降低数据更新速度。
4. 当唯一性是某种数据本身的特征时,指定唯一索引 。使用唯一索引需能确保定义的列的数据完整
性,以提高查询速度。
5. 在频繁进行排序或分组(即进行 group by order by 操作)的列上建立索引 ,如果待排序的列有多个,可以在这些列上建立组合索引。

1.2.8 索引是越多越好吗?

参考答案
索引并非越多越好,一个表中如有大量的索引, 不仅占用磁盘空间 ,还会影响 INSERT DELETE 、UPDATE等语句的性能,因为在表中的 数据更改时,索引也会进行调整和更新。

1.2.9 数据库索引失效了怎么办?

参考答案
可以采用以下几种方式,来避免索引失效:
1. 使用组合索引时,需要遵循 最左前缀 原则;
2. 不在索引列上做任何操作,例如计算、函数、类型转换,会导致索引失效而转向全表扫描;
3. 尽量使用覆盖索引(之访问索引列的查询),减少 select * 覆盖索引能减少回表次数;
4. MySQL 在使用不等于( != 或者 <> )的时候无法使用索引会导致全表扫描;
5. LIKE 以通配符开头( %abc MySQL 索引会失效变成全表扫描的操作;
6. 字符串不加单引号会导致索引失效(可能发生了索引列的隐式转换);
7. 少用 or ,用它来连接时会索引失效。

1.2.10 所有的字段都适合创建索引吗?

参考答案
不是。
下列几种情况,是不适合创建索引的:
1. 频繁更新的字段 不适合建立索引;
2. where 条件中用不到的字段 不适合建立索引;
3. 数据比较少 的表不需要建索引;
4. 数据重复且分布比较均匀 的的字段不适合建索引,例如性别、真假值;
5. 参与列计算的列 不适合建索引。

1.2.11 说一说索引的实现原理

参考答案
MySQL 中,索引是在存储引擎层实现的,不同存储引擎对索引的实现方式是不同的,下面我们探讨一下MyISAM InnoDB 两个存储引擎的索引实现方式。
MyISAM 索引实现:
MyISAM 引擎使用 B+Tree 作为索引结构 叶节点的 data 域存放的是数据记录的地址 MyISAM 索引的原理图如下。这里假设表一共有三列,假设我们以Col1 为主键,则上图是一个 MyISAM 表的主索引 (Primary key )示意。可以看出 MyISAM 的索引文件仅仅保存数据记录的地址。在 MyISAM 中,主索引 和辅助索引(Secondary key )在结构上没有任何区别,只是主索引要求 key 是唯一的,而辅助索引的key可以重复。

如果我们在 Col2 上建立一个辅助索引,则此索引的结构如下图所示。同样也是一颗 B+Tree data 域保存数据记录的地址。因此,MyISAM 中索引检索的算法为首先按照 B+Tree 搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址,读取相应数据记录。

InnoDB 索引实现:
虽然 InnoDB 也使用 B+Tree 作为索引结构,但具体实现方式却与 MyISAM 截然不同。
第一个重大区别是 InnoDB 数据文件本身就是索引文件 。从上文知道, MyISAM 索引文件和数据文件是 分离的 索引文件仅保存数据记录的地址 。而在 InnoDB 中,表数据文件本身就是按 B+Tree 组织的一个 索引结构,这棵树的叶节点 data 域保存了完整的数据记录 。这个索引的 key 是数据表的主键 ,因此 、InnoDB表数据文件本身就是主索引。下图是InnoDB 主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB 的数据文件本身要按主键聚集,所以 InnoDB 要求表必须有主键 (MyISAM 可以没有),如果没有显式指定,则 MySQL 系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL 自动为 InnoDB 表生成一个隐含字段作为主键,这个字段长度为6 个字节,类型为长整形。

第二个与 MyISAM 索引的不同是 InnoDB 的辅助索引 data 域存储相应记录主键的值而不是地址。换句话说,InnoDB 的所有辅助索引都引用主键作为 data 域。下图为定义在 Col3 上的一个辅助索引。这里以英文字符的ASCII 码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了 InnoDB 的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的 主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为 InnoDB数据文件本身是一颗 B+Tree ,非单调的主键会造成在插入新记录时数据文件为了维持 B+Tree 的 特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。

1.2.12 介绍一下数据库索引的重构过程

参考答案
什么时候需要重建索引呢?
1. 表上频繁发生 update,delete 操作;
2. 表上发生了 alter table ..move 操作( move 操作导致了 rowid 变化)。
怎么判断索引是否应该重建?
1. 一般看索引是否倾斜的严重,是否浪费了空间,对索引进行结构分析:
analyze index index_name validate structure;

 2. 在相同的session中查询index_stats表:

        select height,DEL_LF_ROWS/LF_ROWS from index_stats;

当查询的 height>=4 (索引的深度,即从根到叶节点的高度)或 DEL_LF_ROWS/LF_ROWS>0.2 的情况下,就应该考虑重建该索引。
如何重建索引?
drop 原索引,然后再创建索引:
drop index index_name;
create index index_name on table_name ( index_column ) ;

 这种方式相当耗时,一般不建议使用。

直接重建索引:

alter index indexname rebuild ;
alter index indexname rebuild online ;
此方法较快,建议使用。
rebuild 是快速重建索引的一种有效的办法,因为它是一种使用现有索引项来重建新索引的方法。如果重 建索引时有其他用户在对这个表操作,尽量使用带online 参数来最大限度的减少索引重建时将会出现的 任何加锁问题。由于新旧索引在建立时同时存在,因此,使用这种重建方法需要有额外的磁盘空间可供 临时使用,当索引建完后把老索引删除,如果没有成功,也不会影响原来的索引。利用这种办法可以用来将一个索引移到新的表空间。
rebuild 重建索引的过程:
1. Rebuild index fast full scan table full scan 方式(采用那种方式取决于 cost )读取原索引中的
数据来构建一个新的索引,重建过程中有排序操作, rebuild online 执行表扫描获取数据,重建过
程中有排序的操作;
2. Rebuild 会阻塞 DML 操作, rebuild online 不会阻塞 DML 操作;
3. rebuild online 时系统会产生一个 SYS_JOURNAL_xxx IOT 类型的系统临时日志表,所有 rebuild
online 时索引的变化都记录在这个表中,当新的索引创建完成后,把这个表的记录维护到新的索引
中去,然后 drop 掉旧的索引, rebuild online 就完成了。重建索引过程中的注意事项:
1. 执行 rebuild 操作时,需要检查表空间是否足够;
2. 虽然说 rebuild online 操作允许 DML 操作,但还是建议在业务不繁忙时间段进行;
3. Rebuild 操作会产生大量 Redo Log

1.2.13 MySQL的索引为什么用B+树?

参考答案
B+ 树由 B 树和索引顺序访问方法演化而来,它是为磁盘或其他直接存取辅助设备设计的一种平衡查找树,在B+ 树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点,各叶子节点通过指针进 行链接。如下图:
4.2.14 联合索引的存储结构是什么,它的有效方式是什么?
参考答案
从本质上来说,联合索引还是一棵 B+ 树,不同的是联合索引的键值数量不是 1 ,而是大于等于 2 ,参考下 图。另外,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,所以使用联合索引时遵循最左前缀集合。

 1.2.15 MySQLHash索引和B树索引有什么区别?

参考答案
hash 索引底层就是 hash 表,进行查找时,调用一次 hash 函数就可以获取到相应的键值,之后进行回表 查询获得实际数据。B+ 树底层实现是多路平衡查找树,对于每一次的查询都是从根节点出发,查找到叶 子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。它们有以下的不同:
hash 索引进行等值查询更快 ( 一般情况下 ) ,但是却无法进行范围查询。因为在 hash 索引中经过
hash 函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询。而 B+ 树的的所
有节点皆遵循 ( 左节点小于父节点,右节点大于父节点,多叉树也类似 ) ,天然支持范围。
hash 索引不支持使用索引进行排序,原理同上。
hash 索引不支持模糊查询以及多列索引的最左前缀匹配,原理也是因为 hash 函数的不可预测。
hash 索引任何时候都避免不了回表查询数据,而 B+ 树在符合某些条件 ( 聚簇索引,覆盖索引等 ) 的时
候可以只通过索引完成查询。 hash索引虽然在等值查询上较快,但是不稳定,性能不可预测,当某个键值存在大量重复的时候, 发生hash 碰撞,此时效率可能极差。而 B+ 树的查询效率比较稳定,对于所有的查询都是从根节点 到叶子节点,且树的高度较低。 因此,在大多数情况下,直接选择B+ 树索引可以获得稳定且较好的查询速度。而不需要使用 hash 索引。

1.2.16 聚簇索引和非聚簇索引有什么区别?

参考答案
InnoDB 存储引擎中,可以将 B + 树索引分为聚簇索引和辅助索引 (非聚簇索引)。无论是何种索引, 每个页的大小都为16KB ,且不能更改。 聚簇索引是根据主键创建的一棵 B+ 树,聚簇索引的叶子节点存放了表中的所有记录。辅助索引是根据索
引键创建的一棵 B+ 树, 与聚簇索引不同的是,其叶子节点仅存放索引键值,以及该索引键值指向的主 键。也就是说,如果通过辅助索引来查找数据,那么当找到辅助索引的叶子节点后,很有可能还需要根 据主键值查找聚簇索引来得到数据,这种查找方式又被称为书签查找。因为辅助索引不包含行记录的所有数据,这就意味着每页可以存放更多的键值,因此其高度一般都要小于聚簇索引。

1.2.17 什么是联合索引?

参考答案
联合索引是指对表上的多个列进行索引 ,联合索引的创建方法与单个索引创建的方法一样,不同之处仅在于有多个索引列。从本质上来说,联合索引还是一棵B+ 树,不同的是联合索引的键值数量不是 1 ,而是大于等于2 ,参考下图。另外,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,所以使用联合索引时遵循最左前缀集合。

1.2.18 select in语句中如何使用索引?

参考答案
索引是否起作用,主要取决于字段类型:
如果字段类型为字符串,需要给 in 查询中的数值与字符串值都需要添加引号,索引才能起作用。
如果字段类型为 int ,则 in 查询中的值不需要添加引号,索引也会起作用。
IN 的字段,在联合索引中,按以上方法,也会起作用。

1.2.19 模糊查询语句中如何使用索引?

参考答案
MySQL 中模糊查询 mobile like ‘%8765’ ,这种情况是不能使用 mobile 上的索引的,那么如果需
要根据手机号码后四位进行模糊查询,可以用一下方法进行改造。 我们可以加入冗余(MySQL5.7 之后加入了虚拟列,使用虚拟列更合适,思路相同),比如 mobile_reverse,内部存储为 mobile 的倒叙文本,如 mobile 17312345678 ,那么 mobile_reverse 存储 87654321371 ,为 mobile_reverse 列建立索引,查询中使用语句 mobile_reverse like reverse(’%5678’) 即可。reverse 是 MySQL 中的反转函数,这条语句相当于 mobile_reverse like ‘8765%’ ,这种语句是可以使用索引的。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/99956.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32 RTC实验

RTC时钟简介 STM32F103的实时时钟&#xff08;RTC&#xff09;是一个独立的定时器。 STM32的RTC模块拥有一组连续计数的计数器&#xff0c;在相对应的软件配置下&#xff0c;可提供时钟日历的功能。 修改计数器的值可以重新设置系统的当前时间和日期。 RTC模块和时钟配置系统…

uniapp项目实战系列(4):服务的异步请求,请求服务的二次封装

目录 系列往期文章&#xff08;点击跳转&#xff09;uniapp项目实战系列(1)&#xff1a;导入数据库&#xff0c;启动后端服务&#xff0c;开启代码托管&#xff08;点击跳转&#xff09;uniapp项目实战系列(2)&#xff1a;新建项目&#xff0c;项目搭建&#xff0c;微信开发工具…

七、高并发内存池--Page Cache

七、高并发内存池–Page Cache 7.1 PageCache的工作原理 PageCache是以span的大小(以页为单位)和下标一一对应为映射关系的哈希桶&#xff0c;下标是几就说明这个哈希桶下挂的span的大小就是几页的&#xff0c;是绝对映射的关系。因为PageCache也是全局只有唯一一个的&#x…

线上批量查询物流导出到表格的操作指南

现在的生活中&#xff0c;我们经常需要查询包裹物流信息。如果一次性需要查询多个快递单号的物流信息&#xff0c;手动一个一个查询会非常麻烦。今天&#xff0c;我将向大家分享一个简单实用的方法&#xff0c;可以批量查询物流并导出到表格&#xff0c;方便随时查看。 首先&am…

js 正则表达式 验证 :页面中一个输入框,可输入1个或多个vid/pid,使用英文逗号隔开...

就是意思一个输入框里面&#xff0c;按VID/PID格式输入,VID和PID最大长度是4,最多50组 1、页面代码 <el-form ref"ruleForm" :model"tempSet" :rules"rules" label-position"right"> <!-- 最多 50组&#xff0c;每组9个字符…

【USRP】集成化仪器系列1 :信号源,基于labview实现

USRP 信号源 1、设备IP地址&#xff1a;默认为192.168.10.2&#xff0c;请勿 修改&#xff0c;运行阶段无法修改。 2、天线输出端口是TX1&#xff0c;请勿修改。 3、通道&#xff1a;0 对应RF A、1 对应 RF B&#xff0c;运行 阶段无法修改。 4、中心频率&#xff1a;当需要…

LinuxUbuntu安装OpenWAF

Linux&Ubuntu安装OpenWAF 官方GitHub地址 介绍 OpenWAF&#xff08;Web Application Firewall&#xff09;是一个开源的Web应用防火墙&#xff0c;用于保护Web应用程序免受各种网络攻击。它通过与Web服务器集成&#xff0c;监控和过滤对Web应用程序的流量&#xff0c;识…

JDK源码解析-LinkedList

1. LinkedList类 1.1 LinkedList类定义&数据结构 定义 LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列&#xff0c;它是基于双向链表实现的。 数据结构 基础知识补充 单向链表&#xff1a; element&#xff1a;用来存放元素 next&#xff1a;用来…

Redis 7 第六讲 主从模式(replica)

🌹🌹🌹 此篇开始进入高级篇范围(❤艸`❤) 理论 即主从复制,master以写为主,Slave以读为主。当master数据变化的时候,自动将新的数据异步同步到其它slave数据库。 使用场景 读写分离 容灾备份数据备份水平扩容主从架构 演示案例 注:masterauth、replicaof主…

FTP文件传输服务器

目录 一、FTP协议两种工作模式 二、FTP数据两种传输模式 三、FTP用户分类 四、VSFTP配置案例 4.1匿名开放模式 4.2本地用户模式 4.3虚拟用户模式 五、实验总结 一、FTP协议两种工作模式 主动模式&#xff1a; 1、客户端主动向ftp服务器发送控制连接&#xff0c;三次握手控制连接…

go锁-互斥锁

go锁-互斥锁 sema初始值是0&#xff0c;waitershift等待协程的数量 正常枷锁&#xff1a; 尝试CAS直接加锁&#xff0c;通过原子包给lockerd 为枷锁 若无法直接获取&#xff0c;进行多次自旋尝试&#xff0c;未获取到的锁的g &#xff0c;多次执行空语句&#xff0c;多次尝试…

线程同步与互斥

目录 前言&#xff1a;基于多线程不安全并行抢票 一、线程互斥锁 mutex 1.1 加锁解锁处理多线程并发 1.2 如何看待锁 1.3 如何理解加锁解锁的本质 1.4 CRAII方格设计封装锁 前言&#xff1a;基于线程安全的不合理竞争资源 二、线程同步 1.1 线程同步处理抢票 1.2 如何…

《QDebug 2023年8月》

一、Qt Widgets 问题交流 1.获取 QWidget 当前所在屏幕区域 本来以为 QWidget 的 screen() 接口返回的是组件自己所在屏幕的 QSreen&#xff0c;实测是所属 Window 所在的屏幕&#xff0c;如果 Window 跨屏了两者所属屏幕可能就不是同一个。 获取 QWidget 当前所在屏幕区域可…

HTTP与SOCKS5的区别对比

在互联网世界中&#xff0c;服务器是一种重要的工具&#xff0c;可以帮助我们提高网络安全性等。今天&#xff0c;我们将重点关注两种常见的技术&#xff1a;HTTP和SOCKS5。让我们深入了解它们的工作原理、用途和优缺点&#xff0c;并通过Python代码示例学习如何使用它们。 HT…

Uniapp笔记(五)uniapp语法4

本章目标 授权登录【难点、重点】 条件编译【理解】 小程序分包【理解】 一、授权登录 我的模块其实是两个组件&#xff0c;一个是登录组件&#xff0c;一个是用户信息组件&#xff0c;根据用户的登录状态判断是否要显示那个组件 1、登录的基本布局 <template><…

QT创建可移动点类

效果如图所示&#xff1a; 创建新类MovablePoint&#xff0c;继承自QWidget. MovablePoint头文件: #ifndef MOVABLEPOINT_H #define MOVABLEPOINT_H#include <QWidget> #include <QPainter> #include <QPaintEvent> #include <QStyleOption> #includ…

2023年7月京东打印机行业品牌销售排行榜(京东运营数据分析)

鲸参谋监测的京东平台7月份打印机行业销售数据已出炉&#xff01; 7月份&#xff0c;打印机市场呈现下滑趋势。根据鲸参谋平台的数据可知&#xff0c;当月京东平台打印机的销量为48万&#xff0c;环比下降约28%&#xff0c;同比下降约18%&#xff1b;销售额为4亿&#xff0c;环…

OCR多语言识别模型构建资料收集

OCR多语言识别模型构建 构建多语言识别模型方案 合合&#xff0c;百度&#xff0c;腾讯&#xff0c;阿里这四家的不错 调研多家&#xff0c;发现有两种方案&#xff0c;但是大多数厂商都是将多语言放在一个字典里&#xff0c;构建1w~2W的字典&#xff0c;训练一个可识别多种语…

Java 8 新特性——Lambda 表达式(2)

一、Java Stream API Java Stream函数式编程接口最初在Java 8中引入&#xff0c;并且与 lambda 一起成为Java开发里程碑式的功能特性&#xff0c;它极大的方便了开放人员处理集合类数据的效率。 Java Stream就是一个数据流经的管道&#xff0c;并且在管道中对数据进行操作&…

IP初学习

1.IP报文 首部长度指的是报头长度&#xff0c;用于分离报头和有效载荷 2.网段划分 IP地址 目标网络 目标主机 3.例子 4.特殊的IP地址 5.真正的网络环境 6.调制解调器 “猫”&#xff0c;学名叫宽带无线猫 7.NAT 源IP在内网环境不断被替换 8.私有IP不能出现在公网上 因…
最新文章