目录
二十六. 关心过业务系统里面的 sql 耗时吗?统计过慢查询吗?对慢查询都怎么优化过?
三十. 简述 mysql 中索引类型以及对数据库的性能的影响
三十二. mysql 主从复制集群如何搭建?读写分离是怎么样做的?
在学习的过程中最好是参考 mysql 的官方文档,在 document 下点击最新的 mysql 官方文档,在搜索框中搜索想要查询的数据;
一. 索引的基本概述
索引是什么?索引本质上是一种排好序的数据结构,需要消耗一定的磁盘空间,利用了空间换时间的思想;
1. 主键索引
InnoDb 中主键索引也叫作聚簇索引,主键会默认加上索引,主键索引在创建表的时候自动创建,主键索引不允许为空:
# 创建一张表
create table t_user(id varchar (20) primary key, name varchar(20));
# 查看一张表中存在哪些索引
show index from t_user
2. 单列索引
也叫单值索引,普通索引,单列索引有两种创建方式:① 建表的时候创建;② 建表之后创建,例如我们有 t_user 表,想给 t_user 表的 name 属性添加单列索引:
# 建表的时候创建单列索引, key()表示给哪个属性添加索引, 但是这种方式在无法指定索引的名字
create table t_user(id varchar(20) primary key, name varchar(20), key(name))
# 建表之后创建单列索引, 其中index_name 表示索引的名字, t_user 表示哪一张表, name表示表中的哪个属性
create index name_index on t_user(name)
建表时创建单列索引:
建表后创建单列索引:
3. 唯一索引
存在两种方式创建唯一索引,在建表的时候创建或者是建表之后创建:
# 建表的时候创建
create table t_user(id varchar(20) primary key, name varchar(20), unique(name))
# 建表之后创建
create unique index name_index on t_user(name)
4. 复合索引
复合索引需要匹配多个多个列共同构建一个索引,也存在两种创建方式,可以在创建表的时候创建也可以在创建表之后创建:
# 建表的时候创建
create table t_user(id varchar(20) primary key, name varchar(20), age int, key(name, age))
# 建表之后创建
create index name_index on t_user(name, age)
这里需要注意一个面试题,例如当前我是根据 name,age,bir 三个字段创建的复合索引,判断下面的字段是否可以利用索引:
- name bir age 能否利用索引;可以
- name age bir 能否利用索引;可以
- age bir 能否利用索引;不可以,没有包括左前缀
- bir age name 能否利用索引;可以
- age bir 能否利用索引;不可以,没有包括左前缀
① 如果当前是复合索引那么需要遵循最左前缀原则才能够利用索引,所以字段为 (a,b,c) 的时候只能够通过 (a),(a,b),(a,b,c) 来查询才可以利用复合索引;
② mysql 引擎在查询的时候为了更好地利用索引,在查询的过程中会动态调整查询字段顺序以便利用索引;
二. 索引底层原理解析之 B+ 树
先思考一个问题,现在有一张表 t_emp,插入几条数据,我们可以先查询一下:select * from t_emp,可以发现插入的数据是无序的但是查询的结果大概是有序的,为什么它要对 id 进行排序呢?原因是主键 id 默认会创建主键索引,主键索引进行了排序,排序之后从前往后找的时候就可以比较快的查询出结果,如果没有排序那么就需要整个表中进行查询那么查询的速度就会比较慢,但是怎么样做排序的呢?主键索引在我们每一次插入的时候会对我们的数据做一个排序并且会对我们排序之后的最后一个把所有的数据链接起来,但是 mysql 优化到这里并不是最优的,因为表中的数据可能成千上万,所以对于链表来说查询的时间复杂度就是O(n),mysql 对此做了一个优化把这些底层排好序的数据基于页来管理,mysql 中每一页默认存储大小为 16 KB,并且 mysql 又做了一层页目录进行管理,页目录存储每一页的第一个索引的id和对应的指针,(页目录其实也是一种数据结构),通过指针找到对应的是哪一页;进行页目录管理之后首先是从页目录中去找,页目录大小也是 16KB;例如上面我们创建的三个字段,id,age各占 4 个字节,varchar 20 个字节,指针4~8个字节,这里算为 8 个字节,那么总共是 36 个字节,也即36B,而一页 16KB,则 16 * 1024 / 36 = 455 条数据,页目录可以放 16 KB * 1024 / 12 = 1365条,所以两阶的 B+ 树可以存储 455 * 1365 = 621075,对于大部分情况下两层 B+ 树就可以实现存储大部分数据了;
create table t_emp(id int primary key, name varchar(20), age int)
# 插入数据
insert into t_emp values(5, 'd', 22);
insert into t_emp values(6, 'd', 22);
insert into t_emp values(7, 'e', 21);
insert into t_emp values(1, 'a', 23);
insert into t_emp values(2, 'b', 26);
insert into t_emp values(3, 'c', 27);
insert into t_emp values(4, 'a', 32);
insert into t_emp values(8, 'f', 53);
insert into t_emp values(9, 'v', 13);
# 查看当前t_emp中的索引
show index from t_emp
select * from t_emp
# 使用explain 语句查看是否使用了索引
explain select * from t_emp
优化:
B+ 树最大的好处是只有叶子节点存储数据,非叶子节点不存储数据,B 树的节点必须存数据,所以在存储页数据的时候势必需要消耗更多的空间来存储,所以同样的两级存储的数据会多一点,mysql 为了加快查询在 Innodb 设置的时候,顶层页是常驻内存的,也就是说查找某一个键值的行记录的时候最多只需要 1~3 次 IO 操作;叶子节点通过双向指针链接所有的节点,这样可以从后往前或者从前往后寻找节点;
三. 聚簇索引与非聚簇索引
1. 聚簇索引
将数据存储与索引放到了一块,索引结构的叶子节点保存了数据;一个表中只能有一个聚簇索引(下面两张图片引用的都是 csdn 博主的图片,下面两张图其实非常清楚地展示出了InnoDb 中聚簇索引与非聚簇索引的数据结构);
2. 非聚簇索引
将数据与索引分来存储,索引结构的叶子节点指向了数据对应的位置,
注意:在聚簇索引上创建的索引称之为辅助索引,非聚簇索引都是辅助索引,像复合索引,前缀索引,唯一索引,InnoDb 辅助索引叶子节点存储的不再是行的物理位置,而是主键值,辅助索引访问数据总是需要二次查找,但是为什么存储的主键 id 值而不是具体的地址呢?其实 mysql 对此是做了优化的,因为在增删改的时候会造成树上的地址发生改变(重排),所以宁愿去使用 id 再去查找一遍所以存储的不是地址;
3. InnoDb
InnoDb 使用的是聚簇索引,将主键组织到一棵 B+ 树中,而行数据就存储在叶子节点中,若使用 "where id = 14" 这样的条件查找主键,则按照 B+ 树的检索算法即可查找对应的叶子节点之后获得行数据;若对 name 按照条件搜索,则需要两个步骤:第一步在辅助索引 B+ 树中检索 name,到达其叶子节点获得对应的主键。第二步使用主键在主索引 B+ 树中再执行一次 B+ 树检索操作,最终达到叶子节点即可获取整行数据(重点在于通过其他键需要建立辅助索引);聚簇索引默认是主键,如果表中没有主键,InnoDb 会选择一个唯一而且非空的索引代替,如果没有这样的索引,InnoDb 会隐式定义一个主键(类似于Oracle 中的 RowId) 来作为聚簇索引,如果已经设置了主键作为聚簇索引而且又希望再单独设置聚簇索引,必须先删除主键,然后添加我们想要的聚簇索引,最后恢复设置主键即可:
当不是按照主键查询的时候,按照辅助索引查询的时候如果创建的辅助索引的 B+ 树叶子节点存储的是数据不仅浪费空间而且后面如果需要修改辅助索引的某个字段值那么受到影响的辅助索引 B+ 树的结构是非常大的所以辅助索引叶子节点一般存储的是主键值,当不是按照 id 值查询的时候那么首先是通过辅助索引找到对应的主键,通过主键查找对应的记录;
4. MYISAM
MYISAM 使用的是非聚簇索引,非聚簇索引的两棵 B+ 树看上去没有什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引 B+ 树的节点存储了主键,辅助索引 B+ 树存储了辅助键,表数据存储在独立的地方,这两棵 B+ 树的叶子节点都是用一个地址指向真正的表数据,对于表数据来说这两个键没有任何差别,由于索引树是独立的,通过辅助键检索无需访问主键的索引树:
5. 使用聚簇索引的优势
1. 每次使用辅助索引检索的时候都需要经过两次 B+ 树查找,看上去聚簇索引的效率明显要低于非聚簇索引,那么聚簇索引的优势在哪里呢?由于行数据和聚簇索引的叶子节点存储在一起,同一页中会有多条行数据,访问同一数据页不同行记录的时候,已经把页加载到了 Buffer 中(缓存器),再次访问的时候会在内存中完成访问不必访问磁盘,这样主键和行数据是一起被载入内存的,找到叶子节点就可以立刻将行数据返回了,如果按照主键 id 来组织数据获得数据更快;
2. 辅助索引的叶子节点,存储主键值,而不是数据的存放地址,好处是当行数据发生变化的时候,索引树的节点也需要分裂变化,或者是我们需要查找的数据,在上一次 IO 读写的缓存中没有需要发生一次新的 IO 操作的时候,可以避免对辅助索引的维护工作,只需要维护聚簇索引树就好了,另外一个好处是因为辅助索引存放的是主键值,减少了辅助索引占用的存储空间大小。
6. 什么情况下无法利用索引?
1. 查询语句中使用 like 关键字
在查询语句中使用 like 关键字查询的时候,如果匹配字符串的第一个字符为 "%",那么索引不会被使用,如果 "%" 不是在第一个位置那么索引会被使用;
2. 查询语句中使用多列索引
多列索引是在表的多个字段上创建一个索引,只有查询条件使用了这些字段的第一个字段,索引才会被使用,也即需要满足最左匹配原则;
3. 查询语句中使用 or 关键字
查询语句使用 or 关键字的时候,如果 or 前后两个条件的列都是索引,那么查询中将使用索引,如果 or 前后有一个条件的列不是那么查询中将不使用索引;
7. 范围查找导致索引失效?
例如:select * from t2 where b > 1;这条sql 语句,使用 explain select * from t2 where b > 1;执行语句之后可以发现查询的时候使用的是全表扫描的方式,其实并不是使用了索引就一定会比没有使用索引块,当 b > 1 的时候那么使用索引的情况下先找到 b = 1 然后通过 b = 1 后面的数据找到 b > 1的 id 值然后查询出满足条件的数据(回表),mysql 在执行的查询的时候会判断哪一种 sql 执行得会比较快一点那么就执行哪一种 sql;例如执行select * from t2 where b > 5;可以发现又走索引了因为通过索引可以直接定位到具体的几条数据;
四. 覆盖索引
覆盖索引:是指一个索引包含或者覆盖了所有需要查询的字段的值,不需要根据索引回表查询数据,也即可以直接将所需要的数据返回即可;例如:explain select b from t2 where b > 1;(参照下面的第二个例子)可以发现又利用索引了,并且显示 Extra 显示的是 using index;因为查询的是字段 b 而字段 b 在叶子节点中存在所以不用回表直接返回索引中的数据即可;这里需要注意一个问题是使用最左匹配原则的时候需要注意是否满足覆盖索引的条件(如果查询的字段在索引中直接可以得到那么是可以使用到索引的虽然此时可能不满足最左匹配原则),可以参照下面的两个例子:
假如现在有一张 t1 表,a 为主键,创建 (b,c,d) 的复合索引,此时其实存在两个索引,一个是默认的主键索引,第二个是 (b,c,d) 复合索引,此时不管使用查询 b,c,d 的哪一个字段都会使用索引(此时是覆盖索引,复合索引中的一部分):
create table t1(a int primary key, b int, c int, d int);
insert into t1(b, c, d) values(1, 1, 1);
insert into t1(b, c, d) values(2, 2, 2);
insert into t1(b, c, d) values(2, 3, 5);
insert into t1(b, c, d) values(3, 1, 1);
insert into t1(b, c, d) values(3, 2, 2);
insert into t1(b, c, d) values(4, 5, 5);
insert into t1(b, c, d) values(6, 4, 4);
insert into t1(b, c, d) values(8, 8, 8);
# 创建(b, c, d)复合索引
create index b_c_d on t1(b, c, d);
# 查看有哪些哪些索引
show index from t1
# explain 语句可以获取sql语句的运行结果, 表的读取顺序, 数据库读取操作的操作类型, 哪些索引可以使用,实际使用了哪些索引, 表之间的引用, 每张表有多少行别优化器查询等信息
explain select * from t1 where b = 1 and d = 2 and c = 3;
explain select * from t1 where b = 1 and c = 3 and d = 2;
explain select * from t1 where d = 2 and c = 2;
explain select * from t1 where c = 1;
explain select * from t1 where a = 1;
我们可以在表 t1 的基础上增加多一列 e,并且 a 仍然是主键,创建 (b,c,d)三列的复合索引,创建表 t2,此时会更好观察是否满足索引的最左匹配原则:
create table t2(a int primary key, b int, c int, d int, e int);
insert into t2(b, c, d, e) values(1, 1, 1, 1);
insert into t2(b, c, d, e) values(2, 2, 2, 2);
insert into t2(b, c, d, e) values(2, 3, 5, 2);
insert into t2(b, c, d, e) values(3, 1, 1, 2);
insert into t2(b, c, d, e) values(3, 2, 2, 5);
insert into t2(b, c, d, e) values(4, 5, 5, 1);
insert into t2(b, c, d, e) values(6, 4, 4, 1);
insert into t2(b, c, d, e) values(8, 8, 8, 5);
create index b_c_d on t2(b, c, d);
show index from t2;
explain select * from t2 where b = 1 and d = 2 and c = 3;
explain select * from t2 where b = 1 and c = 3 and d = 2;
explain select * from t2 where d = 2 and c = 2;
explain select * from t2 where c = 1;
explain select * from t2 where a = 1;
explain select * from t2 where b = 1;
可以发现当增加多一列 e 之后只有当满足最左前缀的时候才可以利用索引,查询的字段并不是在索引中可以全部获取到;
覆盖索引的底层原理:
假如当前执行的 mysql 语句为:explain select b from t2;可以发现也是使用了 b_c_d 索引,没有使用 where 条件但是使用了 b_c_d 索引这是为什么呢?有两种 mysql 语句的执行过程,第一种是直接遍历主键索引的叶子节点,依次遍历每一条数据取出b字段的值即可;第二种是从 b_c_d 索引上去遍历(b_c_d 索引树上存储了 b 字段):主要是考虑性能方面的原因,因为 b_c_d 索引树上存储的是不完整的字段,而主键索引的叶子节点存储的是完整的字段所以 b_c_d 索引树上可能使用更少的页就可以存储所有的节点,所以查询的效果可能更快一点:
五. order by 为什么会导致索引失效
执行 mysql 语句:
explain select * from t2 order by b, c, d;
其中存在两种可能的执行方式:第一种方式是扫描整个表并且对其数据进行排序(此时不用回表);第二种方式由于没有 where 条件所以无法从索引的根部从上往下查询页即无法利用索引,所以也只能够从叶子节点去取数据但是有一个好处是不用对数据进行排序,不需要排序,但是由于是 select * 所以还需要根据主键去回表, 数据量比较小的时候直接全表扫描然后对数据在内存中排序的速度是非常快的,所以当数据量比较小的时候 mysql 排序的时间可以忽略不计所以会认为全表扫描会更快一点:
执行 mysql 语句:
explain select b from t2 order by b, c, d;
此时由于查询的是字段 b 所以不用回表而且不用排序,那么走 b,c,d 索引会更快一点,mysql 认为直接走索引会更快一点:
六. mysql 中的数据类型转换需要注意的?
假设现在有一张表 t3:
create table t3(a int primary key, b int, c int, d int, e varchar(20));
insert into t3(b, c, d, e) values(1, 1, 1, 'a');
insert into t3(b, c, d, e) values(2, 2, 2, 'b');
insert into t3(b, c, d, e) values(2, 3, 5, 'c');
insert into t3(b, c, d, e) values(3, 1, 1, 'd');
insert into t3(b, c, d, e) values(3, 2, 2, 'e');
insert into t3(b, c, d, e) values(4, 5, 5, 'f');
insert into t3(b, c, d, e) values(6, 4, 4, 'g');
insert into t3(b, c, d, e) values(8, 8, 8, 'h');
create index b_c_d on t3(b, c, d);
# 在e这一列创建单列索引
create index idx_e on t3(e);
show index from t3;
我们可以看下面的 mysql 语句:
# 字段a 属于int类型, 字段e属于varchar 类型
explain select * from t3 where a = 1;
explain select * from t3 where e = '1';
# a = '1'先将'1'转为数字1, 然后就可以走索引了(将字符转为数字)
explain select * from t3 where a = '1';
# 前面三个都可以走索引但是第4个不走索引
explain select * from t3 where e = 1;
当类型不一致的时候会发生类型转换,mysql 中会将不是数字的字符统一转为 0,将数字型字符转为对应的数字:
select 'a' = 0
select 'b' = 0
select '123' = 123
a = '1' 其实修改的是值,而 a = '1' ,而 e = 1 修改的是整个 B+ 树中的字段,所以代价是非常大的而且将其修改为数字之后那么可能会破坏掉原来 B+ 树之间的大小关系,所以主要是对字段进行的操作那么就会导致对应的mysql 语句走不了索引,也即索引失效;
七. 存储引擎
InnoDb 存储引擎与 MYISAM 存储引擎区别:1. InnoDb 支持事务的四种隔离级别,MYISAM 不支持任何事务,但是每一次查询都是原子的,这也是mysql 将默认的存储引擎由 MYISAM 变为 InnoDb;2. InnoDb 支持外键,但是 MYISAM 不支持外键;3. InnoDb 是聚集索引,MYISAM 是非聚集索引;4. InnoDb 不保存表的具体行数,执行 select count(*) from table 需要全表扫描,而 MYISAM 使用一个变量保存了整个表的行数,执行上述语句只需要读出变量即可,速度很快;5. InnoDb 最小的锁力度行锁,MYISAM 最小的锁粒度是表锁,一个更新语句会锁住整张表,导致其他查询和更新都会阻塞,因为并发访问受到限制,这也是mysql 将默认的存储引擎从 MYISAM 变为 InnoDb 的一个重要原因;6. 一个 MYISAM 有三个文件:索引文件,表结构文件,数据文件;一个 InnoDb 引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空间,表大小受操作系统文件大小限制,一般为 2G),受操作系统文件大小的限制;
八. 数据表设置的时候表字段该如何选择?
数据库表设计时,字段你会如何选择?
1. 字段类型优先级
整型 > date,time > enum char > varchar > blob,text;
选用字段长度最小、优先使用定长型、数值型字段中避免使用 "ZEROFILL";
time:定长运算快,节省时间,考虑时区,写 sql 不方便;
enum:能约束值的目的,内部用整形来储存,但与 char 联查时,内部要经历串与值的转化;
char:定长,考虑字符集和校对集;
varchar:不定长,要考虑字符集的转换与排序时的校对集,速度慢;
text,blob:无法使用内存临时表(排序操作只能在磁盘上进行);
注意 date,time 的选择可以直接选择使用时间戳,enum("男","女")内部转成数字来储存,多了一个转换的过程,可以使用tinyint代替最好使用 tinyint。
2. 可以选整型就不选字符串
整型是定长的,没有国家/地区之分,没有字符集差异。例如:tinyint 和 char(1) 从空间上看都是一字节,但是 order by 排序 tinyint 快。原因是后者需要考虑字符集与校对集(就是排序优先集) ;
3. 够用就行不要慷慨
大的字段影响内存影响速度。以年龄为例:tinyint unsigned not null;可以储存255岁,足够了,
用 int 浪费3个字节。以 varchar(10),varchar(300) 储存的内容相同,但在表中查询时,varchar(300) 要花用更多内存。
4. 尽量避免使用null
null 不利于索引,也不利于查询,= null 或者 !=null 都查询不到值,只有使用 is null 或者 is not null 才可以,因此可以在创建字段时候使用 "not null default" 的形式。
5. char 与 varchar 选择
char 长度固定,处理速度要比 varchar 快很多,但是相对较浪费存储空间;所以对存储空间要求不
大,但在速度上有要求的可以使用 char 类型,反之可以用 varchar 类型。
九. varchar 最多能够存储多少数据?
对于 varchar(M) 类型的列最多可以定义 65535 个字节,其中的 M 代表该类型最多存储的字符数
量,但在实际存储时并不能放这么多。mysql 对一条记录占用的最大存储空间是有限制的,除了blob 或者 text 类型的列之外,其他所有的列(不包括隐藏列和记录头信息)占用的字节长度加起来不能超过 65535 个字节。
十. 事务的基本特性
事务应该具有 4 个属性:原子性、一致性、隔离性、持久性,这四个属性通常称为 ACID 特性。
原子性指的是一个事务中的操作要么全部成功,要么全部失败;
一致性指的是数据库总是从一个一致性的状态转换到另外一个一致性的状态。比如 A 转账给 B 100块钱,假设中间 sql 执行过程中系统崩溃 A 也不会损失100 块,因为事务没有提交,修改也就不会保存到数据库;
隔离性指的是一个事务的修改在最终提交前,对其他事务是不可见的;
持久性指的是一旦事务提交,所做的修改就会永久保存到数据库中;
十一. 事务并发可能引起什么问题?
脏读
1. 在事务 A 执行过程中,事务 A 对数据资源进行了修改,事务 B 读取了事务 A 修改后的数据。
2. 由于某些原因,事务 A 并没有完成提交,发生了 Roll Back 操作,则事务 B 读取的数据就是脏数据,这种读取到另一个事务未提交的数据的现象就是脏读 (Dirty Read)。
不可重复读
事务 B 读取了两次数据资源,在这两次读取的过程中事务 A 修改了数据,导致事务 B 在这两次读取出来的数据不一致。这种在同一个事务中,前后两次读取的数据不一致的现象就是不可重复读。
幻读
事务 B 前后两次读取同一个范围的数据,在事务 B 两次读取的过程中事务 A 新增了数据,导致事务 B 后一次读取到前一次查询没有看到的行。幻读和不可重复读有些类似,但是幻读强调的是集合的增减,而不是单条数据的更新;
不可重复读和幻读的区别:
不可重复读:在一个事务内,多次读同一个数据。在这个事务还没有结束时,另 一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。
幻读:是指事务 A 读取与搜索条件相匹配的若干行,事务 B 以插入或删除行等方式来修改事务 A 的结果集,然后再提交。
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的 "全部数据行",同时,第二个事务也修改这个表中的数据,这种修改是向表中插入"一行新数据",那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样,一般解决幻读的方法是增加范围锁 RangeS,锁定检锁范围为只读,这样就避免了幻读。简单来说,幻读是由插入或者删除引起的。
十二. 什么是三星索引?
对于一个查询而言,一个三星索引,可能是其最好的索引。如果查询使用三星索引,一次查询通常只需要进行一次磁盘随机读以及一次窄索引片的扫描,因此其响应时间通常比使用一个普通索引的响应时间少几个数量级;一个查询相关的索引行是相邻的或者至少相距足够靠近则获得一星;如果索引中的数据顺序和查找中的排列顺序一致则获得二星;如果索引中的列包含了查询中需要的全部列则获得三星。三星索引在实际的业务中如果无法同时达到,一般我们认为第三颗星最重要,第一和第二颗星重要性差不多,根据业务情况调整这两颗星的优先度。
十三. InnoDb 一棵B+树可以存放多少行的数据?
InnDb 的所有数据文件(后缀为 ibd 的文件),他的大小始终都是16384(16k)的整数倍;数据表中的数据都是存储在页中的,所以一个页中能存储多少行数据呢?假设一行数据的大小是 1 KB,那么一个页可以存放 16 行这样的数据。对于 B+ 树而言,只有叶子节点存放数据,非叶子节点存放的是只保存索引信息和下一层节点的指针信息。一个非叶子节点能存放多少指针?其实这也很好算,我们假设主键ID为常用的 bigint 类型,长度为 8 字节,而指针大小在 InnoDB 源码中设置为6字节,这样一共 14 字节,我们一个页中能存放多少这样的单元,其实就代表有多少指针,即16384 / 14 = 1170 个。那么可以算出一棵高度为 2 的 B+ 树,存在一个根节点和若干个叶子节点能存放 1170 * 16 = 18720 条这样的数据记录。根据同样的原理我们可以算出一个高度为 3 的 B+ 树可以存放:这样的记录。1170 * 1170 * 16 = 21902400 条这样的记录;
十四. 如何提高insert 语句的性能呢?
1. 合并多条 insert 为一条即:insert into t values(a,b,c),(d,e,f) ,,,
原因分析:主要原因是多条 insert 合并后日志量 (mysql 的 binlog 和 innodb的事务日志) 减少了,降低日志刷盘的数据量和频率,从而提高效率。通过合并 sql 语句,同时也能减少 sql 语句解析的次数,减少网络传输的 IO 。
2. 修改参数 bulk_insert_buffer_size,调大批量插入的缓存;
3. 设置 innodb_flush_log_at_trx_commit = 0
相对于 innodb_flush_log_at_trx_commit = 1 可以十分明显的提升导入速度;innodb_flush_log_at_trx_commit参数解释如下:
0:log buffert 中的数据将以每秒一次的频率写入到log file中,且同时会进行文件系统到磁盘的同步提作,但是每个事务的 commit 并不会然触发任何 log buffer 到 log file 的刷新或者文件系统到磁盘的刷新操作:
1:在每次事务提交的时候将 log buffer 中的数据都会写入到 log file,同时也会触发文件系统到磁盘的同步;
2:事务提交会触发 log buffer 到 log file 的刷新,但并不会触发磁盘文件系统到磁盘的同步。此外,每秒会有一次文件系统到磁盘同步操作。
4. 手动使用事务
因为 mysql 默认是自动提交事务的,这样每插入一条数据,都会进行一次 commit 所以为了减少创建事务的消耗,我们可以手工使用事务;
十五. 什么是全局锁,共享锁和排它锁?
全局锁就是对整个数据库实例加锁,它的典型使用场景就是做全库逻辑备份。这个命令可以使整个库处于只读状态。使用该命令之后,数据更新语句、数据定义语句、更新类事务的提交语句等操作都会被阻塞。
共享锁又称读锁(read lock)是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。当如果事务对读锁进行修改操作,很可能会造成死锁。
排他锁也叫又称写锁(writer lock)若某个事物对某一行加上了排他锁,只能这个事务对其进行读写,在此事务结束之前,其他事务不能对其进行加任何锁,其他进程可以读取,不能进行写操作,需等待其释放。排它锁是悲观锁的一种实现。若事务 1 对数据对象 A 加上 X 锁,事务 1 可以读 A 也可以修改 A,其他事务不能再对 A 加任何锁,直到事物 1 释放 A 上的锁。这保证了其他事务在事务 1 释放 A 上的锁之前不能再读取和修改 A。排它锁会阻塞所有的排它锁和共享锁;
十六. mysql 中的死锁
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁;如何查看死锁?使用命令:show engine innodb status 查看最近的一次死锁,InnoDb Lock Monitor 打开锁监控,每 15s 输出一次日志,使用完毕后建议关闭,否则会影响数据库性能。对待死锁常见的两种策略:
通过 innodblockwait_timeout来设置超时时间,一直等待直到超时;发起死锁检测,发现死锁之后,主动回滚死锁中的某一个事务,让其它事务继续执行。
十七. mysql 如何实现读写分离?
一个服务器上安装运行多个 mysql 实例并实现主从复制
注意:以 centos 7为例,mysql 版本为 5.6.10,由于是在一台机器上安装多个相同服务,所以最好使用二进制方式或者编译源码配置启动,不能使用yum安装
1. 添加用户组
groupadd mysql
添加用户 mysql 到用户组 mysql
useradd -g mysql mysql
并以mysql 用户登录,现在所在目录为用户的主目录/home/mysql
2. 下载一份 mysql 二进制包
wget https://downloads.mysql.com/archives/get/file/mysql-5.6.10-linux-glibc2.5-x86 64.tar.gz
十八. mysql 如何实现分库分表?
随着业务的发展,我们表中的数据量会变的越来越大,字段也可能随着业务复杂度的升高而逐渐增多,我们为了解决单表的查询性能问题,一般会进行分表操作;同时我们业务的用户活跃度也会越来越高,并发量级不断加大,那么可能会达到单个数据库的处理能力上限。此时我们为了解决数据库的处理性能瓶颈,一般会进行分库操作。不管是分库操作还是分表操作,我们一般都有两种方式进行拆分,第一种是垂直拆分,第二种是水平拆分。关于两种拆分方式的区别和特点
垂直拆分特点:
- 每个库(表)的结构都不一样
- 每个库(表)的数据至少一列一样
- 每个库(表)的并集是全量数据
垂直拆分优缺点:
优点:
- 拆分后业务清晰(专库专用,按业务拆分);
- 数据维护简单,按业务不同,业务放到不同机器上;
缺点:
- 如果单表的数据量,写读压力大;
- 受某种业务决定,或者被限制,也就是说一个业务往往会影响到数据库的瓶颈(性能问题,如双十一抢购);
- 部分业务无法关联 join,只能通过 java 程序接口去调用,提高了开发复杂度;
水平拆分特点:
每个库(表)的结构都一样
每个库(表)的数据都不一样
每个库(表)的并集是全量数据
水平拆分优缺点
优点:
- 单库/单表的数据保持在一定量(减少),有助于性能提高
- 提高了系统的稳定性和负载能力
- 拆分表的结构相同,程序改造较少。
缺点:
- 数据的扩容很有难度维护量大
- 拆分规则很难抽象出来
- 分片事务的一致性问题部分业务无法关联 join,只能通过 java 程序接口去调用
十九. 索引的基本原理
索引可以用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。索引的原理:就是把无序的数据变成有序的查询;
1. 把创建了索引的列的内容进行排序;
2. 对排序结果生成倒排表;
3. 在倒排表内容上拼上数据地址链;
4. 在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据;
二十. 聚簇索引与非聚簇索引的区别
聚簇索引与非聚簇索引都是 B+ 树的数据结构;
聚簇索引:将数据存储与索放到了一块、并且是按照一定的顺序组织的,找到索引也就找到了数据,数据的物理存放顺序与索引顺序是一致的,即:只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上的;
非聚簇索引:叶子节点不存储数据、存储的是数据行地址,也就是说根据索引查找到数据行的位置再去磁盘查找数据,这个就有点类似一本书的目录,比如我们要找第三章第一节,那我们先在这个目录里面找,找到对应的页码后再去对应的页码看文章。
优势:
1. 查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次查询(非覆盖索引的情况下)效率要高;
2. 聚簇索引对于范围查询的效常很高,因为其数据是按照大小排列的;
3. 聚簇索引适合用在排序的场合,非聚簇索引不适合;
劣势:
1. 维护索引很昂贵,特别是插入新行或者主键被更新导致要分页的时候,建议在大量插入新行后,选在负载较低的时间段,通过 optimization table 优化表,因为被移动的行数据可能造成碎片。使用独享表空问可以弱化碎片;
2. 表因为使用 uuid 作为主键,使数据存储稀疏,这就会出现聚簇索引有可能有比全表扫描更慢,所以建议使用 int 的 auto_increment 作为主键;
3. 如果主键比较大的话,那辅助索引将会变的更大,因为轴助索引的叶子存储的是主键值:过长的主键值,会导致非叶子节点占用更多的物理空间;
InnoDb 中一定有主键,主键一定是聚簇索引,不手动设置、则会使用 unique 索引,没有 unique 索引,则会使用数据库内部的一个行的隐藏 id 来当作主键索引。在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值;
MYISAM 使用的是非聚簇索引,没有聚簇索引,非聚簇索引的两棵 B+ 树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引 B+树的节点存储了主键,辅助键索引 B+树存储了辅助键。表数据存储在独立的地方,这两棵 B+ 树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。如果涉及到大数据量的排序、全表扫描、计数之类的操作的话,还是 MyISAM 占优势些,因为索引所占空间小,这些操作是需要在内存中完成的。
二十一. mysql 索引的数据结构以及各自的优劣
索引的数据结构和具体存储引擎的实现有关,在 mysql 中使用较多的索引有 hash 索引和 B+ 树索引等,InnoDb 存储引擎的默认索引实现为:B+ 树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择 B+ 树索引。
B+ 树索引:
B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的节点间有指针相互链接,在 B+ 树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时也可以利用双向时,也可以利用指针快速左右移动,效率非常高。因此,B+ 树索引被广泛应用于数据库、文件系统等场景。
哈希索引:
哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似 B+ 树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快;如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;前提是键值都是唯一的,如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;哈希索引也没办法利用索引完成排序,以及像 like 'xxx%' 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);哈希索引也不支持多列联合索引的最左匹配规则;B+ 树索引的关键字检索效率比较平均,不像 B 树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在哈希碰撞问题:
所以只有当单条查询的情况下才会使用哈希索引,其余情况都是使用 B+ 树索引;
二十二. 索引设计的原则
一般需要遵循两个原则:查询更快和占用空间更小
1. 适合索引的列是出现在 where 子句中的列,或者连接子句中指定的列;
2. 基数较小的表,索引效果较差,没有必要在此列建立索引;
3. 使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间,如果搜索词超过索引前缀长度,则使用索引排除不匹配的行,然后检查其余行是否可能匹配;
4. 不要过度索引,索引需要额外的磁盘空间,并降低写操作的性能,在修改表内容的时候,索引会进行更新甚至重构,索列越多,这个时间就会越长,所以只保持需要的索引有利于查询即可;
5. 定义有外键的数据列一定要建立索引;
6. 更新频繁字段不适合创建索引;
7. 若是不能有效区分数据的列不适合做索引,列如性别,男女未知,最多也就三种,区分度实在太低),也即使用索引查询到的数据量越少越好;
8. 尽量的扩展索引,不要新建索引,比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可;
9. 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引;
10. 对于定义为text、image和bit的数据类型的列不要建立索引;
二十三. mysql 锁的类型
基于锁的属性分类:共享锁、排他锁
基于锁的粒度分类:行级锁(InnoDb)、表级锁(InnoDb、MYISAM)、页级锁(BDB 引擎)、记录锁、间隙锁、临键锁。
基于锁的状态分类:意向共享锁、意向排它锁。
共享锁:
共享锁又称读锁,简称 S 锁:当一个事务为数据加上读锁之后,其他事务只能对该数据加读锁,而不能对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加持写锁,共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,避免出现重复读的问题:
排他锁:
排他锁又称写锁,简你 X 锁;当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁,排他锁的目的是在数据修改时候,不允许其他人同时修改,也不允许其他人读取。避免了出现脏数据和脏读的问题;
表锁:
表锁是指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问;
特点:
粒度大,加锁简单,容易冲突;
行锁:
行锁是指上锁的时候锁住的是表的某一行或多行记录,其他事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问(行锁和记录锁会出现死锁问题)
特点:粒度小,加锁比表锁麻顷,不容易冲突,相比表锁支持的并发要高:
记录锁:
记录锁也属于行锁中的一种,只不过记录锁的范围只是表中的某一条记录,记录锁是说事务在加锁后锁住的只是表的某一条记录。精准条件命中,并且命中的条件字段是唯一索引,加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前被其他事务读取的脏读问题;
页锁:
页级锁是 mysql 中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录;
特点:开销和加锁时间界于表锁和行锁之间:会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般;
间隙锁:
属于行锁中的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间,当表的相邻 id 之间出现空隙则会形成一个区间,遵循左开右闭原则。范围查询并且查询未命中记录,查询条件必须命中索引、间隙锁只会出现在 REPEATABLE_READ(重复读)的事务级别中。
触发条件:防止幻读问题,事务并发的时候,如果没有间隙锁,就会发生下面的问题,在同一个事务里,A 事务的两次查询出的结果会不一样。比如表里面的数据 id 为1,4,5,7,10,那么会形成以下几个间隙区问,-n-1 区间,1-4 区间,7-10 区间,10-n 区间(-n代表负无穷大,n 代表无穷大)
临键锁
也属于行锁的一种,并且它是 InnoDb 的行锁默认算法,总结来说它就是记录锁和问隙锁的组合,临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住;触发条件:范围查询并命中,查询命中了索引;结合记录锁和间隙锁的特性,临键锁避免了在范围查询时出现脏读、重复读、幻读问题。加了临键锁之后,在范围区间内数据不允许被修改和插入;
意向共享锁
当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁。
意向排他锁
当一个事务试图对整个表进行加排它锁之前,首先需要获得这个表的意向排它锁。
如果当事务A加锁成功之后就设置一个状态告诉后面的人,已经有人对表里的行加了一个排他锁了,你们不能对整个表加共享锁或排它锁了,那么后面需要对整个表加锁的人只需要获取这个状态就知道白己是不是可以对表加锁,避免了对整个索引树的每个节点扫描是否加锁,而这个状态就是意向锁。
二十四. mysql 执行计划
主要涉及到 mysql 优化这方面的知识点, 那么就需要掌握执行计划中的各个参数,参数还是比较多的但是使用得比较多的就只有几个字段而已;执行计划就是 sql 的执行查询的顺序,以及如何使用索引查询,返回的结果集的行数;
例如:EXPLAIN SELECT from A where X=? and Y=?
1. id:是一个有顺序的编号,是查询的顺序号,有几个 select 就显示几行。id 的顺序是按 select 出现的顺序增长的,id 列的值越大执行优先级越高越先执行,id 列的值相同则从上往下执行,id 列的值为 null 最后执行。
2. select_type 表示查询中每个 select 子句的类型
SIMPLE:表示此查询不包含UNION查询或子查询
PRIMARY:表示此查询是最外层的查询(包含子查询)
SUBQUERY:子查询中的第一个SELECT
UNION:表示此查询是 UNION 的第二或随后的查询
DEPENDENT UNION:UNION中的第二个或后面的查询语句,取决于外面的查询
UNION RESULT:UNION的结果
DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询. 即子查询依赖于外层查询的结果
DERIVED:衍生,表示导出表的SELECT(FROM子句的子查询)
3. table:表示该语句查询的表
4. type(核心字段):优化 sql 的重要手段,也是我们判断 mysql 性能和优化程度重要指标,取值范围为:const:通过索引一次命中,匹配一行数据;
system:表中只有一行记录,相当于系统表;
eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配;
ref:非唯一性索引扫描,返回匹配某个值的所有;
range:只检索给定范围的行,使用一个索引来选择行,一般用于between、<、>;
index:只遍历索引树;
ALL:表示全表扫描,这个类型的查询是性能最差的查询之一。那么基本就是随着表的数量增多,执行效率越慢;执行效率:
ALL < index < range < ref < eq_ref < const < system,最好是避免 ALL 和 index;
5. possible_keys:它表示 mysql 在执行该 sql 语句的时候,可能用到的索引信息,仅仅是可能,实际不一定会用到;
6. key:此字段是 mysql 在当前查询时所真正使用到的索引,它是 possible_keys 的子集;
7. key_len:表示查询优化器使用了索引的字节数,这个字段可以评估组合索引是否完全被使用,这也是我们优化 sql 时,评估索引的重要指标
9. rows:mysql 查询优化器根据统计信息,估算该 sql 返回结果集需要扫描读取的行数,这个值相关重要,索引优化之后,扫描读取的行数越多,说明索引设置不对,或者字段传入的类型之类的问题,说明要优化空间越大;
10. filtered:返回结果的行占需要读到的行(rows 列的值)的百分比,就是百分比越高,说明需要查询到数据越准确,百分比越小,说明查询到的数据量大,而结果集很少
11. extra
using filesort:表示mysql对结果集进行外部排序,不能通过索引顺序达到排序效果。一般有 using filesort 都建议优化去掉,因为这样的查询cpu资源消耗大,延时大;
using index:覆盖索引扫描,表示查询在索引树中就可查找所需数据,不用扫描表数据文件,往往说明性能不错;
using temporary:查询有使用临时表,一般出现于排序,分组和多表 join 的情况,查询效率不高,建议优化;
using where:sql 使用了 where 过滤,效率较高。
二十五. 事务的隔离级别
隔离性有 4 个隔离级别,分别是:
read uncommit:读未提交,可能会读到其他事务未提交的数据,也叫做脏读;
用户本来应该读取到 id = 1 的用户 age 应该是 10,结果读取到了其他事务还没有提交的事务,结果读取结果 age = 20,这就是脏读;
read commit:读已提交,两次读取结果不一致,叫做不可重复读;不可重复读解决了脏读的问题,他只会读取已经提交的事务;
用户开启事务读取 id = 1 用户,查询到 age = 10,再次读取发现结果 = 20,在同一个事务里同一个查询读取到不同;
repeatable read:可重复读,这是 mysql 的默认级别,就是每次读取结果都一样,但是有可能产生幻读;
serializable:串行,一般是不会使用,他会给每一行读取的数据加锁,会导致大量超时和锁竞争的问题;
二十六. 关心过业务系统里面的 sql 耗时吗?统计过慢查询吗?对慢查询都怎么优化过?
在业务系统中,除了使用主键进行的查询,其他的都会在测试库上测试其耗时,慢查询的统计主要由运维在做,会定期将业务中的慢查询反馈给我们。慢查询的优化首先要搞明白慢的原因是什么?是查询条件没有命中索引?是查询了不需要的数据列?还是数据量太大?所以优化也是针对这三个方向来的:
1. 首先分析语句,看看是否 load 了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写;
2. 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引;
3. 如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表;
二十七. ACID 靠什么来保证的?
在了解 ACID 靠什么来保证的时候需要了解 mysql 的日志;
A:原子性由 undo log 日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的 sql;
C:一致性由其他三大特性保证、程序代码要保证业务上的一致性;
I:隔离性由 mvcc 来保证;
D:持久性由内存 + redo log 来保证,mysql 修改数据同时在内存和 redo log 记录这次操作,宕机的时候可以从 redo log 恢复;
InnoDb redo log 写盘,InnoDb 事务进入 prepare 状态;
如果前面 prepare 成功,binlog 写盘,再继续将事务日志持久化到 binlog,如果持久化成功,那么 InnoDb 事务则进入 commit 状态(在redo log 里面写个 commit 记录);redolog 的刷盘会在系统空闲时进行。
二十八. 什么是 mvcc ?
mvcc 是 mysql 中隔离级别中一个最最重要的一个概念,对于 mvcc 的掌握意味着是否真正理解 mysql 事务的隔离级别是如何实现的?多版本并发控制:读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务 session 会看到自己特定版本的数据,版本链 mvcc 只在 read commited (不可重复读)和 repeatable read (可重复读)两个隔离级别下工作。其他两个隔离级别和 mvcc 不兼容,因为 read uncommited 总是读取最新的数据行,而不是符合当前事务版本的数据行,而 serializable 则会对所有读取的行都加锁。
聚簇索引记录中有两个必要的隐藏列:
trx_id:用来存储每次对某条聚簇索引记录进行修改的时候的事务 id;
roll_pointer:每次对哪条聚簇索引记录有修改的时候,都会把老版本写入 undo 日志中。这个 roll pointer 就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本);
已提交读和可重复读的区别就在于它们生成 ReadView 的策略不同;
开始事务时创建 readview,readView 维护当前活动的事务 id,即未提交的事务id,排序生成一个数组访问数据,获取数据中的事务 id(获取的是事务 id 最大的记录),对比 readview:
如果在 readview 的左边(比 readview 都小),可以访问(在左边意味着该事务已经提交)
如果在 readview 的右边(比 readview 都大)或者就在 readview 中,不可以访问,获取 roll_pointer,取上一版本重新对比,在右边意味着事务物在 readview 生成之后出现,在 readview 中说明事务还未提交;
已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的 readview ,而可重复读隔离级别则在第一次读的时候生成一个 ReadView,之后的读都复用之前的 readview 。
这就是 mysql 的 mvcc 通过版本链,实现多版本,可并发读写,写-读,通过 readview:生成策略的不同实现不同的隔离级别。
二十九. mysql 主从同步原理
mysql 主从同步的过程:
mysql 的主从复制中主要有三个线程:master(Binary log dump thread)、slave(I/O thread、SQL thread);master一条线程和 slave 中的两条线程。
1. 主节点 binlog,主从复制的基础是主库记录数据库的所有变更记录到 binlog。binlog 是数据库服务器启动的那一刻起,保存所有修改数据库结构或内容的一个文件;
2. 主节点 Binary log dump thread 线程,当 binlog 有变动时,Binary log dump thread 线程读取其内容并发送给从节点;
3. 从节点 I/O 线程接收 binlog 内容,并将其写入到 relay log 文件中;
4. 从节点的 SQL 线程读取relay log文件内容对数据更新进行重放,最终保证主从数据库的一致性;
注:主从节点使用 binlog 文件 + position 偏移量来定位主从同步的位置,从节点会保存其已接收到的偏移量,如果从节点发生宕机重启,则会自动从position的位置发起同步。由于 mysql 默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念:
全同步复制:
主库写入 binlog 后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响:
半同步复制:
和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回 ACK 确认给主库,主库收到至少一个从库确认就认为写操作完成:
三十. 简述 mysql 中索引类型以及对数据库的性能的影响
普通索引:允许被索引的数据列包含重复的值。
唯一索引:可以保证数据记录的唯一性。
主键:是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字 PRIMARY KEY 来创建。(在 InnoDb 中主键索引与唯一索引是有区别的,但是在 MYISAM 中主键与唯一索引的存储是一样的,都是只存储地址而不存储数据,通过地址来获取对应的数据)
联合索引:索引可以覆盖多个数据列,如像 index(columnA,columnB) 索引。
全文索引:通过建立倒排索引,可以极大的提升检索效率,解决判断字段是否包含的问题,是目前搜索引擎使用的一种关键技术。可以通过 alter table table_name add fulltext (column):创建全文索引;
索引可以极大的提高数据的查询速度,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能,但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要操作索引文件索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大,如果非聚集索引很多,一旦聚集索引改变,那么所有非聚集索引都会跟着变。
三十一. 什么是倒排索引?有什么好处呢?
索引:从 id 到内容;倒排索引:从内容到 id;好处:比较适合做关键字检索,可以控制搜索的总量,提高查询的效率;例如百度中:文章--> term--> 排序--> term dictionary ---> term index --> posting list 记录文章 id 和偏移量,权重;例如:java 1,5,10;搜索引擎为什么比 mysql 快:term dictionary 和 term index的存在;
三十二. mysql 主从复制集群如何搭建?读写分离是怎么样做的?
mysql 主从集群搭建的原理:mysql 通过将主节点的 binlog 同步给从节点完成主从之间的数据同步,mysql 的主从集群只会将 binlog 从主节点同步到从节点,而不会反过来同步,由此也就引申出来读写分离的问题,因为要保证主从中间的数据一致,写数据的操作只能够在主节点完成,而读数据的操作可以在主节点或者是从节点完成,读写分离可以很大地提升数据库的性能;主从架构师由 mysql 搭建的,但是读写分离并不是由 mysql 提供的功能,而是由 mysql 的主从集群上由业务系统提供的自己去实现的功能,