当前仅显示指定条件回帖 [ 展开查看全部 ]
文件名称:
面试题_01.md
所在目录:
mysql / 面试题
文件大小:
36.54 KB
下载地址:
文本预览:
#
能说下myisam 和 innodb的区别吗?
##### 参考答案
myisam引擎是5.1版本之前的默认引擎,支持全文检索、压缩、空间函数等,但是不支持事务和行级锁,所以一般用于有大量查询少量插入的场景来使用,而且myisam不支持外键,并且索引和数据是分开存储的。
innodb是基于聚簇索引建立的,和myisam相反它支持事务、外键,并且通过MVCC来支持高并发,索引和数据存储在一起。
#
说下mysql的索引有哪些吧,聚簇和非聚簇索引又是什么?
##### 参考答案
索引按照数据结构来说主要包含B+树和Hash索引。
假设我们有张表,结构如下:
```C
create table user( id int(11) not null, age int(11) not null, primary key(id), key(age) );
```
B+树是左小右大的顺序存储结构,节点只包含id索引列,而叶子节点包含索引列和数据,这种数据和索引在一起存储的索引方式叫做聚簇索引,一张表只能有一个聚簇索引。假设没有定义主键,InnoDB会选择一个唯一的非空索引代替,如果没有的话则会隐式定义一个主键作为聚簇索引。

这是主键聚簇索引存储的结构,那么非聚簇索引的结构是什么样子呢?非聚簇索引(二级索引)保存的是主键id值,这一点和myisam保存的是数据地址是不同的。

最终,我们一张图看看InnoDB和Myisam聚簇和非聚簇索引的区别

#
那你知道什么是覆盖索引和回表吗?
##### 参考答案
覆盖索引指的是在一次查询中,如果一个索引包含或者说覆盖所有需要查询的字段的值,我们就称之为覆盖索引,而不再需要回表查询。
而要确定一个查询是否是覆盖索引,我们只需要explain sql语句看Extra的结果是否是“Using index”即可。
以上面的user表来举例,我们再增加一个name字段,然后做一些查询试试。
```C
explain select * from user where age=1; //查询的name无法从索引数据获取 explain select id,age from user where age=1; //可以直接从索引获取
```
#
锁的类型有哪些呢?
##### 参考答案
* mysql锁分为共享锁和排他锁,也叫做读锁和写锁。
* 读锁是共享的,可以通过lock in share mode实现,这时候只能读不能写。
* 写锁是排他的,它会阻塞其他的写锁和读锁。从颗粒度来区分,可以分为表锁和行锁两种。
* 表锁会锁定整张表并且阻塞其他用户对该表的所有读写操作,比如alter修改表结构的时候会锁表。
* 行锁又可以分为乐观锁和悲观锁,悲观锁可以通过for update实现,乐观锁则通过版本号实现。
#
你能说下事务的基本特性和隔离级别吗?
##### 参考答案
* 事务基本特性ACID分别是:
* 原子性指的是一个事务中的操作要么全部成功,要么全部失败。
* 一致性指的是数据库总是从一个一致性的状态转换到另外一个一致性的状态。比如A转账给B100块钱,假设中间sql执行过程中系统崩溃A也不会损失100块,因为事务没有提交,修改也就不会保存到数据库。
* 隔离性指的是一个事务的修改在最终提交前,对其他事务是不可见的。
* 持久性指的是一旦事务提交,所做的修改就会永久保存到数据库中。
* 隔离性有4个隔离级别,分别是:
* 1. read uncommit 读未提交,可能会读到其他事务未提交的数据,也叫做脏读。
用户本来应该读取到id=1的用户age应该是10,结果读取到了其他事务还没有提交的事务,结果读取结果age=20,这就是脏读。

* 2. read commit 读已提交,两次读取结果不一致,叫做不可重复读。
不可重复读解决了脏读的问题,他只会读取已经提交的事务。
用户开启事务读取id=1用户,查询到age=10,再次读取发现结果=20,在同一个事务里同一个查询读取到不同的结果叫做不可重复读。

* 3. repeatable read 可重复复读,这是mysql的默认级别,就是每次读取结果都一样,但是有可能产生幻读。
* 4. serializable 串行,一般是不会使用的,他会给每一行读取的数据加锁,会导致大量超时和锁竞争的问题。
#
那ACID靠什么保证的呢?
##### 参考答案
* A 原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
* C 一致性一般由代码层面来保证
* I 隔离性由MVCC来保证
* D 持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log恢复
#
那你说说什么是幻读,什么是MVCC?
##### 参考答案
要说幻读,首先要了解MVCC,MVCC叫做多版本并发控制,实际上就是保存了数据在某个时间节点的快照。
我们每行数实际上隐藏了两列,创建时间版本号,过期(删除)时间版本号,每开始一个新的事务,版本号都会自动递增。
还是拿上面的user表举例子,假设我们插入两条数据,他们实际上应该长这样。
a-draft-type=“table” data-size=“normal” data-row-style=“normal”>
id name create_version delete_version
这时候假设小明去执行查询,此时current_version=3
select * from user where id<=3; 复制代码
同时,小红在这时候开启事务去修改id=1的记录,current_version=4
update user set name=‘张三三’ where id=1; 复制代码
执行成功后的结果是这样的
a-draft-type=“table” data-size=“normal” data-row-style=“normal”>
id name create_version delete_version
如果这时候还有小黑在删除id=2的数据,current_version=5,执行后结果是这样的。
a-draft-type=“table” data-size=“normal” data-row-style=“normal”>
id name create_version delete_version
由于MVCC的原理是查找创建版本小于或等于当前事务版本,删除版本为空或者大于当前事务版本,小明的真实的查询应该是这样
select * from user where id<=3 and create_version<=3 and (delete_version>3 or delete_version is null); 复制代码
所以小明最后查询到的id=1的名字还是’张三’,并且id=2的记录也能查询到。这样做是为了保证事务读取的数据是在事务开始前就已经存在的,要么是事务自己插入或者修改的。
明白MVCC原理,我们来说什么是幻读就简单多了。举一个常见的场景,用户注册时,我们先查询用户名是否存在,不存在就插入,假定用户名是唯一索引。
小明开启事务current_version=6查询名字为’王五’的记录,发现不存在。
小红开启事务current_version=7插入一条数据,结果是这样:
a-draft-type=“table” data-size=“normal” data-row-style=“normal”>
id Name create_version delete_version
小明执行插入名字’王五’的记录,发现唯一索引冲突,无法插入,这就是幻读。
#
那你知道什么是间隙锁吗?
##### 参考答案
间隙锁是可重复读级别下才会有的锁,结合MVCC和间隙锁可以解决幻读的问题。我们还是以user举例,假设现在user表有几条记录
e data-draft-node=“block” data-draft-type=“table” data-size=“normal” data-row-style=“normal”>
当我们执行:
begin; select * from user where age=20 for update; begin; insert into user(age) values(10); #成功 insert into user(age) values(11); #失败 insert into user(age) values(20); #失败 insert into user(age) values(21); #失败 insert into user(age) values(30); #失败
只有10可以插入成功,那么因为表的间隙mysql自动帮我们生成了区间(左开右闭)
(negative infinity,10],(10,20],(20,30],(30,positive infinity)
由于20存在记录,所以(10,20],(20,30]区间都被锁定了无法插入、删除。
如果查询21呢?就会根据21定位到(20,30)的区间(都是开区间)。
需要注意的是唯一索引是不会有间隙索引的。
#
你们数据量级多大?分库分表怎么做的?
##### 参考答案
首先分库分表分为垂直和水平两个方式,一般来说我们拆分的顺序是先垂直后水平。
* 垂直分库
基于现在微服务拆分来说,都是已经做到了垂直分库了。

* 垂直分表
如果表字段比较多,将不常用的、数据较大的等等做拆分。

* 水平分表
首先根据业务场景来决定使用什么字段作为分表字段(sharding_key),比如我们现在日订单1000万,我们大部分的场景来源于C端,我们可以用user_id作为sharding_key,数据查询支持到最近3个月的订单,超过3个月的做归档处理,那么3个月的数据量就是9亿,可以分1024张表,那么每张表的数据大概就在100万左右。
比如用户id为100,那我们都经过hash(100),然后对1024取模,就可以落到对应的表上了。
#
那分表后的ID怎么保证唯一性的呢?
##### 参考答案
因为我们主键默认都是自增的,那么分表之后的主键在不同表就肯定会有冲突了。有几个办法考虑:
1. 设定步长,比如1-1024张表我们分别设定1-1024的基础步长,这样主键落到不同的表就不会冲突了。
2. 分布式ID,自己实现一套分布式ID生成算法或者使用开源的比如雪花算法这种。
3. 分表后不使用主键作为查询依据,而是每张表单独新增一个字段作为唯一主键使用,比如订单表订单号是唯一的,不管最终落在哪张表都基于订单号作为查询依据,更新也一样。
#
分表后非sharding_key的查询怎么处理呢?
##### 参考答案
1. 可以做一个mapping表,比如这时候商家要查询订单列表怎么办呢?不带user_id查询的话你总不能扫全表吧?所以我们可以做一个映射关系表,保存商家和用户的关系,查询的时候先通过商家查询到用户列表,再通过user_id去查询。
2. 打宽表,一般而言,商户端对数据实时性要求并不是很高,比如查询订单列表,可以把订单表同步到离线(实时)数仓,再基于数仓去做成一张宽表,再基于其他如es提供查询服务。
3. 数据量不是很大的话,比如后台的一些查询之类的,也可以通过多线程扫表,然后再聚合结果的方式来做。或者异步的形式也是可以的。
```java
List>> taskList = Lists.newArrayList();
for (int shardingIndex = 0; shardingIndex < 1024; shardingIndex++) {
taskList.add(() -> (userMapper.getProcessingAccountList(shardingIndex)));
}
List list = null;
try {
list = taskExecutor.executeTask(taskList);
} catch (Exception e) {
//do something
}
public class TaskExecutor {
public List executeTask(Collection extends Callable> tasks) throws Exception {
List result = Lists.newArrayList();
List> futures = ExecutorUtil.invokeAll(tasks);
for (Future future : futures) {
result.add(future.get());
} return result;
}
}
```
#
说说mysql主从同步怎么做的吧?
##### 参考答案
首先先了解mysql主从同步的原理
1. master提交完事务后,写入binlog
2. slave连接到master,获取binlog
3. master创建dump线程,推送binglog到slave
4. slave启动一个IO线程读取同步过来的master的binlog,记录到relay log中继日志中
5. slave再开启一个sql线程读取relay log事件并在slave执行,完成同步
6. slave记录自己的binglog

由于mysql默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念。
* 全同步复制
主库写入binlog后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响。
* 半同步复制
和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回ACK确认给主库,主库收到至少一个从库的确认就认为写操作完成。
#
那主从的延迟怎么解决呢?
##### 参考答案
这个问题貌似真的是个无解的问题,只能是说自己来判断了,需要走主库的强制走主库查询。
#
为什么用自增列作为主键?
##### 参考答案
1. 如果我们定义了主键(PRIMARY KEY),那么InnoDB会选择主键作为聚集索引。
如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引。
如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐含的聚集索引(ROWID随着行记录的写入而主键递增,这个ROWID不像ORACLE的ROWID那样可引用,是隐含的)。
2. 数据记录本身被存于主索引(一颗B+Tree)的叶子节点上,这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放
因此每当有一条新的记录插入时,MySQL会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)
3. 如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页
4. 如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置
此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销
同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE来重建表并优化填充页面。
#
为什么使用数据索引能提高效率?
##### 参考答案
1. 数据索引的存储是有序的
2. 在有序的情况下,通过索引查询一个数据是无需遍历索引记录的
3. 极端情况下,数据索引的查询效率为二分法查询效率,趋近于 log2(N)
#
B+树索引和哈希索引的区别?
##### 参考答案
B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的节点间有指针相互链接,是有序的,如下图:

哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可,是无序的,如下图所示:

#
哈希索引的优势?
##### 参考答案
等值查询,哈希索引具有绝对优势(前提是:没有大量重复键值,如果大量重复键值时,哈希索引的效率很低,因为存在所谓的哈希碰撞问题。)
#
哈希索引不适用的场景?
##### 参考答案
1. 不支持范围查询
2. 不支持索引完成排序
3. 不支持联合索引的最左前缀匹配规则
通常,B+树索引结构适用于绝大多数场景,像下面这种场景用哈希索引才更有优势:
在HEAP表中,如果存储的数据重复度很低(也就是说基数很大),对该列数据以等值查询为主,没有范围查询、没有排序的时候,特别适合采用哈希索引,例如这种SQL:
**仅等值查询**
```C
select id, name from table where name='李明';
```
而常用的 InnoDB 引擎中默认使用的是B+树索引,它会实时监控表上索引的使用情况。
如果认为建立哈希索引可以提高查询效率,则自动在内存中的“自适应哈希索引缓冲区”建立哈希索引(在InnoDB中默认开启自适应哈希索引)。
通过观察搜索模式,MySQL会利用index key的前缀建立哈希索引,如果一个表几乎大部分都在缓冲池中,那么建立一个哈希索引能够加快等值查询。
注意:在某些工作负载下,通过哈希索引查找带来的性能提升远大于额外的监控索引搜索情况和保持这个哈希表结构所带来的开销。
但某些时候,在负载高的情况下,自适应哈希索引中添加的read/write锁也会带来竞争,比如高并发的join操作。like操作和%的通配符操作也不适用于自适应哈希索引,可能要关闭自适应哈希索引。
#
B树和B+树的区别?
##### 参考答案
1. B树,每个节点都存储key和data,所有节点组成这棵树,并且叶子节点指针为nul,叶子结点不包含任何关键字信息。

2. B+树,所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接
所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含需要查找的有效信息)

#
为什么说B+比B树更适合实际应用中操作系统的文件索引和数据库索引?
##### 参考答案
1. B+的磁盘读写代价更低。
B+的内部结点并没有指向关键字具体信息的指针,因此其内部结点相对B树更小。
如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。
2. B+-tree的查询效率更加稳定。
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
#
MySQL联合索引?
##### 参考答案
1. 联合索引是两个或更多个列上的索引。
对于联合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。
例如索引是key index (a,b,c). 可以支持a 、 a,b 、 a,b,c 3种组合进行查找,但不支持 b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效。
2. 利用索引中的附加列,您可以缩小搜索的范围,但使用一个具有两列的索引不同于使用两个单独的索引。
复合索引的结构与电话簿类似,人名由姓和名构成,电话簿首先按姓氏对进行排序,然后按名字对有相同姓氏的人进行排序。
如果您知道姓,电话簿将非常有用;如果您知道姓和名,电话簿则更为有用,但如果您只知道名不知道姓,电话簿将没有用处。
#
什么情况下应不建或少建索引?
##### 参考答案
1. 表记录太少
2. 经常插入、删除、修改的表
3. 数据重复且分布平均的表字段,假如一个表有10万行记录,有一个字段A只有T和F两种值,且每个值的分布概率大约为50%,那么对这种表A字段建索引一般不会提高数据库的查询速度。
4. 经常和主字段一块查询但主字段索引值比较多的表字段
#
什么是表分区?
##### 参考答案
表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成。
#
表分区与分表的区别?
##### 参考答案
* 分表:指的是通过一定规则,将一张表分解成多张不同的表。比如将用户订单记录根据时间成多个表。
* 分表与分区的区别在于:分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表。
#
表分区有什么好处?
##### 参考答案
1. 存储更多数据。分区表的数据可以分布在不同的物理设备上,从而高效地利用多个硬件设备。和单个磁盘或者文件系统相比,可以存储更多数据
2. 优化查询。在where语句中包含分区条件时,可以只扫描一个或多个分区表来提高查询效率;涉及sum和count语句时,也可以在多个分区上并行处理,最后汇总结果。
3. 分区表更容易维护。例如:想批量删除大量数据可以清除整个分区。
4. 避免某些特殊的瓶颈,例如InnoDB的单个索引的互斥访问,ext3问价你系统的inode锁竞争等。
#
分区表的限制因素?
##### 参考答案
1. 一个表最多只能有1024个分区
2. MySQL5.1中,分区表达式必须是整数,或者返回整数的表达式。在MySQL5.5中提供了非整数表达式分区的支持。
3. 如果分区字段中有主键或者唯一索引的列,那么多有主键列和唯一索引列都必须包含进来。即:分区字段要么不包含主键或者索引列,要么包含全部主键和索引列。
4. 分区表中无法使用外键约束
5. MySQL的分区适用于一个表的所有数据和索引,不能只对表数据分区而不对索引分区,也不能只对索引分区而不对表分区,也不能只对表的一部分数据分区。
#
如何判断当前MySQL是否支持分区?
##### 参考答案
命令:show variables like '%partition%' 运行结果: have_partintioning 的值为YES,表示支持分区。
#
MySQL支持的分区类型有哪些?
##### 参考答案
* RANGE分区: 这种模式允许将数据划分不同范围。例如可以将一个表通过年份划分成若干个分区
* LIST分区: 这种模式允许系统通过预定义的列表的值来对数据进行分割。按照List中的值分区,与RANGE的区别是,range分区的区间范围值是连续的。
* HASH分区 :这中模式允许通过对表的一个或多个列的Hash Key进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区。例如可以建立一个对表主键进行分区的表。
* KEY分区 :上面Hash模式的一种延伸,这里的Hash Key是MySQL系统产生的。
#
四种隔离级别?
##### 参考答案
* Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
* Repeatable read (可重复读):可避免脏读、不可重复读的发生。
* Read committed (读已提交):可避免脏读的发生。
* Read uncommitted (读未提交):最低级别,任何情况都无法保证。
#
关于MVVC的运行原理介绍?
##### 参考答案
MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control)
注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control
**MVCC最大的好处**:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能,现阶段几乎所有的RDBMS,都支持了MVCC。
1. LBCC:Lock-Based Concurrency Control,基于锁的并发控制
2. MVCC:Multi-Version Concurrency Control
基于多版本的并发控制协议。纯粹基于锁的并发机制并发量低,MVCC是在基于锁的并发控制上的改进,主要是在读操作上提高了并发量。
#
在MVCC并发控制中,读操作可以分成两类?
##### 参考答案
1. 快照读 (snapshot read):读取的是记录的可见版本 (有可能是历史版本),不用加锁(共享读锁s锁也不加,所以不会阻塞其他事务的写)
2. 当前读 (current read):读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录
#
行级锁定的优点有哪些?
##### 参考答案
1. 当在许多线程中访问不同的行时只存在少量锁定冲突。
2. 回滚时只有少量的更改
3. 可以长时间锁定单一的行。
#
行级锁定的缺点有哪些?
##### 参考答案
1. 比页级或表级锁定占用更多的内存。
2. 当在表的大部分中使用时,比页级或表级锁定速度慢,因为你必须获取更多的锁。
3. 如果你在大部分数据上经常进行GROUP BY操作或者必须经常扫描整个表,比其它锁定明显慢很多。
4. 用高级别锁定,通过支持不同的类型锁定,你也可以很容易地调节应用程序,因为其锁成本小于行级锁定。
#
你对MySQL优化了解多少?
##### 参考答案
1. 开启查询缓存,优化查询
2. explain你的select查询,这可以帮你分析你的查询语句或是表结构的性能瓶颈。EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的
3. 当只要一行数据时使用limit 1,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据
4. 为搜索字段建索引
5. 使用 ENUM 而不是 VARCHAR。如果你有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,你知道这些字段的取值是有限而且固定的,那么,你应该使用 ENUM 而不是VARCHAR
6. Prepared StatementsPrepared Statements很像存储过程,是一种运行在后台的SQL语句集合,我们可以从使用 prepared statements 获得很多好处,无论是性能问题还是安全问题。
7. Prepared Statements 可以检查一些你绑定好的变量,这样可以保护你的程序不会受到“SQL注入式”攻击
8. 垂直分表
9. 选择正确的存储引擎
#
key和index的区别?
##### 参考答案
1. key 是数据库的物理结构,它包含两层意义和作用,一是约束(偏重于约束和规范数据库的结构完整性),二是索引(辅助查询用的)。包括primary key, unique key, foreign key 等
2. index是数据库的物理结构,它只是辅助查询的,它创建时会在另外的表空间(mysql中的innodb表空间)以一个类似目录的结构存储。索引要分类的话,分为前缀索引、全文本索引等;
#
Mysql 中 MyISAM 和 InnoDB 的区别有哪些?
##### 参考答案
* 区别:
* 1. InnoDB支持事务,MyISAM不支持
* 2. 对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
* 3. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
* 4. InnoDB是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。
* 5. 但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此主键不应该过大,因为主键太大,其他索引也都会很大。
* 6. 而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
* 7. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
* 8. Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高;
* 如何选择:
* 1. 是否要支持事务,如果要请选择innodb,如果不需要可以考虑MyISAM;
* 2. 如果表中绝大多数都只是读查询,可以考虑MyISAM,如果既有读写也挺频繁,请使用InnoDB
* 3. 系统奔溃后,MyISAM恢复起来更困难,能否接受;
* 4. MySQL5.5版本开始Innodb已经成为Mysql的默认引擎(之前是MyISAM),说明其优势是有目共睹的,如果你不知道用什么,那就用InnoDB,至少不会差。
#
数据库表创建注意事项?
##### 参考答案
1. 字段名及字段配制合理性
* 剔除关系不密切的字段;
* 字段命名要有规则及相对应的含义(不要一部分英文,一部分拼音,还有类似a.b.c这样不明含义的字段);
* 字段命名尽量不要使用缩写(大多数缩写都不能明确字段含义);
* 字段不要大小写混用(想要具有可读性,多个英文单词可使用下划线形式连接);
* 字段名不要使用保留字或者关键字;
* 保持字段名和类型的一致性;
* 慎重选择数字类型;
* 给文本字段留足余量;
2. 系统特殊字段处理及建成后建议
* 添加删除标记(例如操作人、删除时间);
* 建立版本机制;
3. 表结构合理性配置
* 多型字段的处理,就是表中是否存在字段能够分解成更小独立的几部分(例如:人可以分为男人和女人);
* 多值字段的处理,可以将表分为三张表,这样使得检索和排序更加有调理,且保证数据的完整性!
4. 其它建议
* 对于大数据字段,独立表进行存储,以便影响性能(例如:简介字段);
* 使用varchar类型代替char,因为varchar会动态分配长度,char指定长度是固定的;
* 给表创建主键,对于没有主键的表,在查询和索引定义上有一定的影响;
* 避免表字段运行为null,建议设置默认值(例如:int类型设置默认值为0)在索引查询上,效率立显;
* 建立索引,最好建立在唯一和非空的字段上,建立太多的索引对后期插入、更新都存在一定的影响(考虑实际情况来创建);
#
MyISAM和InnoDB存储引擎使用的锁?
##### 参考答案
* MyISAM采用表级锁(table-level locking)。
* InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁
表级锁和行级锁对比:
* 表级锁: Mysql中锁定 粒度最大 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。
* 行级锁: Mysql中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
InnoDB存储引擎的锁的算法有三种:
* Record lock:单个行记录上的锁
* Gap lock:间隙锁,锁定一个范围,不包括记录本身
* Next-key lock:record+gap 锁定一个范围,包含记录本身
#
数据库一个大表如何优化?
##### 参考答案
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:
1. **限定数据的范围**: 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。;
2. **读/写分离**: 经典的数据库拆分方案,主库负责写,从库负责读;
3. **缓存**: 使用MySQL的缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存;
4. **垂直分区**:
根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。
简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说大家应该就更容易理解了。

* 垂直拆分的优点: 可以使得行数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。
* 垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;
5. **水平分区**:
保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。
水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。

水品拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL 并发能力没有什么意义,所以 水品拆分最好分库 。
水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨界点Join 性能较差,逻辑复杂。《Java工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络 I/O。
6. 数据库分片方案
* **客户端代理**: 分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。 当当网的 Sharding-JDBC 、阿里的TDDL是两种比较常用的实现。
* **中间件代理**: 在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。 我们现在谈的 Mycat、360的Atlas、网易的DDB等等都是这种架构的实现。
#
如何理解字符集及校对规则?
##### 参考答案
字符集指的是一种从二进制编码到某类字符符号的映射。校对规则则是指某种字符集下的排序规则。Mysql中每一种字符集都会对应一系列的校对规则。
Mysql采用的是类似继承的方式指定字符集的默认值,每个数据库以及每张数据表都有自己的默认值,他们逐层继承。比如:某个库中所有表的默认字符集将是该数据库所指定的字符集(这些表在没有指定字符集的情况下,才会采用默认字符集)
面试题1
能说下myisam 和 innodb的区别吗?
##### 参考答案
myisam引擎是5.1版本之前的默认引擎,支持全文检索、压缩、空间函数等,但是不支持事务和行级锁,所以一般用于有大量查询少量插入的场景来使用,而且myisam不支持外键,并且索引和数据是分开存储的。
innodb是基于聚簇索引建立的,和myisam相反它支持事务、外键,并且通过MVCC来支持高并发,索引和数据存储在一起。
#
面试题2
说下mysql的索引有哪些吧,聚簇和非聚簇索引又是什么?
##### 参考答案
索引按照数据结构来说主要包含B+树和Hash索引。
假设我们有张表,结构如下:
```C
create table user( id int(11) not null, age int(11) not null, primary key(id), key(age) );
```
B+树是左小右大的顺序存储结构,节点只包含id索引列,而叶子节点包含索引列和数据,这种数据和索引在一起存储的索引方式叫做聚簇索引,一张表只能有一个聚簇索引。假设没有定义主键,InnoDB会选择一个唯一的非空索引代替,如果没有的话则会隐式定义一个主键作为聚簇索引。

这是主键聚簇索引存储的结构,那么非聚簇索引的结构是什么样子呢?非聚簇索引(二级索引)保存的是主键id值,这一点和myisam保存的是数据地址是不同的。

最终,我们一张图看看InnoDB和Myisam聚簇和非聚簇索引的区别

#
面试题3
那你知道什么是覆盖索引和回表吗?
##### 参考答案
覆盖索引指的是在一次查询中,如果一个索引包含或者说覆盖所有需要查询的字段的值,我们就称之为覆盖索引,而不再需要回表查询。
而要确定一个查询是否是覆盖索引,我们只需要explain sql语句看Extra的结果是否是“Using index”即可。
以上面的user表来举例,我们再增加一个name字段,然后做一些查询试试。
```C
explain select * from user where age=1; //查询的name无法从索引数据获取 explain select id,age from user where age=1; //可以直接从索引获取
```
#
面试题4
锁的类型有哪些呢?
##### 参考答案
* mysql锁分为共享锁和排他锁,也叫做读锁和写锁。
* 读锁是共享的,可以通过lock in share mode实现,这时候只能读不能写。
* 写锁是排他的,它会阻塞其他的写锁和读锁。从颗粒度来区分,可以分为表锁和行锁两种。
* 表锁会锁定整张表并且阻塞其他用户对该表的所有读写操作,比如alter修改表结构的时候会锁表。
* 行锁又可以分为乐观锁和悲观锁,悲观锁可以通过for update实现,乐观锁则通过版本号实现。
#
面试题5
你能说下事务的基本特性和隔离级别吗?
##### 参考答案
* 事务基本特性ACID分别是:
* 原子性指的是一个事务中的操作要么全部成功,要么全部失败。
* 一致性指的是数据库总是从一个一致性的状态转换到另外一个一致性的状态。比如A转账给B100块钱,假设中间sql执行过程中系统崩溃A也不会损失100块,因为事务没有提交,修改也就不会保存到数据库。
* 隔离性指的是一个事务的修改在最终提交前,对其他事务是不可见的。
* 持久性指的是一旦事务提交,所做的修改就会永久保存到数据库中。
* 隔离性有4个隔离级别,分别是:
* 1. read uncommit 读未提交,可能会读到其他事务未提交的数据,也叫做脏读。
用户本来应该读取到id=1的用户age应该是10,结果读取到了其他事务还没有提交的事务,结果读取结果age=20,这就是脏读。

* 2. read commit 读已提交,两次读取结果不一致,叫做不可重复读。
不可重复读解决了脏读的问题,他只会读取已经提交的事务。
用户开启事务读取id=1用户,查询到age=10,再次读取发现结果=20,在同一个事务里同一个查询读取到不同的结果叫做不可重复读。

* 3. repeatable read 可重复复读,这是mysql的默认级别,就是每次读取结果都一样,但是有可能产生幻读。
* 4. serializable 串行,一般是不会使用的,他会给每一行读取的数据加锁,会导致大量超时和锁竞争的问题。
#
面试题6
那ACID靠什么保证的呢?
##### 参考答案
* A 原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
* C 一致性一般由代码层面来保证
* I 隔离性由MVCC来保证
* D 持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log恢复
#
面试题7
那你说说什么是幻读,什么是MVCC?
##### 参考答案
要说幻读,首先要了解MVCC,MVCC叫做多版本并发控制,实际上就是保存了数据在某个时间节点的快照。
我们每行数实际上隐藏了两列,创建时间版本号,过期(删除)时间版本号,每开始一个新的事务,版本号都会自动递增。
还是拿上面的user表举例子,假设我们插入两条数据,他们实际上应该长这样。
a-draft-type=“table” data-size=“normal” data-row-style=“normal”>
id name create_version delete_version
这时候假设小明去执行查询,此时current_version=3
select * from user where id<=3; 复制代码
同时,小红在这时候开启事务去修改id=1的记录,current_version=4
update user set name=‘张三三’ where id=1; 复制代码
执行成功后的结果是这样的
a-draft-type=“table” data-size=“normal” data-row-style=“normal”>
id name create_version delete_version
如果这时候还有小黑在删除id=2的数据,current_version=5,执行后结果是这样的。
a-draft-type=“table” data-size=“normal” data-row-style=“normal”>
id name create_version delete_version
由于MVCC的原理是查找创建版本小于或等于当前事务版本,删除版本为空或者大于当前事务版本,小明的真实的查询应该是这样
select * from user where id<=3 and create_version<=3 and (delete_version>3 or delete_version is null); 复制代码
所以小明最后查询到的id=1的名字还是’张三’,并且id=2的记录也能查询到。这样做是为了保证事务读取的数据是在事务开始前就已经存在的,要么是事务自己插入或者修改的。
明白MVCC原理,我们来说什么是幻读就简单多了。举一个常见的场景,用户注册时,我们先查询用户名是否存在,不存在就插入,假定用户名是唯一索引。
小明开启事务current_version=6查询名字为’王五’的记录,发现不存在。
小红开启事务current_version=7插入一条数据,结果是这样:
a-draft-type=“table” data-size=“normal” data-row-style=“normal”>
id Name create_version delete_version
小明执行插入名字’王五’的记录,发现唯一索引冲突,无法插入,这就是幻读。
#
面试题8
那你知道什么是间隙锁吗?
##### 参考答案
间隙锁是可重复读级别下才会有的锁,结合MVCC和间隙锁可以解决幻读的问题。我们还是以user举例,假设现在user表有几条记录
e data-draft-node=“block” data-draft-type=“table” data-size=“normal” data-row-style=“normal”>
当我们执行:
begin; select * from user where age=20 for update; begin; insert into user(age) values(10); #成功 insert into user(age) values(11); #失败 insert into user(age) values(20); #失败 insert into user(age) values(21); #失败 insert into user(age) values(30); #失败
只有10可以插入成功,那么因为表的间隙mysql自动帮我们生成了区间(左开右闭)
(negative infinity,10],(10,20],(20,30],(30,positive infinity)
由于20存在记录,所以(10,20],(20,30]区间都被锁定了无法插入、删除。
如果查询21呢?就会根据21定位到(20,30)的区间(都是开区间)。
需要注意的是唯一索引是不会有间隙索引的。
#
面试题9
你们数据量级多大?分库分表怎么做的?
##### 参考答案
首先分库分表分为垂直和水平两个方式,一般来说我们拆分的顺序是先垂直后水平。
* 垂直分库
基于现在微服务拆分来说,都是已经做到了垂直分库了。

* 垂直分表
如果表字段比较多,将不常用的、数据较大的等等做拆分。

* 水平分表
首先根据业务场景来决定使用什么字段作为分表字段(sharding_key),比如我们现在日订单1000万,我们大部分的场景来源于C端,我们可以用user_id作为sharding_key,数据查询支持到最近3个月的订单,超过3个月的做归档处理,那么3个月的数据量就是9亿,可以分1024张表,那么每张表的数据大概就在100万左右。
比如用户id为100,那我们都经过hash(100),然后对1024取模,就可以落到对应的表上了。
#
面试题10
那分表后的ID怎么保证唯一性的呢?
##### 参考答案
因为我们主键默认都是自增的,那么分表之后的主键在不同表就肯定会有冲突了。有几个办法考虑:
1. 设定步长,比如1-1024张表我们分别设定1-1024的基础步长,这样主键落到不同的表就不会冲突了。
2. 分布式ID,自己实现一套分布式ID生成算法或者使用开源的比如雪花算法这种。
3. 分表后不使用主键作为查询依据,而是每张表单独新增一个字段作为唯一主键使用,比如订单表订单号是唯一的,不管最终落在哪张表都基于订单号作为查询依据,更新也一样。
#
面试题11
分表后非sharding_key的查询怎么处理呢?
##### 参考答案
1. 可以做一个mapping表,比如这时候商家要查询订单列表怎么办呢?不带user_id查询的话你总不能扫全表吧?所以我们可以做一个映射关系表,保存商家和用户的关系,查询的时候先通过商家查询到用户列表,再通过user_id去查询。
2. 打宽表,一般而言,商户端对数据实时性要求并不是很高,比如查询订单列表,可以把订单表同步到离线(实时)数仓,再基于数仓去做成一张宽表,再基于其他如es提供查询服务。
3. 数据量不是很大的话,比如后台的一些查询之类的,也可以通过多线程扫表,然后再聚合结果的方式来做。或者异步的形式也是可以的。
```java
List
for (int shardingIndex = 0; shardingIndex < 1024; shardingIndex++) {
taskList.add(() -> (userMapper.getProcessingAccountList(shardingIndex)));
}
List
try {
list = taskExecutor.executeTask(taskList);
} catch (Exception e) {
//do something
}
public class TaskExecutor {
public
List
List
for (Future
result.add(future.get());
} return result;
}
}
```
#
面试题12
说说mysql主从同步怎么做的吧?
##### 参考答案
首先先了解mysql主从同步的原理
1. master提交完事务后,写入binlog
2. slave连接到master,获取binlog
3. master创建dump线程,推送binglog到slave
4. slave启动一个IO线程读取同步过来的master的binlog,记录到relay log中继日志中
5. slave再开启一个sql线程读取relay log事件并在slave执行,完成同步
6. slave记录自己的binglog

由于mysql默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念。
* 全同步复制
主库写入binlog后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响。
* 半同步复制
和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回ACK确认给主库,主库收到至少一个从库的确认就认为写操作完成。
#
面试题13
那主从的延迟怎么解决呢?
##### 参考答案
这个问题貌似真的是个无解的问题,只能是说自己来判断了,需要走主库的强制走主库查询。
#
面试题14
为什么用自增列作为主键?
##### 参考答案
1. 如果我们定义了主键(PRIMARY KEY),那么InnoDB会选择主键作为聚集索引。
如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引。
如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐含的聚集索引(ROWID随着行记录的写入而主键递增,这个ROWID不像ORACLE的ROWID那样可引用,是隐含的)。
2. 数据记录本身被存于主索引(一颗B+Tree)的叶子节点上,这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放
因此每当有一条新的记录插入时,MySQL会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)
3. 如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页
4. 如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置
此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销
同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE来重建表并优化填充页面。
#
面试题15
为什么使用数据索引能提高效率?
##### 参考答案
1. 数据索引的存储是有序的
2. 在有序的情况下,通过索引查询一个数据是无需遍历索引记录的
3. 极端情况下,数据索引的查询效率为二分法查询效率,趋近于 log2(N)
#
面试题16
B+树索引和哈希索引的区别?
##### 参考答案
B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的节点间有指针相互链接,是有序的,如下图:

哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可,是无序的,如下图所示:

#
面试题17
哈希索引的优势?
##### 参考答案
等值查询,哈希索引具有绝对优势(前提是:没有大量重复键值,如果大量重复键值时,哈希索引的效率很低,因为存在所谓的哈希碰撞问题。)
#
面试题18
哈希索引不适用的场景?
##### 参考答案
1. 不支持范围查询
2. 不支持索引完成排序
3. 不支持联合索引的最左前缀匹配规则
通常,B+树索引结构适用于绝大多数场景,像下面这种场景用哈希索引才更有优势:
在HEAP表中,如果存储的数据重复度很低(也就是说基数很大),对该列数据以等值查询为主,没有范围查询、没有排序的时候,特别适合采用哈希索引,例如这种SQL:
**仅等值查询**
```C
select id, name from table where name='李明';
```
而常用的 InnoDB 引擎中默认使用的是B+树索引,它会实时监控表上索引的使用情况。
如果认为建立哈希索引可以提高查询效率,则自动在内存中的“自适应哈希索引缓冲区”建立哈希索引(在InnoDB中默认开启自适应哈希索引)。
通过观察搜索模式,MySQL会利用index key的前缀建立哈希索引,如果一个表几乎大部分都在缓冲池中,那么建立一个哈希索引能够加快等值查询。
注意:在某些工作负载下,通过哈希索引查找带来的性能提升远大于额外的监控索引搜索情况和保持这个哈希表结构所带来的开销。
但某些时候,在负载高的情况下,自适应哈希索引中添加的read/write锁也会带来竞争,比如高并发的join操作。like操作和%的通配符操作也不适用于自适应哈希索引,可能要关闭自适应哈希索引。
#
面试题19
B树和B+树的区别?
##### 参考答案
1. B树,每个节点都存储key和data,所有节点组成这棵树,并且叶子节点指针为nul,叶子结点不包含任何关键字信息。

2. B+树,所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接
所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含需要查找的有效信息)

#
面试题20
为什么说B+比B树更适合实际应用中操作系统的文件索引和数据库索引?
##### 参考答案
1. B+的磁盘读写代价更低。
B+的内部结点并没有指向关键字具体信息的指针,因此其内部结点相对B树更小。
如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。
2. B+-tree的查询效率更加稳定。
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
#
面试题21
MySQL联合索引?
##### 参考答案
1. 联合索引是两个或更多个列上的索引。
对于联合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。
例如索引是key index (a,b,c). 可以支持a 、 a,b 、 a,b,c 3种组合进行查找,但不支持 b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效。
2. 利用索引中的附加列,您可以缩小搜索的范围,但使用一个具有两列的索引不同于使用两个单独的索引。
复合索引的结构与电话簿类似,人名由姓和名构成,电话簿首先按姓氏对进行排序,然后按名字对有相同姓氏的人进行排序。
如果您知道姓,电话簿将非常有用;如果您知道姓和名,电话簿则更为有用,但如果您只知道名不知道姓,电话簿将没有用处。
#
面试题22
什么情况下应不建或少建索引?
##### 参考答案
1. 表记录太少
2. 经常插入、删除、修改的表
3. 数据重复且分布平均的表字段,假如一个表有10万行记录,有一个字段A只有T和F两种值,且每个值的分布概率大约为50%,那么对这种表A字段建索引一般不会提高数据库的查询速度。
4. 经常和主字段一块查询但主字段索引值比较多的表字段
#
面试题23
什么是表分区?
##### 参考答案
表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成。
#
面试题24
表分区与分表的区别?
##### 参考答案
* 分表:指的是通过一定规则,将一张表分解成多张不同的表。比如将用户订单记录根据时间成多个表。
* 分表与分区的区别在于:分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表。
#
面试题25
表分区有什么好处?
##### 参考答案
1. 存储更多数据。分区表的数据可以分布在不同的物理设备上,从而高效地利用多个硬件设备。和单个磁盘或者文件系统相比,可以存储更多数据
2. 优化查询。在where语句中包含分区条件时,可以只扫描一个或多个分区表来提高查询效率;涉及sum和count语句时,也可以在多个分区上并行处理,最后汇总结果。
3. 分区表更容易维护。例如:想批量删除大量数据可以清除整个分区。
4. 避免某些特殊的瓶颈,例如InnoDB的单个索引的互斥访问,ext3问价你系统的inode锁竞争等。
#
面试题26
分区表的限制因素?
##### 参考答案
1. 一个表最多只能有1024个分区
2. MySQL5.1中,分区表达式必须是整数,或者返回整数的表达式。在MySQL5.5中提供了非整数表达式分区的支持。
3. 如果分区字段中有主键或者唯一索引的列,那么多有主键列和唯一索引列都必须包含进来。即:分区字段要么不包含主键或者索引列,要么包含全部主键和索引列。
4. 分区表中无法使用外键约束
5. MySQL的分区适用于一个表的所有数据和索引,不能只对表数据分区而不对索引分区,也不能只对索引分区而不对表分区,也不能只对表的一部分数据分区。
#
面试题27
如何判断当前MySQL是否支持分区?
##### 参考答案
命令:show variables like '%partition%' 运行结果: have_partintioning 的值为YES,表示支持分区。
#
面试题28
MySQL支持的分区类型有哪些?
##### 参考答案
* RANGE分区: 这种模式允许将数据划分不同范围。例如可以将一个表通过年份划分成若干个分区
* LIST分区: 这种模式允许系统通过预定义的列表的值来对数据进行分割。按照List中的值分区,与RANGE的区别是,range分区的区间范围值是连续的。
* HASH分区 :这中模式允许通过对表的一个或多个列的Hash Key进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区。例如可以建立一个对表主键进行分区的表。
* KEY分区 :上面Hash模式的一种延伸,这里的Hash Key是MySQL系统产生的。
#
面试题29
四种隔离级别?
##### 参考答案
* Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
* Repeatable read (可重复读):可避免脏读、不可重复读的发生。
* Read committed (读已提交):可避免脏读的发生。
* Read uncommitted (读未提交):最低级别,任何情况都无法保证。
#
面试题30
关于MVVC的运行原理介绍?
##### 参考答案
MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control)
注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control
**MVCC最大的好处**:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能,现阶段几乎所有的RDBMS,都支持了MVCC。
1. LBCC:Lock-Based Concurrency Control,基于锁的并发控制
2. MVCC:Multi-Version Concurrency Control
基于多版本的并发控制协议。纯粹基于锁的并发机制并发量低,MVCC是在基于锁的并发控制上的改进,主要是在读操作上提高了并发量。
#
面试题31
在MVCC并发控制中,读操作可以分成两类?
##### 参考答案
1. 快照读 (snapshot read):读取的是记录的可见版本 (有可能是历史版本),不用加锁(共享读锁s锁也不加,所以不会阻塞其他事务的写)
2. 当前读 (current read):读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录
#
面试题32
行级锁定的优点有哪些?
##### 参考答案
1. 当在许多线程中访问不同的行时只存在少量锁定冲突。
2. 回滚时只有少量的更改
3. 可以长时间锁定单一的行。
#
面试题33
行级锁定的缺点有哪些?
##### 参考答案
1. 比页级或表级锁定占用更多的内存。
2. 当在表的大部分中使用时,比页级或表级锁定速度慢,因为你必须获取更多的锁。
3. 如果你在大部分数据上经常进行GROUP BY操作或者必须经常扫描整个表,比其它锁定明显慢很多。
4. 用高级别锁定,通过支持不同的类型锁定,你也可以很容易地调节应用程序,因为其锁成本小于行级锁定。
#
面试题34
你对MySQL优化了解多少?
##### 参考答案
1. 开启查询缓存,优化查询
2. explain你的select查询,这可以帮你分析你的查询语句或是表结构的性能瓶颈。EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的
3. 当只要一行数据时使用limit 1,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据
4. 为搜索字段建索引
5. 使用 ENUM 而不是 VARCHAR。如果你有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,你知道这些字段的取值是有限而且固定的,那么,你应该使用 ENUM 而不是VARCHAR
6. Prepared StatementsPrepared Statements很像存储过程,是一种运行在后台的SQL语句集合,我们可以从使用 prepared statements 获得很多好处,无论是性能问题还是安全问题。
7. Prepared Statements 可以检查一些你绑定好的变量,这样可以保护你的程序不会受到“SQL注入式”攻击
8. 垂直分表
9. 选择正确的存储引擎
#
面试题35
key和index的区别?
##### 参考答案
1. key 是数据库的物理结构,它包含两层意义和作用,一是约束(偏重于约束和规范数据库的结构完整性),二是索引(辅助查询用的)。包括primary key, unique key, foreign key 等
2. index是数据库的物理结构,它只是辅助查询的,它创建时会在另外的表空间(mysql中的innodb表空间)以一个类似目录的结构存储。索引要分类的话,分为前缀索引、全文本索引等;
#
面试题36
Mysql 中 MyISAM 和 InnoDB 的区别有哪些?
##### 参考答案
* 区别:
* 1. InnoDB支持事务,MyISAM不支持
* 2. 对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
* 3. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
* 4. InnoDB是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。
* 5. 但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此主键不应该过大,因为主键太大,其他索引也都会很大。
* 6. 而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
* 7. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
* 8. Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高;
* 如何选择:
* 1. 是否要支持事务,如果要请选择innodb,如果不需要可以考虑MyISAM;
* 2. 如果表中绝大多数都只是读查询,可以考虑MyISAM,如果既有读写也挺频繁,请使用InnoDB
* 3. 系统奔溃后,MyISAM恢复起来更困难,能否接受;
* 4. MySQL5.5版本开始Innodb已经成为Mysql的默认引擎(之前是MyISAM),说明其优势是有目共睹的,如果你不知道用什么,那就用InnoDB,至少不会差。
#
面试题37
数据库表创建注意事项?
##### 参考答案
1. 字段名及字段配制合理性
* 剔除关系不密切的字段;
* 字段命名要有规则及相对应的含义(不要一部分英文,一部分拼音,还有类似a.b.c这样不明含义的字段);
* 字段命名尽量不要使用缩写(大多数缩写都不能明确字段含义);
* 字段不要大小写混用(想要具有可读性,多个英文单词可使用下划线形式连接);
* 字段名不要使用保留字或者关键字;
* 保持字段名和类型的一致性;
* 慎重选择数字类型;
* 给文本字段留足余量;
2. 系统特殊字段处理及建成后建议
* 添加删除标记(例如操作人、删除时间);
* 建立版本机制;
3. 表结构合理性配置
* 多型字段的处理,就是表中是否存在字段能够分解成更小独立的几部分(例如:人可以分为男人和女人);
* 多值字段的处理,可以将表分为三张表,这样使得检索和排序更加有调理,且保证数据的完整性!
4. 其它建议
* 对于大数据字段,独立表进行存储,以便影响性能(例如:简介字段);
* 使用varchar类型代替char,因为varchar会动态分配长度,char指定长度是固定的;
* 给表创建主键,对于没有主键的表,在查询和索引定义上有一定的影响;
* 避免表字段运行为null,建议设置默认值(例如:int类型设置默认值为0)在索引查询上,效率立显;
* 建立索引,最好建立在唯一和非空的字段上,建立太多的索引对后期插入、更新都存在一定的影响(考虑实际情况来创建);
#
面试题38
MyISAM和InnoDB存储引擎使用的锁?
##### 参考答案
* MyISAM采用表级锁(table-level locking)。
* InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁
表级锁和行级锁对比:
* 表级锁: Mysql中锁定 粒度最大 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。
* 行级锁: Mysql中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
InnoDB存储引擎的锁的算法有三种:
* Record lock:单个行记录上的锁
* Gap lock:间隙锁,锁定一个范围,不包括记录本身
* Next-key lock:record+gap 锁定一个范围,包含记录本身
#
面试题39
数据库一个大表如何优化?
##### 参考答案
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:
1. **限定数据的范围**: 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。;
2. **读/写分离**: 经典的数据库拆分方案,主库负责写,从库负责读;
3. **缓存**: 使用MySQL的缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存;
4. **垂直分区**:
根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。
简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说大家应该就更容易理解了。

* 垂直拆分的优点: 可以使得行数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。
* 垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;
5. **水平分区**:
保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。
水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。

水品拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL 并发能力没有什么意义,所以 水品拆分最好分库 。
水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨界点Join 性能较差,逻辑复杂。《Java工程师修炼之道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络 I/O。
6. 数据库分片方案
* **客户端代理**: 分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。 当当网的 Sharding-JDBC 、阿里的TDDL是两种比较常用的实现。
* **中间件代理**: 在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。 我们现在谈的 Mycat、360的Atlas、网易的DDB等等都是这种架构的实现。
#
面试题40
如何理解字符集及校对规则?
##### 参考答案
字符集指的是一种从二进制编码到某类字符符号的映射。校对规则则是指某种字符集下的排序规则。Mysql中每一种字符集都会对应一系列的校对规则。
Mysql采用的是类似继承的方式指定字符集的默认值,每个数据库以及每张数据表都有自己的默认值,他们逐层继承。比如:某个库中所有表的默认字符集将是该数据库所指定的字符集(这些表在没有指定字符集的情况下,才会采用默认字符集)
点赞
回复
X