Appearance
数据库表设计时,字段类型如何选择?
更小的数据类型通常更好,应该尽量使用可以正确存储数据的最小数据类型。更小的数据类型通常更快,因为它们占用更少的磁盘、内存和 CPU 缓存,并且处理时需要的 CPU 周期也更少。
MySQL 索引,以及它们的好处和坏处
得分点 检索效率、存储资源、索引维护
标准回答 索引就像指向表行的指针,是一种允许查询操作快速确定哪些行符合 WHERE 子句中的条件,并检索到这些行的其他列值的数据结构;索引主要有普通索引、唯一索引、主键索引、外键索引、全文索引、复合索引几种;在大数据量的查询中,合理使用索引的优点非常明显,不仅能大幅提高匹配 where 条件的检索效率,还能用于排序和分组操作的加速。当时索引如果使用不当也有比较大的坏处:比如索引必定会增加存储资源的消耗;同时也增大了插入、更新和删除操作的维护成本,因为每个增删改操作后相应列的索引都必须被更新。
加分回答 只要创建了索引,就一定会走索引吗? 不一定。 比如,在使用组合索引的时候,如果没有遵从“最左前缀”的原则进行搜索,则索引是不起作用的。 举例,假设在 id、name、age 字段上已经成功建立了一个名为 MultiIdx 的组合索引。索引行中按 id、name、age 的顺序存放,索引可以搜索 id、(id,name)、(id, name, age)字段组合。如果列不构成索引最左面的前缀,那么 MySQL 不能使用局部索引,如(age)或者(name,age)组合则不能使用该索引查询。
说说 MySQL 的事务隔离级别?
得分点 未提交读、已提交读、可重复读、可串行化
标准回答
SQL 标准定义了四种隔离级别,这四种隔离级别分别是:
- 读未提交(READ UNCOMMITTED)
- 读已提交 (READ COMMITTED)
- 可重复读 (REPEATABLE READ)
- 串行化 (SERIALIZABLE)
事务隔离是为了解决脏读、不可重复读、幻读问题
- 读未提交 -> 均未解决
- 读已提交 -> 解决脏读问题
- 可重复读 -> 解决脏读、不可重复读问题
- 串行化 -> 解决全部问题(但性能最差)
上述 4 种隔离级别 MySQL 都支持,并且 InnoDB 存储引擎默认的支持隔离级别是 REPEATABLE READ,但是与标准 SQL 不同的是,InnoDB 存储引擎在 REPEATABLE READ 事务隔离级别下,使用 Next-Key Lock 的锁算法,因此避免了幻读的产生。所以,InnoDB 存储引擎在默认的事务隔离级别下已经能完全保证事务的隔离性要求,即达到 SQL 标准的 SERIALIZABLE 隔离级别;
加分回答
- READ UNCOMMITTED: 它是性能最好、也最野蛮的方式,因为它压根儿就不加锁,所以根本谈不上什么隔离效果,可以理解为没有隔离。
- SERIALIZABLE: 读的时候加共享锁,其他事务可以并发读,但是不能写。写的时候加排它锁,其他事务不能并发写也不能并发读。
- REPEATABLE READ & READ COMMITTED: 为了解决不可重复读,MySQL 采用了 MVVC (多版本并发控制) 的方式。 我们在数据库表中看到的一行记录可能实际上有多个版本,每个版本的记录除了有数据本身外,还要有一个表示版本的字段,记为 row trx_id,而这个字段就是使其产生的事务的 id,事务 ID 记为 transaction id,它在事务开始的时候向事务系统申请,按时间先后顺序递增。
说说聚簇索引和非聚簇索引?
得分点 索引即数据、二次查询
标准回答
两者主要区别是数据和索引是否分离。
- 聚簇索引是将数据与索引存储到一起,找到索引也就找到了数据;
- 而非聚簇索引是将数据和索引存储分离开,索引树的叶子节点存储了数据行的地址。
在 InnoDB 中,一个表有且仅有一个聚簇索引(因为原始数据只留一份,而数据和聚簇索引在一起),并且该索引是建立在主键上的,即使没有指定主键,也会特殊处理生成一个聚簇索引;其他索引都是辅助索引,使用辅助索引访问索引外的其他字段时都需要进行二次查找。
而在 MyISAM 中,所有索引都是非聚簇索引,叶子节点存储着数据的地址,对于主键索引和普通索引在存储上没有区别。
加分回答
在 InnoDB 存储引擎中,可以将 B+树索引分为聚簇索引和辅助索引(非聚簇索引)。无论是何种索引,每个页的大小都为 16KB,且不能更改。
- 聚簇索引是根据主键创建的一棵 B+树,聚簇索引的叶子节点存放了表中的所有记录。
- 辅助索引是根据索引键创建的一棵 B+树,与聚簇索引不同的是,其叶子节点仅存放索引键值,以及该索引键值指向的主键。也就是说,如果通过辅助索引来查找数据,那么当找到辅助索引的叶子节点后,很有可能还需要根据主键值查找聚簇索引来得到数据,这种查找方式又被称为书签查找。
因为辅助索引不包含行记录的所有数据,这就意味着每页可以存放更多的键值,因此其高度一般都要小于聚簇索引。
数据库为什么不用红黑树而用 B+树?
得分点 磁盘 IO
标准回答
首先,红黑树是一种近似平衡二叉树(不完全平衡),结点非黑即红的树,它的树高最高不会超过 2*log(n),因此查找的时间复杂度为 O(log(n)),无论是增删改查,它的性能都十分稳定;
但是,红黑树本质还是二叉树,在数据量非常大时,需要访问+判断的节点数还是会比较多,同时数据是存在磁盘上的,访问需要进行磁盘 IO,导致效率较低;
而 B+树是多叉的,可以有效减少磁盘 IO 次数;同时 B+树增加了叶子结点间的连接,能保证范围查询时找到起点和终点后快速取出需要的数据。
加分回答
红黑树做索引底层数据结构的缺陷 试想一下,以红黑树作为底层数据结构在面对在些表数据动辄数百万数千万的场景时,创建的索引它的树高得有多高?
索引从根节点开始查找,而如果我们需要查找的数据在底层的叶子节点上,那么树的高度是多少,就要进行多少次查找,数据存在磁盘上,访问需要进行磁盘 IO,这会导致效率过低; 那么红黑树作为索引数据结构的弊端即是:树的高度过高导致查询效率变慢。
说说乐观锁和悲观锁?
得分点 乐观锁、悲观锁定义及使用场景
- 乐观锁:乐观锁总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和 CAS 算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实都是提供的乐观锁。
- 悲观锁:悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。
传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
加分回答 两种锁的使用场景
- 乐观锁: GIT,SVN,CVS 等代码版本控制管理器,就是一个乐观锁使用很好的场景,例如:A、B 程序员,同时从 SVN 服务器上下载了 code.html 文件,当 A 完成提交后,此时 B 再提交,那么会报版本冲突,此时需要 B 进行版本处理合并后,再提交到服务器。这其实就是乐观锁的实现全过程。如果此时使用的是悲观锁,那么意味者所有程序员都必须一个一个等待操作提交完,才能访问文件,这是难以接受的。
- 悲观锁: 悲观锁的好处在于可以减少并发,但是当并发量非常大的时候,由于锁消耗资源、锁定时间过长等原因,很容易导致系统性能下降,资源消耗严重。因此一般我们可以在并发量不是很大,并且出现并发情况导致的异常用户和系统都很难以接受的情况下,会选择悲观锁进行。
索引设计的原则?
- 适合索引的列是出现在 where 子句中的列,或者连接子句中指定的列
- 基数较小的类,索引效果较差,没有必要在此列建立索引
- 使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
- 不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。
创建索引的原则(重点)
- 最左前缀匹配原则,组合索引非常重要的原则,mysql 会一直向右匹配直到遇到范围查询(
>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4如果建立(a,b,c,d)顺序的索引,d 是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d 的顺序可以任意调整。 - 较频繁作为查询条件的字段才去创建索引
- 更新频繁字段不适合创建索引
- 若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
- 尽量的扩展索引,不要新建索引。比如表中已经有 a 的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可
- 定义有外键的数据列一定要建立索引
- 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引
- 对于定义为 text、image 和 bit 的数据类型的列不要建立索引
- 使用非空字段:应该指定列为 NOT NULL,除非你想存储 NULL。在 mysql 中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用 0、一个特殊的值或者一个空串代替空值;
- 取值离散大的字段:(变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过 count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高;
- 索引字段越小越好:数据库的数据存储以页为单位一页存储的数据越多一次 IO 操作获取的数据越大效率越高。
查询操作方法需要使用事务吗?
例如生成比较复杂的报表时,想查询的多个不同表的数据在都在同一时间维度时,此需要开启事务,这样确保不会在生成报表这段长时间中,被其他事务将数据修改了,导致数据的时间维度不一致。