最全最新分享:mysql索引的数据结构。含mysql面试专题及答案

索引

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。

我们知道,数据库查询是数据库的最主要功能之一。我们都希望查询数据的速度能尽可能的快,因此数据库系统的设计者会从查询算法的角度进行优化。

最基本的查询算法当然是顺序查找(linear search),这种复杂度为O(n)的算法在数据量很大时显然是糟糕的,好在计算机科学的发展提供了很多更优秀的查找算法,例如二分查找(binary search)、二叉树查找(binary tree search)等。

如果稍微分析一下会发现,每种查找算法都只能应用于特定的数据结构之上,例如二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树上,但是数据本身的组织结构不可能完全满足各种数据结构(例如,理论上不可能同时将两列都按顺序进行组织),所以,在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

看一个例子:

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第1张图片

图1展示了一种可能的索引方式。左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(注意逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在O(log2n)的复杂度内获取到相应数据。

虽然这是一个货真价实的索引,但是实际的数据库系统几乎没有使用二叉查找树或其进化品种红黑树(red-black tree)实现的,原因会在下文介绍。

B-Tree和B+Tree

目前大部分数据库系统及文件系统都采用B-Tree或其变种B+Tree作为索引结构,在本文的下一节会结合存储器原理及计算机存取原理讨论为什么B-Tree和B+Tree在被如此广泛用于索引,这一节先单纯从数据结构角度描述它们。

B-Tree

是一种多路搜索树(并不是二叉的):

1.定义任意非叶子结点最多只有M个儿子;且M>2;

2.根结点的儿子数为[2, M];

3.除根结点以外的非叶子结点的儿子数为[M/2, M];

4.每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)

5.非叶子结点的关键字个数=指向儿子的指针个数-1;

6.非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];

7.非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的

子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;

8.所有叶子结点位于同一层;

9.每个k对应一个data。

如:(M=3)相当于一个2–3树,2–3树是一个这样的一棵树, 它的每个节点要么有2个孩子和1个数据元素,要么有3个孩子和2个数据元素,叶子节点没有孩子,并且有1个或2个数据元素。

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第2张图片

B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点;B-Tree上查找算法的伪代码如下:

BTree_Search(node, key) { if(node == null) return null; foreach(node.key) { if(node.key[i] == key) return node.data[i]; if(node.key[i] > key) return BTree_Search(point[i]->node); } return BTree_Search(point[i+1]->node); } data = BTree_Search(root, my_key);

B-树的特性:

1.关键字集合分布在整颗树中;

2.任何一个关键字出现且只出现在一个结点中;

3.搜索有可能在非叶子结点结束;

4.其搜索性能等价于在关键字全集内做一次二分查找;

5.自动层次控制;

B-树的自控制:

B树中每一个内部节点会包含一定数量的键值。通常,键值的数量被选定在d和2d之间。在实际中,键值占用了节点中大部分的空间。因数2将保证节点可以被拆分或组合。如果一个内部节点有2d个键值,那么添加一个键值给此节点的过程,将会拆分2d键值为2个d键值的节点,并把此键值添加给父节点。每一个拆分的节点需要最小数目的键值。相似地,如果一个内部节点和他的邻居两者都有d个键值,那么将通过它与邻居的合并来删除一个键值。删除此键值将导致此节点拥有d-1个键值;与邻居的合并则加上d个键值,再加上从邻居节点的父节点移来的一个键值。结果为完全填充的2d个键值。

B-树的构造过程:

下面是往B树中依次插入

6 10 4 14 5 11 15 3 2 12 1 7 8 8 6 3 6 21 5 15 15 6 32 23 45 65 7 8 6 5 4

B+Tree

B-Tree有许多变种,其中最常见的是B+Tree,例如MySQL就普遍使用B+Tree实现其索引结构。

与B-Tree相比,B+Tree有以下不同点:

1.非叶子结点的子树指针与关键字个数相同;

2.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);

3.为所有叶子结点增加一个链指针;

4.所有关键字都在叶子结点出现;

5.内节点不存储data,只存储key

如:(M=3)

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第3张图片

B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;

B+的特性:

1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;

2.不可能在非叶子结点命中;

3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;

4.更适合文件索引系统;

B+树的构造过程:

下面是往B+树中依次插入

6 10 4 14 5 11 15 3 2 12 1 7 8 8 6 3 6 21 5 15 15 6 32 23 45 65 7 8 6 5 4

索引的物理存储

一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。

B-tree

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第4张图片

假如每个盘块可以正好存放一个B树的结点(正好存放2个文件名)。那么一个BTNODE结点就代表一个盘块,而子树指针就是存放另外一个盘块的地址。

下面,咱们来模拟下查找文件29的过程:

1.根据根结点指针找到文件目录的根磁盘块1,将其中的信息导入内存。【磁盘IO操作 1次】

2.此时内存中有两个文件名17、35和三个存储其他磁盘页面地址的数据。根据算法我们发现:17<29<35,因此我们找到指针p2。

3.根据p2指针,我们定位到磁盘块3,并将其中的信息导入内存。【磁盘IO操作 2次】

4.此时内存中有两个文件名26,30和三个存储其他磁盘页面地址的数据。根据算法我们发现:26<29<30,因此我们找到指针p2。

5.根据p2指针,我们定位到磁盘块8,并将其中的信息导入内存。【磁盘IO操作 3次】

6.此时内存中有两个文件名28,29。根据算法我们查找到文件名29,并定位了该文件内存的磁盘地址。

分析上面的过程,发现需要3次磁盘IO操作和3次内存查找操作。关于内存中的文件名查找,由于是一个有序表结构,可以利用折半查找提高效率。至于IO操作是影响整个B树查找效率的决定因素。

当然,如果我们使用平衡二叉树的磁盘存储结构来进行查找,磁盘4次,最多5次,而且文件越多,B树比平衡二叉树所用的磁盘IO操作次数将越少,效率也越高。

B+tree

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第5张图片

B+tree的优点:

B+-tree的磁盘读写代价更低

****B+-tree****的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。

举个例子,假设磁盘中的一个盘块容纳16bytes,而一个关键字2bytes,一个关键字具体信息指针2bytes。一棵9阶B-tree(一个结点最多8个关键字)的内部结点需要2个盘快。而****B+

****树内部结点只需要1个盘快。当需要把内部结点读入内存中的时候,B 树就比****B+ ****树多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。

B+-tree的查询效率更加稳定

由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

mysql的两种存储引擎的索引存储机制

MyISAM索引实现

MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM索引的原理图:

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第6张图片

这里设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示:

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第7张图片

同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。

MyISAM的索引方式也叫做“非聚集”的。

InnoDB索引实现

虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。

第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第8张图片

上图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。

第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。例如,定义在Col3上的一个辅助索引:

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第9张图片

这里以英文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。

最全的MySQL大表优化方案

当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化:

单表优化

除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑、部署、运维的各种复杂度,一般以整型值为主的表在千万级以下,字符串为主的表在五百万以下是没有太大问题的。而事实上很多时候MySQL单表的性能依然有不少优化空间,甚至能正常支撑千万级以上的数据量:

字段

尽量使用TINYINT、SMALLINT、MEDIUM_INT作为整数类型而非INT,如果非负则加上UNSIGNED

VARCHAR的长度只分配真正需要的空间

使用枚举或整数代替字符串类型

尽量使用TIMESTAMP而非DATETIME,

单表不要有太多字段,建议在20以内

避免使用NULL字段,很难查询优化且占用额外索引空间

用整型来存IP

索引

索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE和ORDER

BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描

应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描

值分布很稀少的字段不适合建索引,例如”性别”这种只有两三个值的字段

字符字段只建前缀索引

字符字段最好不要做主键

不用外键,由程序保证约束

尽量不用UNIQUE,由程序保证约束

使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引

查询SQL

可通过开启慢查询日志来找出较慢的SQL

不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等等,查询时要尽可能将操作移至等号右边 sql语句尽可能简单:一条sql只能在一个cpu运算;大语句拆小语句,减少锁时间;一条大sql可以堵死整个库 不用SELECT *

OR改写成IN:OR的效率是n级别,IN的效率是log(n)级别,in的个数建议控制在200以内

不用函数和触发器,在应用程序实现

避免%xxx式查询

少用JOIN

使用同类型进行比较,比如用’123’和’123’比,123和123比

尽量避免在WHERE子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描

对于连续数值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5

列表数据不要拿全表,要使用LIMIT来分页,每页数量也不要太大

引擎

目前广泛使用的是MyISAM和InnoDB两种引擎:

MyISAM

MyISAM引擎是MySQL 5.1及之前版本的默认引擎,它的特点是:

不支持行锁,读取时对需要读到的所有表加锁,写入时则对表加排它锁

不支持事务

不支持外键

不支持崩溃后的安全恢复

在表有读取查询的同时,支持往表中插入新纪录

支持BLOB和TEXT的前500个字符索引,支持全文索引

支持延迟更新索引,极大提升写入性能

对于不会进行修改的表,支持压缩表,极大减少磁盘空间占用

InnoDB

InnoDB在MySQL 5.5后成为默认索引,它的特点是:

支持行锁,采用MVCC来支持高并发

支持事务

支持外键

支持崩溃后的安全恢复

不支持全文索引

总体来讲,MyISAM适合SELECT密集型的表,而InnoDB适合INSERT和UPDATE密集型的表

系统调优参数

可以使用下面几个工具来做基准测试:

sysbench:一个模块化,跨平台以及多线程的性能测试工具

iibench-mysql:基于 Java 的 MySQL/Percona/MariaDB 索引进行插入性能测试工具

tpcc-mysql:Percona开发的TPC-C测试工具

具体的调优参数内容较多,具体可参考官方文档,这里介绍一些比较重要的参数:

back_log:back_log值指出在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。也就是说,如果MySql的连接数据达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源。可以从默认的50升至500

wait_timeout:数据库连接闲置时间,闲置连接会占用内存资源。可以从默认的8小时减到半小时

max_user_connection: 最大连接数,默认为0无上限,最好设一个合理上限

thread_concurrency:并发线程数,设为CPU核数的两倍

skip_name_resolve:禁止对外部连接进行DNS解析,消除DNS解析时间,但需要所有远程主机用IP访问

key_buffer_size:索引块的缓存大小,增加会提升索引处理速度,对MyISAM表性能影响最大。对于内存4G左右,可设为256M或384M,通过查询show status like ‘key_read%’,保证key_reads / key_read_requests在0.1%以下最好

innodb_buffer_pool_size:缓存数据块和索引块,对InnoDB表性能影响最大。通过查询show status like ‘Innodb_buffer_pool_read%’,保证 (
Innodb_buffer_pool_read_requests – Innodb_buffer_pool_reads) / Innodb_buffer_pool_read_requests越高越好


innodb_additional_mem_pool_size:InnoDB存储引擎用来存放数据字典信息以及一些内部数据结构的内存空间大小,当数据库对象非常多的时候,适当调整该参数的大小以确保所有数据都能存放在内存中提高访问效率,当过小的时候,MySQL会记录Warning信息到数据库的错误日志中,这时就需要该调整这个参数大小

innodb_log_buffer_size:InnoDB存储引擎的事务日志所使用的缓冲区,一般来说不建议超过32MB

query_cache_size:缓存MySQL中的ResultSet,也就是一条SQL语句执行的结果集,所以仅仅只能针对select语句。当某个表的数据有任何任何变化,都会导致所有引用了该表的select语句在Query Cache中的缓存数据失效。所以,当我们的数据变化非常频繁的情况下,使用Query Cache可能会得不偿失。根据命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))进行调整,一般不建议太大,256MB可能已经差不多了,大型的配置型静态数据可适当调大.

可以通过命令show status like ‘Qcache_%’查看目前系统Query catch使用大小

read_buffer_size:MySql读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySql会为它分配一段内存缓冲区。如果对表的顺序扫描请求非常频繁,可以通过增加该变量值以及内存缓冲区大小提高其性能

sort_buffer_size:MySql执行排序使用的缓冲大小。如果想要增加ORDER BY的速度,首先看是否可以让MySQL使用索引而不是额外的排序阶段。如果不能,可以尝试增加sort_buffer_size变量的大小

read_rnd_buffer_size:MySql的随机读缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySql会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但MySql会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。

record_buffer:每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,可能想要增加该值

thread_cache_size:保存当前没有与连接关联但是准备为后面新的连接服务的线程,可以快速响应连接的线程请求而无需创建新的

table_cache:类似于thread_cache_size,但用来缓存表文件,对InnoDB效果不大,主要用于MyISAM

升级硬件

Scale up,这个不多说了,根据MySQL是CPU密集型还是I/O密集型,通过提升CPU和内存、使用SSD,都能显著提升MySQL性能

读写分离

也是目前常用的优化,从库读主库写,一般不要采用双主或多主引入很多复杂性,尽量采用文中的其他方案来提高性能。同时目前很多拆分的解决方案同时也兼顾考虑了读写分离

缓存

缓存可以发生在这些层次:

MySQL内部:在系统调优参数介绍了相关设置

数据访问层:比如MyBatis针对SQL语句做缓存,而Hibernate可以精确到单个记录,这里缓存的对象主要是持久化对象Persistence Object

应用服务层:这里可以通过编程手段对缓存做到更精准的控制和更多的实现策略,这里缓存的对象是数据传输对象Data Transfer Object

Web层:针对web页面做缓存

浏览器客户端:用户端的缓存

可以根据实际情况在一个层次或多个层次结合加入缓存。这里重点介绍下服务层的缓存实现,目前主要有两种方式:

直写式(Write Through):在数据写入数据库后,同时更新缓存,维持数据库与缓存的一致性。这也是当前大多数应用缓存框架如Spring Cache的工作方式。这种实现非常简单,同步好,但效率一般。

回写式(Write Back):当有数据要写入数据库时,只会更新缓存,然后异步批量的将缓存数据同步到数据库上。这种实现比较复杂,需要较多的应用逻辑,同时可能会产生数据库与缓存的不同步,但效率非常高。

表分区

MySQL在5.1版引入的分区是一种简单的水平拆分,用户需要在建表的时候加上分区参数,对应用是透明的无需修改代码

对用户来说,分区表是一个独立的逻辑表,但是底层由多个物理子表组成,实现分区的代码实际上是通过对一组底层表的对象封装,但对SQL层来说是一个完全封装底层的黑盒子。MySQL实现分区的方式也意味着索引也是按照分区的子表定义,没有全局索引

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第10张图片

mysql> explain partitions select count(1) from user_partition where id
in (1,2,3,4,5);
+----+-------------+--

| id | select_type | table | partitions | type |
possible_keys | key | key_len | ref | rows |
Extra |
+----+------------------

| 1 | SIMPLE | user_partition | p1,p4 | range | PRIMARY | PRIMARY | 8 |
NULL | 5 | Using where;
Using index |
+----+-------------------

分区的好处是:

可以让单表存储更多的数据

分区表的数据更容易维护,可以通过清楚整个分区批量删除大量数据,也可以增加新的分区来支持新插入的数据。另外,还可以对一个独立分区进行优化、检查、修复等操作

部分查询能够从查询条件确定只落在少数分区上,速度会很快

分区表的数据还可以分布在不同的物理设备上,从而搞笑利用多个硬件设备

可以使用分区表赖避免某些特殊瓶颈,例如InnoDB单个索引的互斥访问、ext3文件系统的inode锁竞争

可以备份和恢复单个分区

分区的限制和缺点:

一个表最多只能有1024个分区

如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须包含进来

分区表无法使用外键约束

NULL值会使分区过滤无效

所有分区必须使用相同的存储引擎

分区的类型:

RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区

LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择

HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式

KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值

分区适合的场景有:

最适合的场景数据的时间序列性比较强,则可以按时间来分区,如下所示:

CREATE TABLE members (
firstname VARCHAR(25)
NOT NULL, lastname VARCHAR(25)
NOT NULL, username VARCHAR(16)
NOT NULL, email VARCHAR(35),
joined DATE NOT NULL )
PARTITION BY RANGE
( YEAR(joined) ) (
PARTITION p0 VALUES LESS THAN (1960),
PARTITION p1 VALUES LESS THAN (1970),
PARTITION p2 VALUES LESS THAN (1980),
PARTITION p3 VALUES LESS THAN (1990),
PARTITION p4 VALUES LESS THAN MAXVALUE
);

查询时加上时间范围条件效率会非常高,同时对于不需要的历史数据能很容的批量删除。

如果数据有明显的热点,而且除了这部分数据,其他数据很少被访问到,那么可以将热点数据单独放在一个分区,让这个分区的数据能够有机会都缓存在内存中,查询时只访问一个很小的分区表,能够有效使用索引和缓存 另外MySQL有一种早期的简单的分区实现 - 合并表(merge table),限制较多且缺乏优化,不建议使用,应该用新的分区机制来替代

垂直拆分

垂直分库是根据数据库里面的数据表的相关性进行拆分,比如:一个数据库里面既存在用户数据,又存在订单数据,那么垂直拆分可以把用户数据放到用户库、把订单数据放到订单库。垂直分表是对数据表进行垂直拆分的一种方式,常见的是把一个多字段的大表按常用字段和非常用字段进行拆分,每个表里面的数据记录数一般情况下是相同的,只是字段不一样,使用主键关联

比如原始的用户表是:

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第11张图片

垂直拆分后是:

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第12张图片

垂直拆分的优点是:

可以使得行数据变小,一个数据块(Block)就能存放更多的数据,在查询时就会减少I/O次数(每次查询时读取的Block 就少)

可以达到最大化利用Cache的目的,具体在垂直拆分的时候可以将不常变的字段放一起,将经常改变的放一起

数据维护简单

缺点是:

主键出现冗余,需要管理冗余列

会引起表连接JOIN操作(增加CPU开销)可以通过在业务服务器上进行join来减少数据库压力

依然存在单表数据量过大的问题(需要水平拆分)

事务处理复杂

水平拆分

概述

水平拆分是通过某种策略将数据分片来存储,分库内分表和分库两部分,每片数据会分散到不同的MySQL表或库,达到分布式的效果,能够支持非常大的数据量。前面的表分区本质上也是一种特殊的库内分表

库内分表,仅仅是单纯的解决了单一表数据过大的问题,由于没有把表的数据分布到不同的机器上,因此对于减轻MySQL服务器的压力来说,并没有太大的作用,大家还是竞争同一个物理机上的IO、CPU、网络,这个就要通过分库来解决

前面垂直拆分的用户表如果进行水平拆分,结果是:

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第13张图片

实际情况中往往会是垂直拆分和水平拆分的结合,即将Users_A_M和Users_N_Z再拆成Users和UserExtras,这样一共四张表

水平拆分的优点是:

不存在单库大数据和高并发的性能瓶颈

应用端改造较少

提高了系统的稳定性和负载能力

缺点是:

分片事务一致性难以解决

跨节点Join性能差,逻辑复杂

数据多次扩展难度跟维护量极大

分片原则

能不分就不分,参考单表优化

分片数量尽量少,分片尽量均匀分布在多个数据结点上,因为一个查询SQL跨分片越多,则总体性能越差,虽然要好于所有数据在一个分片的结果,只在必要的时候进行扩容,增加分片数量

分片规则需要慎重选择做好提前规划,分片规则的选择,需要考虑数据的增长模式,数据的访问模式,分片关联性问题,以及分片扩容问题,最近的分片策略为范围分片,枚举分片,一致性Hash分片,这几种分片都有利于扩容

尽量不要在一个事务中的SQL跨越多个分片,分布式事务一直是个不好处理的问题

查询条件尽量优化,尽量避免Select * 的方式,大量数据结果集下,会消耗大量带宽和CPU资源,查询尽量避免返回大量结果集,并且尽量为频繁使用的查询语句建立索引。

通过数据冗余和表分区赖降低跨库Join的可能

这里特别强调一下分片规则的选择问题,如果某个表的数据有明显的时间特征,比如订单、交易记录等,则他们通常比较合适用时间范围分片,因为具有时效性的数据,我们往往关注其近期的数据,查询条件中往往带有时间字段进行过滤,比较好的方案是,当前活跃的数据,采用跨度比较短的时间段进行分片,而历史性的数据,则采用比较长的跨度存储。

总体上来说,分片的选择是取决于最频繁的查询SQL的条件,因为不带任何Where语句的查询SQL,会遍历所有的分片,性能相对最差,因此这种SQL越多,对系统的影响越大,所以我们要尽量避免这种SQL的产生。

解决方案

由于水平拆分牵涉的逻辑比较复杂,当前也有了不少比较成熟的解决方案。这些方案分为两大类:客户端架构和代理架构。

客户端架构

通过修改数据访问层,如JDBC、Data Source、MyBatis,通过配置来管理多个数据源,直连数据库,并在模块内完成数据的分片整合,一般以Jar包的方式呈现

这是一个客户端架构的例子:

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第14张图片

可以看到分片的实现是和应用服务器在一起的,通过修改Spring JDBC层来实现

客户端架构的优点是:

应用直连数据库,降低外围系统依赖所带来的宕机风险

集成成本低,无需额外运维的组件

缺点是:

限于只能在数据库访问层上做文章,扩展性一般,对于比较复杂的系统可能会力不从心

将分片逻辑的压力放在应用服务器上,造成额外风险

代理架构

通过独立的中间件来统一管理所有数据源和数据分片整合,后端数据库集群对前端应用程序透明,需要独立部署和运维代理组件

这是一个代理架构的例子:

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第15张图片

代理组件为了分流和防止单点,一般以集群形式存在,同时可能需要Zookeeper之类的服务组件来管理

代理架构的优点是:

能够处理非常复杂的需求,不受数据库访问层原来实现的限制,扩展性强

对于应用服务器透明且没有增加任何额外负载

缺点是:

需部署和运维独立的代理中间件,成本高

应用需经过代理来连接数据库,网络上多了一跳,性能有损失且有额外风险

各方案比较

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第16张图片

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第17张图片

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第18张图片

MySQL面试题及答案

1.数据库三范式是什么?

1.第一范式(1NF):字段具有原子性,不可再分。(所有关系型数据库系统都满足第一范式数据库表中的字段都是单一属性的,不可再分)

2.第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。要求数据库表中的每个实例或行必须可以被惟一地区分。通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键。

3.满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。>所以第三范式具有如下特征:>>1.每一列只有一个值>>2.每一行都能区分。>>3.每一个表都不包含其他表已经包含的非主关键字信息。
2.有哪些数据库优化方面的经验?

1.用PreparedStatement,一般来说比Statement性能高:一个sql发给服务器去执行,涉及步骤:语法检查、语义分析,编译,缓存。

2.有外键约束会影响插入和删除性能,如果程序能够保证数据的完整性,那在设计数据库时就去掉外键。

3.表中允许适当冗余,譬如,主题帖的回复数量和最后回复时间等4.UNIONALL要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复数据且不需要排序时的话,那么就使用UNIONALL。>>UNION和UNIONALL关键字都是将两个结果集合并为一个,但这两者从使用和效率上来说都有所不同。>1.对重复结果的处理:UNION在进行表链接后会筛选掉重复的记录,UnionAll不会去除重复记录。>2.对排序的处理:Union将会按照字段的顺序进行排序;UNIONALL只是简单的将两个结果合并后就返回。

3.请简述常用的索引有哪些种类?

1.普通索引:即针对数据库表创建索引

2.唯一索引:与普通索引类似,不同的就是:MySQL数据库索引列的值必须唯一,但允许有空值

3.主键索引:它是一种特殊的唯一索引,不允许有空值。一般是在建表的时候同时创建主键索引

4.组合索引:为了进一步榨取MySQL的效率,就要考虑建立组合索引。即将数据库表中的多个字段联合起来作为一个组合索引。

4.以及在mysql数据库中索引的工作机制是什么?

数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树

5.MySQL的基础操作命令:

1.MySQL是否处于运行状态:Debian上运行命令servicemysqlstatus,在RedHat上运行命令servicemysqldstatus

2.开启或停止MySQL服务:运行命令servicemysqldstart开启服务;运行命令servicemysqldstop停止服务

3.Shell登入MySQL:运行命令mysql-uroot-p

4.列出所有数据库:运行命令showdatabases;

5.切换到某个数据库并在上面工作:运行命令usedatabasename;进入名为databasename的数据库6.列出某个数据库内所有表:showtables;7.获取表内所有Field对象的名称和类型:describetable_name;
6.mysql的复制原理以及流程。

Mysql内建的复制功能是构建大型,高性能应用程序的基础。将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台主机的数据复制到其它主机(slaves)上,并重新执行一遍来实现的。*复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。当一个从服务器连接主服务器时,它通知主服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,然后封锁并等待主服务器通知新的更新。过程如下1.主服务器把更新记录到二进制日志文件中。2.从服务器把主服务器的二进制日志拷贝到自己的中继日志(replaylog)中。3.从服务器重做中继日志中的时间,把更新应用到自己的数据库上。

7.mysql支持的复制类型?
1.基于语句的复制:在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高。一旦发现没法精确复制时,会自动选着基于行的复制。

2.基于行的复制:把改变的内容复制过去,而不是把命令在从服务器上执行一遍.从mysql5.0开始支持

3.混合类型的复制:默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。
8.mysql中myisam与innodb的区别?
1.事务支持>MyISAM:强调的是性能,每次查询具有原子性,其执行数度比InnoDB类型更快,但是不提供事务支持。>InnoDB:提供事务支持事务,外部键等高级数据库功能。具有事务(commit)、回滚(rollback)和崩溃修复能力(crashrecoverycapabilities)的事务安全(transaction-safe(ACIDcompliant))型表。

2.InnoDB支持行级锁,而MyISAM支持表级锁.>>用户在操作myisam表时,select,update,delete,insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。

3.InnoDB支持MVCC,而MyISAM不支持

4.InnoDB支持外键,而MyISAM不支持

5.表主键>MyISAM:允许没有任何索引和主键的表存在,索引都是保存行的地址。>InnoDB:如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值。

6.InnoDB不支持全文索引,而MyISAM支持。

7.可移植性、备份及恢复>MyISAM:数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作。>InnoDB:免费的方案可以是拷贝数据文件、备份binlog,或者用mysqldump,在数据量达到几十G的时候就相对痛苦了

8.存储结构>MyISAM:每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型。.frm文件存储表定义。数据文件的扩展名为.MYD(MYData)。索引文件的扩展名是.MYI(MYIndex)。>InnoDB:所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB。

9.mysql中varchar与char的区别以及varchar(50)中的50代表的涵义?

1.varchar与char的区别:char是一种固定长度的类型,varchar则是一种可变长度的类型.

2.varchar(50)中50的涵义:最多存放50个字节

3.int(20)中20的涵义:int(M)中的

Mindicatesthemaximumdisplaywidth(最大显示宽度)forintegertypes.Themaximumlegaldisplaywidthis255.

10.MySQL中InnoDB支持的四种事务隔离级别名称,以及逐级之间的区别?
1.ReadUncommitted(读取未提交内容)>>在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(DirtyRead)。

2.ReadCommitted(读取提交内容)>>这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(NonrepeatableRead),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

3.RepeatableRead(可重读)>>这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(PhantomRead)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影”行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,

MultiversionConcurrencyControl间隙锁)机制解决了该问题。注:其实多版本只是解决不可重复读问题,而加上间隙锁(也就是它这里所谓的并发控制)才解决了幻读问题。

4.Serializable(可串行化)>>这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

隔离级别脏读(DirtyRead)不可重复读(NonRepeatableRead)幻读(PhantomRead)未提交读(Readuncommitted)可能可能可能已提交读(Readcommitted)不可能可能可能可重复读(Repeatableread)不可能不可能可能可串行化(SERIALIZABLE)不可能不可能不可能

11.表中有大字段X(例如:text类型),且字段X不会经常更新,以读为为主,将该字段拆成子表好处是什么?
12.MySQL中InnoDB引擎的行锁是通过加在什么上完成(或称实现)的?
13.MySQL中控制内存分配的全局参数,有哪些?

14.若一张表中只有一个字段VARCHAR(N)类型,utf8编码,则N最大值为多少(精确到数量级即可)?
15.[SELECT*]和[SELECT全部字段]的2种写法有何优缺点?
16.HAVNG子句和WHERE的异同点?
17.MySQL当记录不存在时insert,当记录存在时update,语句怎么写?
18.MySQL的insert和update的select语句语法

MySQL性能优化面试题及答案

1、一张表,里面有ID自增主键,当insert了17条记录之后,删除了第15,16,17条记录,再把Mysql重启,再insert一条记录,这条记录的ID是18还是15?

(1)如果表的类型是MyISAM,那么是18因为MyISAM表会把自增主键的最大ID记录到数据文件里,重启MySQL自增主键的最大ID也不会丢失

(2)如果表的类型是InnoDB,那么是15InnoDB表只是把自增主键的最大ID记录到内存中,所以重启数据库或者是对表进行OPTIMIZE操作,都会导致最大ID丢失

2、Mysql的技术特点是什么?

Mysql数据库软件是一个客户端或服务器系统,其中包括:支持各种客户端程序和库的多线程SQL服务器、不同的后端、广泛的应用程序编程接口和管理工具。
3、Heap表是什么?
HEAP表存在于内存中,用于临时高速存储。

BLOB或TEXT字段是不允许的

只能使用比较运算符=,<,>,=>,=<

HEAP表不支持AUTO_INCREMENT

索引不可为NULL
4、Mysql服务器默认端口是什么?

Mysql服务器的默认端口是3306。

5、与Oracle相比,Mysql有什么优势
Mysql是开源软件,随时可用,无需付费。

Mysql是便携式的带有命令提示符的GUI。

使用Mysql查询浏览器支持管理
6、如何区分FLOAT和DOUBLE?
以下是FLOAT和DOUBLE的区别:
浮点数以8位精度存储在FLOAT中,并且有四个字节。

浮点数存储在DOUBLE中,精度为18位,有八个字节。
7、区分CHAR_LENGTH和LENGTH?
CHAR_LENGTH是字符数,而LENGTH是字节数。Latin字符的这两个数据是相同的,但是对于Unicode和其他编码,它们是不同的。
8、请简洁描述Mysql中InnoDB支持的四种事务隔离级别名称,以及逐级之间的区别?

SQL标准定义的四个隔离级别为:
readuncommited:读到未提交数据

readcommitted:脏读,不可重复读

repeatableread:可重读

serializable:串行事物
9、在Mysql中ENUM的用法是什么?
ENUM是一个字符串对象,用于指定一组预定义的值,并可在创建表时使用。Createtablesize(nameENUM('Smail,'Medium','Large');
10、如何定义REGEXP?

REGEXP是模式匹配,其中匹配模式在搜索值的任何位置。
11、CHAR和VARCHAR的区别?
12、列的字符串类型可以是什么?

13、如何获取当前的Mysql版本?
14、Mysql中使用什么存储引擎?

15、Mysql驱动程序是什么?
16、TIMESTAMP在UPDATECURRENT_TIMESTAMP数据类型上做什么?

17、主键和候选键有什么区别?
18、如何使用Unixshell登录Mysql?

19、myisamchk是用来做什么的?
20、MYSQL数据库服务器性能分析的方法命令有哪些?

21、如何控制HEAP表的最大尺寸?
22、MyISAMStatic和MyISAMDynamic有什么区别?
23、federated表是什么?

24、如果一个表有一列定义为TIMESTAMP,将发生什么?
25、列设置为AUTOINCREMENT时,如果在表中达到最大值,会发生什么情况?
26、怎样才能找出最后一次插入时分配了哪个自动增量?
27、你怎么看到为表格定义的所有索引?
28.、LIKE声明中的%和_是什么意思?
29、如何在Unix和Mysql时间戳之间进行转换?
30、列对比运算符是什么?

31、我们如何得到受查询影响的行数?

32、Mysql查询是否区分大小写?
33.、LIKE和REGEXP操作有什么区别?

34.、BLOB和TEXT有什么区别?
35、mysql_fetch_array和mysql_fetch_object的区别是什么?
36、我们如何在mysql中运行批处理模式?
37、MyISAM表格将在哪里存储,并且还提供其存储格式?
38.、Mysql中有哪些不同的表格?
39、ISAM是什么?
40、InnoDB是什么?
41、Mysql如何优化DISTINCT?
42、如何输入字符为十六进制数字?
43、如何显示前50行?

44、可以使用多少列创建索引?

45、NOW()和CURRENT_DATE()有什么区别?
46、什么样的对象可以使用CREATE语句创建?
47、Mysql表中允许有多少个TRIGGERS?
48、什么是非标准字符串类型?
49、什么是通用SQL函数?
50、解释访问控制列表
51、MYSQL支持事务吗?

52、mysql里记录货币用什么字段类型好
53、MYSQL数据表在什么情况下容易损坏?
54、mysql有关权限的表都有哪几个?
55、Mysql中有哪几种锁?

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第19张图片

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第20张图片

更多java笔记资料,BAT大厂面试专题及答案,源码分析,分布式专题,并发编程,微服务架构,性能优化,数据结构与算法,后端开发,企业级落地实战等java笔记资料,直接后台小信封扣【999】撩我领取吧!

最全最新分享:mysql索引的数据结构。含mysql面试专题及答案_第21张图片

你可能感兴趣的:(架构,java,后端,java,面试,程序人生,分布式,数据结构)