文章目录
前言
本专栏系列文章仅用于个人学习总结,从零开始逐步到常见服务框架。觉得基础的大佬可以提前离开。欢迎各位大佬评论指教,如有不当之处请及时联系调整 ~
本专栏工作之余抽空更新…
上一篇文章地址:5.springmvc、springBoot、mybatis
Mysql
1.索引的基本原理
索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表
索引的原理: 就是把无序的数据变成有序的查询(排好序),即把创建了索引的列的内容进行排序.
上面每个磁盘块相当于一个页,磁盘块1 中记录着其他磁盘块的地址等,其他同理
索引是如何支持千万级表的快速查询? (B+树,也就是树结构,每个node = 16K)
我们来粗略计算下上图(聚簇索引概图) B+Tree可以存放的总元素, 假设每个索引(10,17等)为 bigInt类型, 即占 8bit ,指针默认 6bit , 页的默认大小为16K, 即每一页可存储
(16*1024 ) / 14 = 1170 个元素 (除了叶子节点,因为它存储了data,占的字节可能比较多)
那么我们假设 叶子节点,每个索引的data存放了 2K的数据 , 图中树的高度为 h =3 ;
那么可存储的总元素为 : 1170 * 1170 * (16/2) 约等于 一千多万个元素
相当于, 我们只需要查询 3 次(前面两层主要是为了算法快捷查询,例二分查找,首层尽可能多的存放元素(你也可以理解新华字段,需要目录),叶子节点涵盖了所有的key), 就能在1000万条数据中, 找到想要的结果,这就是索引支持千万级别表的原理
反过来算,1000万 * 2kb = 2000万kb总数据大小, 2000万/16KB = 125万页
而没有索引的话(没排好序),我们就得全表扫描,才能找到所有记录.
2.mysql聚簇和非聚簇索引的区别
指的都是数据存储方式, 都是B+树的数据结构 .(如果只查一条记录可以设置hash索引,一次定位效率更高)
- 聚簇索引: 数据存储与索引放到了一块, 叶子节点包含了索引对应的完整的数据记录 (参考第1问的图中,叶子节点存储的是data) 例InnoDB必有1个聚簇索引,N个二级索引(二级索引子节点存储的是聚簇索引的id,需要回表到聚簇索引表找到数据)
- 非聚簇索引: 数据存储和索引分开, 叶子节点不存储数据、存储的是该索引对应数据的磁物理指针地址 (也就是分两步走,先根据索引查找到数据行的位置再去取磁盘查找数据) 例MyIsam
磁盘中存储结构区别如图:
① .frm 是表结构文件 (两种引擎都有)
② 而MyISAM将数据 (.MYD) 和索引 (MYI) 分为了两个文件存储.
③ InnoDB直接使用 .ibd 存储数据和索引.
何时使用聚簇索引与非聚簇索引:
如果涉及到大数据量的排序、全表扫描、count之类的操作的话,还是MyISAM占优势些,因为索引所占空间小
第1点索引的基本原理中提到,聚簇索引的叶子存了data,占了2K,所以计算结果约可存放1000万个元素,而非聚簇索引叶子节点不存放数据,故可以放更多的索引,所以大数据量时,myisam更合适
扩展知识:
1. 为什么InnoDB引擎表必须有主键 / 聚簇索引 ? (主键和聚集索引不是一个东西,不要混淆)
InnoDB采用B+树做存储结构, 而一个B+树的节点可以存储key、地址、行数据(仅叶子节点),key 就是不重复的值且可以比较(确保树进行分裂时,可以确定是左孩子还是右孩子)。而主键刚好符合这个要求
2. 聚簇索引生成规则:
(1)有主键,主键默认是聚集索引;
(2)没有主键,首个非空唯一列是聚集索引;
(3)没有符合条件的列,row-id(隐藏id)是聚集索引;
需知:
① 可以不声明主键,但必须要有聚集索引
② 虽然myql自己会帮我们处理,但是这种事情应该我们开发者来做,来声明
3. 辅助索引: 基于聚簇索引的概念上,除聚簇外都是辅助索引. (与非聚簇索引相似, 但是非聚簇索引叶子节点存储的是数据的磁盘地址
, 而辅助索引叶子节点存储的是聚簇索引
), 拿到该聚簇索引后再回到开头所说的聚簇索引树去取出对应的data记录 (回表时是读磁盘,虽然拿的是id,但是磁盘读取是无序的!! 相当于随机 io,数据多时性能不高, MRR-多范围读取可以优化,即先读取一部分二级索引记录,将它们的主键值排好序之后再统一执行回表操作)
覆盖索引: 其实不是真正的索引,是一种概念, 例查询条件 select 后面的字段是有索引的,可以叫做覆盖索引,此时就不需要去回表了,因为该直接第一次查询时就能得到,性能好
MRR: mysql内部对回表的优化,条件苛刻
例如 InnoDB中, A表的主键 id 是聚簇索引, 此时我们额外对 name 列建索引, 该索引就是`辅助索引` .(而MyIsam可以认为都是辅助索引, 其叶子节点都是地址值)
4.最左前缀原则
例如我们建联合索引 index (A,B,C) , 此时sql语句的where条件中, A字段必须有被使用执行,查询才会走该索引.(越靠左边, 索引越重要)
举个例子:
范围列后面的列无法用到索引,为什么呢?(只有组合索引有该问题)
假设 index(a ,b c,d) ,当条件为 where a =1 and b>3 and c =4 and d=5, 此时 c 和 d利用率是很低的, 因为 只有a是真正有序的, b c d只能称作’部分有序’, 即假设 有很多条 a=1,那么这很多条a中,对应的 b是有序的, 即a相等的情况下,他们的b才会有序. c d同理
那么如何提升性能?
如果将索引设置为 index(a,c,b,d) ,底层会优化sql为 where a=1 and c=4 and b>3 and d=5,即只有d的索引无效了,利用提高了,当然,最好将范围查询放最后.
3.索引设计的原则
查询更快、占用空间更小, 并且索引不是越多越好 (更新操作要维护索引)
- ① 基数较小的表,索引效果较差,一般没有必要建立索引.
- ② 使用短索引. 前面辅助索引图可看出,如果某列数值过长, 在该列建立辅助索引时, 该列很占空间,效率也不高. (这种情况应该对该列的数值截取前面N位长度作为索引即可)
- ③ 更新频繁字段不适合创建索引 (更新操作要重新维护索引表)
- ④ 若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低, 前面已说了,索引其实就是
排好序
的结构,你 男 男 男 这种索引,怎么给你排?排了也没意义 ,当然,如果100条数据中,99条男,1条女,这种区分度比较大,而你又经常查询 女 的记录,可以考虑加索引)
4.锁的类型有哪些
基于锁的属性分类: 共享锁、排他锁。
基于锁的粒度分类:行级锁(InnoDB)、表级锁(InnoDB、MyIsam)、页级锁(BDB引擎 )、记录锁(InnoDB)、间隙锁(InnoDB)、临键锁(InnoDB)。
实际上属性分类和状态分类, 是从不同维度看待 粒度分类.
基于锁的状态分类: 意向共享锁、意向排它锁。
- 共享锁(Share Lock)
读锁,简称S锁
当一个事务为数据加上读锁之后,其他事务
只能对该数据加读锁
,而不能对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加持写锁。共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,避免出现重复读的问题。
- 排他锁(eXclusive Lock)
写锁,简称X锁
当一个事务为数据加上写锁时,其他请求将
不能再为数据加任何锁
,直到该锁释放之后,其他事务才能对数据进行加锁。排他锁的目的是在数据修改时候,不允许其他人同时修改,也不允许其他人读取。避免了出现脏数据和脏读的问题。
- 行锁
行锁是指上锁的时候
锁住的是表的某一行或多行记录
,其他事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问;
特点:粒度小,加锁比表锁麻烦(要去找到加锁的那行记录),不容易冲突,相比表锁支持的并发要高;
- 表锁
表锁是指上锁的时候
锁住的是整个表
,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问;
特点: 粒度大,加锁简单,容易冲突;
- 页锁
折中的页级,一次锁定相邻的一组记录
页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁
。表级锁速度快,但冲突多,行级冲突少,但速度慢。
特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
- 记录锁(Record Lock)
属于行锁中的一种 例: SELECT * FROM 表 WHERE id='3' FOR UPDATE;
记录锁是说事务在加锁后
锁住的只是表的某一条记录
。(精准条件命中,并且命中的条件字段是唯一索引 --> 唯一索引不允许两行具有相同的索引值)
加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前被其他事务读取的脏读问题。
- 间隙锁(Gap Lock)
只会出现在REPEATABLE_READ(重复读)的事务级别,防止幻读
属于行锁中的一种,间隙锁是在事务加锁后其
锁住的是表记录的某一个区间
,当表的相邻ID之间出现空隙则会形成一个区间,遵循左开右闭原则。(比如 ID=1 和 ID=4之间,锁住的就是 2,3,4)
触发条件: ①使用for update等加锁操作 ②且是范围查询或查询未命中(即随便查了个不存在数值) ③且查询条件必须是索引(唯一索引则生成记录锁)
具体参考链接 记录锁、间隙锁、临键锁
设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
- 临建锁(Next-Key Lock)
INNODB的行锁默认算法
也属于行锁的一种,它就是
记录锁和间隙锁的组合
,临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住.
触发条件: 范围查询并命中,查询命中了索引。(其实就是和间隙锁差不多)
结合记录锁和间隙锁的特性,临键锁避免了在范围查询时出现脏读、重复读、幻读问题。加了临键锁之后,在范围区间内数据不允许被修改和插入。
- 意向共享锁
当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁。
- 意向排他锁
当一个事务试图对整个表进行加排它锁之前,首先需要获得这个表的意向排它锁。
意向的解释: 例如事务A加锁成功之后就设置一个状态
, 用于告诉后面的所有人,已经有人对表里的行加了一个排他锁了,其他人不能对
整个表加共享锁或排它锁了
。那么后面需要对整个表加锁的人只需要获取这个状态就知道自己是不是可以对表加锁,避免了对整个索引树的每个节点扫描是否加锁
,而这个状态就是意向锁
。
多用在innoDB中,是数据库自身的行为,不需要人工干预,在事务结束后会自行解除。
扩展知识:
- MyISAM在执行查询语句SELECT前,会自动给涉及的所有表加读锁,在执行update、insert、delete操作会自动给涉及的表加写锁。
- InnoDB在执行查询语句SELECT时,不会加锁 (串行隔离级别时会加)。update、insert、delete操作会加行锁 (但有MVCC在时,读写不互斥)。
写在最后:
- ① 读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞.
- ② 使用 … for update 加
写锁
, 如果where条件后的索引不明确(即没有索引), 则会变成表锁, 即无索引行锁会升级为表锁. - ③ 使用… lock in share mode 加读锁.
- ④ 唯一索引只有锁住多条记录或者一条不存在的记录的时候,才会产生间隙锁,指定给某条存在的记录加锁的时候,只会加记录锁,不会产生间隙锁;
- ⑤ 普通索引不管是锁住单条,还是多条记录,都会产生间隙锁;
MySQL InnoDB 引擎的可重复读隔离级别(默认隔离级),根据不同的查询方式,分别提出了避免幻读的方案:
针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读。
针对当前读(select … for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读。
我举例了两个发生幻读场景的例子。
第一个例子:对于快照读, MVCC 并不能完全避免幻读现象。因为当事务 A先查询, 但是能成功更新刚才未查询到的,一条事务 B 插入的记录,就发生幻读。
第二个例子:对于当前读,如果事务开启后,并没有执行当前读,而是先快照读,然后这期间如果其他事务插入了一条记录,那么事务后续使用当前读进行查询的时候,就会发现两次查询的记录条目就不一样了,所以就发生幻读。
所以,MySQL 可重复读隔离级别并没有彻底解决幻读,只是很大程度上避免了幻读现象的发生。
要避免这类特殊场景下发生幻读的现象的话,就是尽量在开启事务之后,马上执行 select ... for update 这类当前读的语句,因为它会对记录加 next-key lock,从而避免其他事务插入一条新记录。
5.事务的基本特性和隔离级别
注:以下特性自己测试需要开启事务-> begin,提交事务-> commit, 回滚-> rollback
四大特性 (ACID):
- 原子性(Atomicity): 一个事务中的操作要么全部成功,要么全部失败。
- 一致性(Consistent): 数据层面, 事务中数据最终要一致, 即预期结果与我们要的一致。
- 隔离性(Isolation): 不同事务中, 不互相干扰 。
例 : A事务中,首次查某条记录的 age = 1,此时即使B事务, 将 age修改为 2 , 但是在A事务中, 再次去 查 age,得到的仍然是 1, 该隔离级别为默认级别-> 可重复读
- 持久性(Durable): 一旦事务提交,所做的修改就会永久保存到数据库中。
隔离性有4个隔离级别,分别是:
- 读未提交: 可能会读到其他事务未提交的数据,也叫做脏读
- 读已提交: 读取其他事务 commit的sql, 多次读同一记录可能出现值不同的情况,叫不可重复读
- 可重复读: A事务第一次使用select , 查到 age=1 ,(即使其他事务中改变了age)后面再去读仍然是 age=1 ( 说明: A事务第一次select后,就生成了A事务唯一的 readView 和 undo(不再改变,包含所有表,使用select的结果都不再因为其他事务改变), 一直存在直到事务commit, 且作用于A事务中的所有 select 。
- 可串行化: 多个事务的读或者写, 如果操作到了同一条数据, 后面操作的事务需要等待!!!
并发事务可能带来的问题:
- 脏读: A事务,读到了B事务 未commit的数据, 因为B可能会回滚, 所以A会出问题。
- 不可重复读: 事务A内部的相同查询语句在不同时刻读出的结果不一致,不符合隔离性。
例: A事务首次读 age=1, 此时B事务将 age改为 2 , A事务再次去读取 age,得到的竟然是 2,称为不可重复读,不符合隔离性 (可重复读则相反 )
- 幻读: 如下流程, 可重复读隔离级别下, 修改和查询到了’不存在’的记录(第13行代码), 最开始select时没找到id= 6, 现在他又出现了, 就好像幻觉一样。
#A事务
begin;
select * from 表; #第一次查出5条记录(id = 1~5)
#此时B事务插入了 id =6 数据, 且提交
select * from 表; #还是5条,没有 id = 6的记录,符合可重复读 (快照读)
select * from 表 where id =6; #没找到,符合可重复读
update 表 set name='修改到了' where id =6; #竟然修改成功 (注:使用update是当前读,会读到真实数据)
select * from 表; #查到了6条记录
commit;
#B事务
begin;
INSERT INTO 表(`id`, `name`, `update_time`) VALUES (6, 'F', '0000-00-00 00:00:00');
commit;
小结: 如果你总是进行快照读,或者总是进行当前读,是不会出现幻读的情况的。如上情况, 快照和当前读混合使用,则会出现幻读。
解决幻读: 第3行sql调整为 select * from 表 for update; 相当于给他加了锁, 其他事务无法在做加锁操作, 即B事务会被阻塞, 直到A事务 commit;
在RR和RC级别下,数据库的读分为快照读和当前读:
- 快照读:单纯的select操作。读取的是快照(ReadView)中的数据,可能是历史数据
- 当前读:select … for update/in share mode 或 update 或 insert 或 delete, 读取的总是当前的最新数据, 即其他事务commit的数据也能读到 (可重复读’失效’)
6.ACID靠什么保证的?
- A: 原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
- C: 一致性由其他三大特性保证、程序代码要保证业务上的一致性
- I: 隔离性由MVCC来保证
- D: 持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,宕机的时候可以从redo log恢复 (redolog的刷盘会在系统空闲时进行)
InnoDB redo log 写盘,InnoDB 事务进入 prepare 状态。
如果前面 prepare 成功,binlog 写盘,再继续将事务日志持久化到 binlog,如果持久化成功,那么InnoDB 事务则进入 commit 状态(在 redo log 里面写一个 commit 记录)
7.什么是MVCC?
多版本并发控制: 数据库通过它能够做到遇到并发读写的时候,在不加锁的前提下实现安全的并发读操作,是一种乐观锁的实现方式,能大大提高数据库的并发性能 (保证了较高的隔离性)。
MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作。其他两个隔离级别和MVCC不兼容, 因为 READ UNCOMMITTED 总是读取最新的数据行, 而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁。
聚簇索引记录中有两个必要的隐藏列:
trx_id: 用来存储每次对某条聚簇索引记录进行修改的时候的事务id。
roll_pointer: 每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中(一般用于回滚)。这个roll_pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本)
(图为undo日志版本链)
(图为readview)
① 开始事务时创建readview,readView维护当前活动的事务id,即未提交的事务id,排序生成一个数组; (假设此时 readview范围为 [200], 300)
② 访问数据时,获取undo版本链中的事务id(获取的是事务id最大的记录),与readview对比;
③ 如果事务id在readview的左边(比readview都小),可以访问(在左边意味着该事务已经提交)
例上图中,目前获取最大的事务为70,在readview的左边, 表示可以访问这条记录
④ 如果在readview的右边(比readview都大)或者就在readview中,不可以访问时,会去获取roll_pointer,取上一版本重新对比(在右边意味着,该事务在readview生成之后出现,在readview中意味着该事务还未提交)
例此时获取的事务id若为 350,比readview最大的300还要大,则不可以访问这条记录, 因为他在其他事务中都还没提交!!! 你不能读取它, 只能拿roll_pointer去找上一轮的值, 反复如此进行~
结语:
已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的ReadView, 而可重复读隔离级别则在第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView。(所以该视图一旦生成, 即使其他事务已经commit了数据, 通过undo与 readview对比, 发现比 readview大, 还是不会读取到, 实现了可重复读
)
这就是Mysql的MVCC,通过版本链,实现多版本,可并发读-写,写-读。通过ReadView生成策略的不同实现不同的隔离级别。
8.mysql执行计划怎么看?
执行计划就是sql的执行查询的顺序,以及如何使用索引查询,返回的结果集的行数.
使用 EXPLAN …SQL语句…, 会得到下图信息
参数说明:
- id: 查询的顺序号,有几个 select 就显示几行。id的顺序是按 select 出现的顺序增长的。id列的值越大执行优先级越高越先执行,id列的值相同则从上往下执行,id列的值为NULL最后执行
- selectType: 表示查询中每个select子句的类型
- SIMPLE: 表示此查询不包含 UNION 查询或子查询
- PRIMARY: 表示此查询是最外层的查询(包含子查询)
- SUBQUERY: 子查询中的第一个 SELECT
- UNION: 表示此查询是 UNION 的第二或随后的查询
- DEPENDENT UNION: UNION 中的第二个或后面的查询语句, 取决于外面的查询
- UNION RESULT, UNION 的结果
- DEPENDENT SUBQUERY: 子查询中的第一个 SELECT, 取决于外面的查询. 即子查询依赖于外层查询的结果.
- DERIVED:衍生,表示导出表的SELECT(FROM子句的子查询)
- table: 表示该语句查询的表
- partitions: 如果查询是基于分区表的话,会显示查询将访问的分区
- type: 优化sql的重要字段,也是我们判断sql性能和优化程度重要指标。取值类型从优到劣排序如下:
- system: 表中只有一行记录,相当于系统表;
- const:通过索引一次命中,匹配一行数据;
- eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配;
- ref: 非唯一性索引扫描,返回匹配某个值的所有数据;
- range: 只检索给定范围的行,使用一个索引来选择行,一般用于between、<、>;
- index: 只遍历索引树;
- ALL: 表示全表扫描,这个类型的查询是性能最差的查询之一。 那么基本就是随着表的数量增多,执行效率越慢。
- possible_keys: mysql内部分析后估算可能会用到的索引,仅仅是可能,实际不一定会用到。
- key: mysql 在当前查询时所真正使用到的索引。(possible_keys的子集)
- key_len: 表示查询优化器使用了索引的字节数,这个字段可以看出组合索引是否完全被使用,这也是我们优化sql时,评估索引的重要指标.
例主键id 为 char(32), 一个char为3字节,那么当正确使用 id 索引时, key_len就为 96
- ref: 这一列显示了在key列记录的索引中,表查找值所用到的列或常量,常见的值有:const(常量), 字段名( 即数据库.表名.字段名 )
- rows: mysql 查询优化器根据统计信息,估算该sql返回结果集需要扫描读取的行数,这个值相关重要,一般索引优化扫描读取的行数越少越好
- filtered: 返回结果的行占需要读到的行(rows列的值)的百分比,就是百分比越高,说明需要查询到数据越准确, 百分比越小,说明查询到的数据量大,但需要的数据(结果集)很少
- extra:
- using index:覆盖索引扫描,表示查询在索引树中就可查找所需数据,不用扫描表数据文件,往往说明性能不错。(例 select 后面的字段,都是索引)
- using filesort :表示 mysql 对结果集进行外部排序,不能通过索引顺序达到排序效果。一般有 using filesort都建议优化去掉,因为这样的查询 cpu 资源消耗大,延时大。(例使用 order by 进行排序, order by后面的字段非索引,则很有可能出现 using filesort)
- using temporary:mysql需要创建一张临时表来处理查询。一般出现于排序,分组和多表 join 的情况, 查询效率不高,首先是想到用覆盖索引来优化。
- using where :使用 where 语句来处理结果,并且查询的列未被索引覆盖, 效率低,需要优化。
9.关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过?
慢查询的优化首先要搞明白慢的原因是什么?是查询条件没有命中索引?是load了不需要的数据列?还是数据量太大?
所以优化也是针对这三个方向来的:
- 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载
了许多结果中并不需要的列,对语句进行分析以及重写。 - 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽
可能的命中索引。 - 如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者
纵向的分表。
慢查询配置及学习地址: https://juejin.cn/post/6844903473079648264
10.简述mysql中索引类型及对数据库的性能的影响
- 普通索引: 允许被索引的数据列包含重复的值。
- 唯一索引: 可以保证数据记录的唯一性。
- 主键: 是一种特殊的唯一索引,主键用于唯一标识一条记录,使用关键字 PRIMARY KEY 来创建。
- 联合索引: 索引可以覆盖多个数据列,如像INDEX(columnA, columnB)索引。
- 全文索引: 通过建立 倒排索引 ,可以极大的提升检索效率,解决判断字段是否包含的问题,是目前搜索引擎使用的一种关键技术。可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全文索引。
优缺点:
索引可以极大的提高数据的查询速度。通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要将这些改动写入索引文件。
11.mysql主从同步原理
Mysql的主从复制中主要有三个线程: master(binlog dump thread)、slave(I/O thread 、SQL thread) ,Master一条线程和Slave中的两条线程。
- 主节点 binlog,主从复制的基础是主库记录数据库的所有变更记录到 binlog。binlog 是数据库服务器启动的那一刻起,保存所有修改数据库结构或内容的一个文件。
- 主节点 log dump 线程,当 binlog 有变动时,log dump 线程读取其内容并发送给从节点。
- 从节点 I/O线程接收 binlog 内容,并将其写入到 relay log 文件中。
- 从节点的SQL 线程读取 relay log 文件内容对数据更新进行重放,最终保证主从数据库的一致性。
注: 主从节点使用 binglog 文件 + position 偏移量来定位主从同步的位置,从节点会保存其已接收到的偏移量,如果从节点发生宕机重启,则会自动从 position 的位置发起同步。
由于mysql默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念。
全同步复制
主库写入binlog后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响。
半同步复制
和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回ACK确认给主库,主库收到至少一个从库的确认就认为写操作完成。