Mysql的学习笔记

Mysql的ACID靠什么保证?

A 原子性 Atom

要么都成功,要么都失败。

爸爸给儿子转账,爸爸转出成功,儿子就得接收成功,爸爸转出失败,儿子就接收失败。

Undo Log 日志保证,它记录了回滚日志,事务回滚时,撤销已经执行成功的SQL

C 一致性 consistency (C这是目的)

由其他三大特性保证。

爸爸给儿子转账,不管转出成功还是转出失败,爸爸账户+儿子账户的总金额不变

I 隔离性 Isolation

由MVCC保证

D 持久性 Duration

内存 + redo log 保证,Mysql修改数据同事再内存和redo log记录这次操作,down机的时候,直接用 redo log 恢复。

  • undo log(回滚日志):是 Innodb 存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和 MVCC
  • redo log(重做日志):是 Innodb 存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复
  • binlog (归档日志):是 Server 层生成的日志,主要用于数据备份和主从复制

InnoDB redo Tog 写盘,InnoDB 事务进入 prepare 状态。

如果前面 prepare 成功,binlog 写盘,再继续将事务日志持久化到 binlog,

如果持久化成功,那么 InnoDB 事务则进入 commit 状态(在 redo log 里面写一个 commit 记录)

redolog 在系统空闲的时候去刷盘

Explain

type

列表示了查询的访问类型,它指示了数据库是如何访问表的。下面是一些常见的 type 类型:

  1. ALL: 扫描整个表。
  2. index: 扫描整个索引。
  3. range: 使用索引选择范围内的行。
  4. ref: 使用非唯一索引或索引前缀来查找行。
  5. const, system: 在索引中找到常数值或者只有一行数据的情况。
  6. eq_ref: 使用索引进行唯一性查找。
  7. NULL: 某些特殊情况下的表示。

从最有效率到最低效率的排序是:const/system > eq_ref > ref > range > index > ALL > NULL。

extra 

  1. Using where: 表示查询使用了 WHERE 条件来过滤行。

  2. Using index: 表示查询使用了覆盖索引,即查询可以直接从索引中获取所需数据而不需要访问实际数据行。

  3. Using index condition: 表示查询使用了覆盖索引条件,即查询的条件部分可以直接通过索引来满足,无需访问实际数据行。

  4. Using filesort: 表示查询需要进行排序操作,但是无法使用索引完成排序,因此需要额外的文件排序操作。

  5. Using temporary: 表示查询需要使用临时表来存储中间结果,通常发生在排序、GROUP BY 或 UNION 查询中。

  6. Using where with pushed condition: 表示查询优化器将部分条件推送到存储引擎执行,以减少从存储引擎获取数据所需的行数。

  7. Using join buffer: 表示查询使用了连接缓冲区进行连接操作。

  8. Range checked for each record: 表示对于每个记录都进行了范围检查,可能是因为查询中使用了范围条件,需要逐条记录进行检查。

  9. Using filesort、Using temporary: 当这两个值同时出现时,通常表示需要在临时表上进行文件排序。

InnoDB 与 MyInsam

1、Innodb与MyIsam的文件结构?

Myism物理文件结构为:
.frm文件:myisam frame与表相关的元数据信息都存放在frm文件,包括表结构的定义信息等
.myd文件:myisam data,用于存储myisam表的数据
.myi文件:myisam index,用于存储myisam表的索引相关信息

Innodb的物理文件结构为:
.frm文件:与表相关的元数据信息都存放在frm文件,包括表结构的定义信息等
.ibd文件和.ibdata文件:
这两种文件都是存放innodb数据的文件,之所以用两种文件来存放innodb的数据,是因为innodb的数据存储方式能够通过配置来决定是使用共享表空间存放存储数据,还是用独享表空间存放存储数据。

独享表空间存储方式使用.ibd文件,并且每个table一个ibd文件
共享表空间存储方式使用.ibdata文件,所有table共同使用一个ibdata文件

觉得使用哪种方式的参数在mysql的配置文件中 innodb_file_per_table

InnoDB 中的独立表空间和共享表空间

  1. 独立表空间(Individual Tablespaces):

    • 每个表一个表空间: 每个 InnoDB 表都有其自己的独立表空间,用于存储该表的数据和索引。
    • 优点: 独立表空间提供了更好的灵活性和管理性。它允许对每个表的表空间进行单独的备份、恢复、优化和监控,使得针对某个特定表的维护更加灵活。
    • 缺点: 管理多个表空间会增加管理负担,特别是在管理大量表的数据库时可能会变得复杂。
  2. 共享表空间(Shared Tablespace):

    • 多个表共享一个表空间: 多个 InnoDB 表共享同一个表空间,这个表空间用于存储这些表的数据和索引。
    • 优点: 共享表空间减少了管理的复杂性,节省了磁盘空间,并且在一些情况下可以提高磁盘利用率。
    • 缺点: 共享表空间可能会导致某些表的性能问题影响其他表,特别是当某个表的数据量较大或者频繁进行写操作时,可能会对共享表空间中其他表的性能产生影响。

对于我来说,实际应用场景迁移数据的需求比较多,所以选择独立表空间!

2、Innodb 与MyIsam的简单总结对比?

InnoDB 特点:

  • 支持事务和外键约束。
  • 支持行级锁定,可以提高并发性能。(MyIsam是表级别锁定)
  • 支持崩溃恢复机制,可以保证数据的可靠性。
  • 支持多版本并发控制(MVCC)机制,可以提高并发性能和可伸缩性。
  • 适合于读写并发的场景,对于需要频繁更新的数据表,InnoDB 的性能比 MyISAM 更高。(MyIsam 适合于读多写少的场景,对于只读的数据表,MyISAM 的查询性能比 InnoDB 更高。)

3、Innodb 与 MyISam 主键索引与其他索引的区别?

  • innodb的主键index树的叶子节点,存放的是数据。 MyIsam存的是数据的物理地址。数据和索引放在一起,叫做聚簇index,MyIsam那种叫做非聚簇index
  • innodb的其他索引树,叶子节点存的是主键id,myIsam的二级索引树,叶子节点存的和主键索引树一样,都是物理地址。(innodb数据移动的时候,二级索引的叶子节点不需要改动,因为存的是主键id,MyIsam就全部都要改动)
  • innodb一定要有主键,因为二级索引存的是主键key,根据主键key再去叶子节点找数据。(InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。)

MyISam 的主键索引和二级索引

Innodb 的主键索引和二级索引树

表结构变更

1、innodb,Alter table 改字段类型,底层会经历什么过程?

  1. 创建一个新的临时表,该表的结构与原表相同,但修改了目标字段的数据类型。
  2. 将原表中的数据逐行复制到临时表中,同时将目标字段的数据类型进行转换。
  3. 在复制数据的过程中,如果发现某行数据无法转换为目标数据类型,则会将该行数据的目标字段设置为 NULL。
  4. 复制完成后,使用 RENAME TABLE 命令将原表重命名为备份表,将临时表重命名为原表。
  5. 删除备份表。

需要注意的是,如果原表中存在索引,则在修改字段类型后需要重新创建索引。

2、Alter table 改字段类型 ,需要注意哪些问题?

  • db 磁盘空间可能不够,因为要新建table和索引

Index 数据结构

上图表示两条记录,1111a 和 2222b

CREATE TABLE tableA (
    a INT PRIMARY KEY,
    b INT,
    c INT,
    d INT,
    e VARCHAR(255),
    INDEX bcd (b, c, d)
);
INSERT INTO tableA (a, b, c, d, e) VALUES
(1, 1, 1, 1, 'a'),
(2, 2, 2, 2, 'b'),
(3, 3, 2, 2, 'c'),
(4, 3, 1, 1, 'd'),
(5, 2, 3, 5, 'e'),
(6, 6, 4, 4, 'f'),
(7, 4, 5, 5, 'g'),
(8, 8, 8, 8, 'h'),

2、下图这两条记录表示一个Page里面,有2条record,这俩record相互链接在一起

1、没有索引能找到数据吗?

可以呀,全部记录过一遍(从左往右一个一个Next比对),也叫全表扫描

 select * from tableA where id = 5;

有了索引之后,你找数据就更快(从上往下,根据index找到数据,b+树下基本就2-4层)


 

2、为什么Leaf节点是双向链表?

关注下叶子节点,其实指针是双向的链表,

这种双向链表结构有助于在查询过程中进行范围扫描和范围查询操作,因为它允许数据库系统按照顺序轻松地遍历叶子节点,而不需要做额外的树遍历

数据来源:https://ftp.unpad.ac.id/orari/library/library-sw-hw/linux-howto/mysql/internals-en.pdfzicon-default.png?t=N7T8https://ftp.unpad.ac.id/orari/library/library-sw-hw/linux-howto/mysql/internals-en.pdf

 如果是select * from tableA where a > 6,那么他会先找到6,然后走6的next,入下图所示

如果是select * from tableA where a < 6 ,以此类推

4、为什么非得最左前缀才能用到索引?

select * from tableA where b=1 and d=1 

那么从bcd的联合索引树上,你可以知道肯定是往左边找,因为b=1,于是能找到b=1的数据,那如何判断d这个数据呢,其实是在bcd索引叶子节点上上直接对比,不用去回表找到那些满足b=1的数据,再去比对,这样就能够减少回表的次数(比如上面的语句,在bcd索引上,就能发现只有一条record满足条件)

注意下图的 Using index condition:

"Using index condition" 在 MySQL 的 EXPLAIN 查询结果中表示查询使用了覆盖索引条件来执行,这意味着查询的条件部分可以直接通过索引来满足,而不需要额外地访问实际的数据行。

EXPLAIN 结果显示 "Using index condition" 时,这通常是优化器利用了索引的一部分或全部来过滤行,而不需要访问数据本身。这可以提高查询性能,因为它避免了对实际数据行的读取,只需使用索引数据即可满足查询条件。

假如 select * from tableA where c=1 and d=1 ,在没有b的情况下,你压根不知道走左分支还是右分支,也就是不符合最左前缀规则!

那大于小于语句能走index不?

select * from tableA where b>1

select * from tableA where b<1

可以呀,原理一样,能用到树。

5、走索引一定比全表扫描快?(range查找导致索引失效)

你非要这么问,那就不一定

 type= ALL 表明全表扫描

因为 select * from tableA where b>1 ,你先走了bcd联合索引树,再去回表找到数据,发现数据就在主键那棵树Leaf节点的第一条,那不如直接全表扫描

但是把b>1换成b>5,他就走了索引了,如下图

type=range 表明范围查找

6、覆盖索引

1、select *  

如图所示,全表扫描了,因为他觉得全表扫描比走索引树然后回表的速度快。

2、 select * 改成 select b,c,d 就走了索引

因为要返回的数据都在b,c,d的联合索引树上,不用回表,这种场景叫做覆盖索引

3、改成 select a, b,c,d 为什么也走了覆盖索引?

因为 bcd覆盖索引上存了主键a ,也不用回表

4、假如 select e ,因为e也需要回表,所以全表扫描

5、不使用Where,查b,c,d 也走了索引

走了索引的意思,不是说一定要从树的Root往下找数据,从索引的叶子节点扫描得到数据也是用了index!!!

6、不使用 Where 查 a,也是走的联合索引bcd

为什么都能查到数据,但是为什么要走覆盖索引?

因为一页page是16KB,

  • 比如索引占用16byte,那么一个page能放1000个数据,
  • 比如真实数据要放1KB,一个page只能放16个数据,

那么走索引明显要翻的Page要少很多,也就是磁盘IO要减少很多

咱们知道主键a,有一颗b+树,但是那棵树就是回表的那棵树,叶子节点很大的!!!

7、不使用Where 查 e ,需要全表扫描

7、Order by 走不走索引?

走不走索引看你sql怎么写!

如图所示,假如你走bcd联合索引,能把数据返回直接返回,不需要回表,那么就走。

上图的bcd联合索引,天生就是排好序的!

1、找的内容都在索引上!select a,b,c,d from tableA order by a,b,c

2、找的内容要回表!select e from tableA order by a,b,c; (或者 select * )

找出数据,然后去内存里面去排序

8、类型转换导致索引失效

select 'a' = 0 
select 'b' = 0
select 'z' = 0

基础知识:

  1. 非数值字符遇到int类型,都会转成数值 0
  2. 数值字符遇到int类型,都会转成数值本身

where条件中如果是非数值字符,会导致索引失效(也就是不走索引)

因为非数值类型字符'a'会被转换成0,等价于

explain select * from tableA where a = 0;

但是a不可能是0,所以extra出现,impossible

MySQL 的 EXPLAIN 输出中,出现 "Impossible WHERE noticed after reading const tables" 这个提示通常表示优化器在执行查询时遇到了一个不可能的 WHERE 条件。这个提示可能是 MySQL 优化器进行查询计划优化时的一种警告或记录。

这种情况通常发生在以下情况下:

  • 当使用了某些条件下的索引,但是这些条件实际上不可能满足时,MySQL 会报告这个警告。这可能是因为 WHERE 子句的某些部分与使用的索引或表达式不兼容,导致了不可能的条件组合。
  • 在使用了某些类型的连接(例如 INNER JOIN、LEFT JOIN 等)并且应用了条件时,优化器可能会发现某些条件组合下的结果是不可能存在的。

下面举一个类型转换索引失效得的例子~

先给Varchar类型的字段e添加索引

语句一:where e = 1  #提示:e本身是varchar类型

语句二:where e = '1' #提示:e本身是varchar类型

where e = 1; e本来类型是 varchar,你来个 where = 1,也就是把表里面的e字段的字符全部改成 0, 然后再去比对。

所以不会走索引

9、数据操作导致索引失效

语句一:where a = 1; # a是int类型的主键

语句二:where a + 1 = 1; # a是int类型的主键

只要操作了数据,那么就不走索引

10、主键索引和其他索引Leaf节点存的数据有什么区别?

  1. Primary Key主键索引的叶子节点存的是数据(或者说是数据的物理地址)
  2. 其他索引的叶子节点存的是主键

如上图所示,bcd字段联合索引,Leaf节点存的是主键a,比如语句

select * from tableA where b = 1 and c = 1 and d= 1;

找到a =1的数据,然后再去主键树上去搜,这种叫回表

但是如果 select b from tableA where b > 1;  (我只要看下我table里面有几个b满足条件)或者

select a,b,c,d from tableA where b > 1; (因为你都能拿到)

这种直接能从索引数据上就找到结果的,叫 覆盖索引

11、mysql为什么要用b+树,不用b树?

b树与b+树演示网站:https://www.cs.usfca.edu/~galles/visualization/BTree.html

1、B+树非Leaf 节点不存储数据,只存储索引,record记录都存在了叶子节点下(那么检索数据的时候,可以都在Leaf节点找到,不用去树里面查找)

2、B+树,Leaf 节点之间用指针链接,B树没有。于是B+树可以支持范围查询和排序操作,也更合适内存缓存。

综上所述,B+ 树相对于 B 树具有更好的磁盘存储、范围查询、内存缓存和高并发性能,更适合作为 MySQL 的索引结构。

图1是一个简单的B-Tree。

图2是一个简单的B+Tree示意。

  1. 范围查询和顺序访问效率高:B+树的叶子节点形成有序链表,适合范围查询和顺序访问。范围查询只需遍历叶子节点链表即可,而B树则可能需要在非叶子节点上进行部分匹配。

  2. 磁盘访问性能更优:B+树内部节点只包含键值信息,叶子节点指向具体数据,减少树的高度,提高磁盘访问效率。

  3. 适合大规模数据存储:B+树结构简洁,适合处理大规模数据,叶子节点的有序链表减少了范围查询的时间。

  4. 有利于查询效率:B+树具有更短的查找路径,叶子节点是数据存储节点,提高了查询效率。

  5. 维护索引代价较低:B+树的分裂与合并通常只涉及叶子节点,减少了维护索引的开销。

总体来说,B+树在范围查询、顺序访问、磁盘访问性能以及大规模数据存储方面有更优秀的表现,因此适合作为MySQL索引结构的选择。而B树也有其优点,更适合于随机查找,但在数据库系统中,B+树更符合多数查询的特性和优化需求。

1、 b+树的节点存的是什么?

看下这个图,上面这个 1、5、15、37,就类似于底下的0005 0009

简单理解:

  1. 假如的id类型是bigint 。
  2. 上面这个节点中的1,主要是一个bigint值+所指向的最底下叶子节点的指针
  3. 假如bigint值8个byte,指针和其他信息占用8个byte的话,那么就是16个byte,那么
  4. 16kb / 16b = 1000个数据。也就是这个情况下,非叶子节点可以管理 1000 个Page

注意:

  • 非 Leaf 节点,不放record(就放index和指针)
  • Leaf 节点,放record

比如下图,我想找id = 50的数据,那么我通过二分查找发,通过非叶子节点定位到具体的Page,再从Page中找到具体的record

2、 三层B+树结构,能存多少record?

分析如下:

  1. 假如是3层,那么上面两层是索引层,下面层是 record层
  2. 每一个Node的大小是Page,默认16KB
  3. 假设一个index占用16个byte,那么一页可以放 index 1000 = 16kb / 16b (可能不止存id值,还存点其他信息,比如指针)
  4. 第1层1000个index,第二层就是 1000 * 1000
  5. 假如每条 record 是1kb大小的话,那么每一Page可以放16条 record
  6. 也就是3层B+树结构,可以放 1000 * 1000 * 16 条 record,也就是1600w

锁相关

锁粒度分类:

  1. 行锁: 锁某行数据,锁粒度最小,并发度高
  2. 表锁: 锁整张表,铁粒度最大,并发度低
  3. 间隙锁: 锁的是一个区间(避免幻读,避免range内插入数据)

还可以分为:

  1. 共享锁: 也就是读锁,一个事务给某行数据加了读锁,其他事务也可以读,但是不能写
  2. 排它锁: 也就是写锁,一个事务给某行数据加了写锁,其他事务不能读,也不能写

还可以分为:

  1. 乐观锁: 并不会真正的去锁某行记录,而是通过一个版本号来实现的 CAS思想
  2. 悲观锁:上面所的行锁、表锁等都是悲观锁

在事务的隔离级别实现中,就需要利用锁来解决幻读

Mysql 优化

1、检查是否走了索引

  1. 没index,那么根据业务场景加上合适的index
  2. 有index,不走index,那么看下为什么index失效

2、检查所利用的索引,是否是最优索引

比如商品表 tb_product ,有字段门店id—wmPoiId与商品id—spuId

假如你的索引是两个单独的 ix_poidi 与 ix_spuId,

针对a业务场景:where poiid = 123 或者  where spuId = 333 这情况是高效的

针对b业务场景:where poiId = 123 and spuId = 333 这个情况是需要回表扫描数据的,

具体怎么加索引,具体得看你的业务场景,如果b场景是高频的,那么建议加上联合索引 ix_poiId_spuId

3、检查所查字段是否都是必须的,是否查询了过多字段

别每次都 select * from table A ;

比如你只想要根据门店id查询出所有的商品id,那就直接 select spuId from product 即可。

如果能用上覆盖索引,那么翻页Page次数就少了,磁盘IO也就少了,可以提高性能。

4、数据清理、大表治理

数据 = 真实数据 + 索引数据

1、保留一定时间的数据

可以与业务方协商,看看是否有必要保留当前表内所有数据,如果可以只保留半年的,那么可以把半年前的数据都从mysql迁移到hive去归档,然后根据 ctime 或者id,把过期数据给清除掉(清除数据过程记得考虑并发度,若有分表情况,最好一个table清零完再清零另外一个表,分db可以并行,同db串行,别对DB造成写压力),删完数据记得治理表空洞可以设置定时任务,每天晚上0点去删数据

2、考虑删除无用索引

如果商品表 tb_product 这个表,里面有个字段 isValid 代表数据有效还是无效,他的值只会是 0 或者 1 ,0表示无效,1表示有效,

针对只包含少量离散值(比如只有两个可能取值)的字段,没有必要加索引

索引的作用在于帮助数据库快速定位到符合特定条件的行。对于包含少量离散值的列,如 isValid 这样只有两个可能值的情况,索引并不会提供太大的性能提升,因为对于两个不同值的行,扫描整个表和使用索引来定位它们的开销差别通常不大。

另外,如果表中大部分行的 isValid 值都是一样的(比如绝大多数都是 1 表示有效),那么即使添加了索引,数据库也可能选择不使用该索引,因为它并不能显著地提高查询性能。

总的来说,对于仅包含极少离散值的字段,如 isValid 这种只有两个值的情况,添加索引的必要性较低。索引的选择应该基于表的其他字段和查询的模式来决定,以确保更有效地提升查询性能。

5、分库分表

如果是真实数据过多,那么可以考虑分库分表

水平切分

分N个库,每个库分N个Table (Table结构一模一样)

  • 根据业务线切库(比如加前缀,tmall_product , taobao_product )
  • 根据数据量去切库(加后缀,tmall_product[0-3],, taobao_product[0-3],假如切了4个库)
路由规则

关于分库分表字段(具体得看你的业务场景,比如你属于商品业务,查询商品的时候,每次都会带上门店id,那么就根据门店id去分片)

路由规则可以自己定制化,分片字段不一定要是主键id,可以是其他业务字段~

分布式唯一Id

  • 第1位占用1bit,其值始终是0,可看做是符号位不使用。
  • 第2位开始的41位是时间戳,41-bit位可表示2^41个数,每个数代表毫秒,那么雪花算法可用的时间年限是(1L<<41)/(1000L360024*365)=69 年的时间。
  • 中间的10-bit位可表示机器数,即2^10 = 1024台机器,但是一般情况下我们不会部署这么台机器。如果我们对IDC(互联网数据中心)有需求,还可以将 10-bit 分 5-bit 给 IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,具体的划分可以根据自身需求定义。
  • 最后12-bit位是自增序列,可表示2^12 = 4096个数。

这样的划分之后相当于在一毫秒一个数据中心的一台机器上可产生4096个有序的不重复的ID。但是我们 IDC 和机器数肯定不止一个,所以毫秒内能生成的有序ID数是翻倍的。

垂直切分

垂直切:把1个Table的部分字段拆出来,单独成一个表

(比如商品product表里面一开始存了商品视频url ,但是后来因为要给每个视频加尺寸、底色、清晰度等一系列图片相关的字段,那么切一个 table_video 表出来,单独存放video信息是最好的,然后 product 那边就放一个 videoId)

比如针对业务商品业务来说,对于不同的业务线(业务的数据隔离),可以分不同的database,这些database还可以按需求分成4个或者10个,然后再去database分100个表或者1000个表。(根据自身业务需求来)

淘宝商品分10个database

taobao_product_db_0
taobao_product_db_1
taobao_product_db_2
taobao_product_db_3
taobao_product_db_4
taobao_product_db_5
taobao_product_db_6
taobao_product_db_7
taobao_product_db_8
taobao_product_db_9

天猫商品分10个database
tmall_product_db_0
tmall_product_db_1
tmall_product_db_2
tmall_product_db_3
tmall_product_db_4
tmall_product_db_5
tmall_product_db_6
tmall_product_db_7
tmall_product_db_8
tmall_product_db_9


每个database中放100个table,
比如 taobao_product_db_0 放 product_[0-99]
比如 taobao_product_db_1 放 product_[100-199]
比如 taobao_product_db_9 放 product_[900-999]

自行根据业务场景去进行分片,比如我每次请求都会带上门店id,那么
路由到database : poiId % 10
路由到table : poiId % 1000

6、升级机器硬件

  1. 提高内存:

    • 缓存效率提升: 更多的内存可以用于缓存数据库中的数据和索引,这可以降低对磁盘的频繁访问。缓存命中率提高后,查询数据将更多地从内存中获取,加快查询速度。
    • 更大的缓冲池: 可以增加 InnoDB 缓冲池的大小,从而提高了缓存的命中率,加速对数据的访问。
  2. 升级CPU:

    • 加速查询处理: 更快的 CPU 可以加速查询的执行,尤其是在进行复杂计算、聚合操作或大量数据排序时。这会直接影响查询的响应速度和系统的处理能力。
    • 并发处理能力提升: 更快的 CPU 可以更有效地处理并发请求,降低查询排队的时间,提高数据库的并发处理能力。
  3. 增加磁盘性能:

    • 提高磁盘 I/O 速度: 更快的磁盘(例如使用固态硬盘 SSD)可以大幅提升数据库的读写速度,尤其是在需要大量磁盘 I/O 操作时,如排序、索引创建、大型查询等。
    • 提高数据持久性: 更快的磁盘可以加速数据持久化操作,如写入日志、执行备份等,从而提高系统的稳定性和可用性。
  4. 带宽:

    • 影响远程访问速度: 如果 MySQL 数据库是通过网络进行远程访问的,提高带宽可以加快远程客户端与数据库之间的数据传输速度。特别是在大数据量的读写操作时,更高的带宽可以显著提高远程访问的效率。
  5. 网卡:

    • 提高网络处理能力: 更快的网卡可以提高服务器的网络处理能力,减少网络延迟,尤其是在高并发情况下。这对于需要大量客户端连接或频繁的网络传输操作的数据库非常重要。

MVCC

1、什么是MVCC

MVCC(Multi-Version Concurrency Control)是MySQL中用于管理并发访问的一种机制,旨在实现多个事务并发执行而不会相互干扰,从而保障数据的一致性和隔离性。MVCC允许每个事务看到数据的一致性快照,使得事务之间可以并行执行,提高了数据库的性能和并发能力。

在MVCC机制中,每条记录都有一个版本号,记录的创建时间和删除时间决定了它的版本号。当事务开始时,系统会为事务分配一个唯一的事务ID。在事务执行期间,事务可以读取记录的旧版本,但不能读取其他事务尚未提交的更新。

以下是MVCC的核心概念和工作原理:

  1. Read View(读视图): 当事务启动时,系统会为该事务创建一个读视图。读视图确定了事务可以看到的记录版本。只有在事务开始时已经存在的记录才会出现在读视图中。

  2. 版本链: 每条记录都可以有多个版本。在插入一条新记录或者删除一条记录时,不是直接删除或覆盖,而是在记录的版本链中新增一个版本。这样在事务中读取数据时,可以根据事务的读视图找到合适的版本。

  3. 并发控制: 在并发执行时,事务需要根据自己的读视图来确定可以读取哪些版本的数据。

  4. 回滚段: MVCC需要为每个事务维护一个回滚段,用于存储事务所做的修改,以便在回滚时使用。

总之,MVCC允许不同的事务同时读取和修改同一张表的数据,而不会相互干扰。每个事务都能够看到一致性的快照数据,保证了事务的隔离性和数据的完整性。MySQL的InnoDB存储引擎就是通过MVCC机制来实现并发控制的。

执行一条语句是否自动提交事务?

由 autocommit 参数决定的,默认是开启。所以,执行一条 update 语句也是会使用事务的。

通过读视图 Read View + undo log的版本链 实现 MVCC

trx_id: 用来存储每次对某条聚族索引记录进行修改的时候的事务id。
roll_pointer: 每次对哪条聚族索引记录有修改的时候,都会把老版本写入undo日志中。这个roll pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本)

2、读读、读写、写写三个并发场景,结合MVCC,解释下MVCC是如何发挥作用的

3、rc 级别的 mvcc 和 rr 级别的mvcc 有啥区别?

Read Committed(RC)和 Repeatable Read(RR)都是支持多版本并发控制(MVCC)机制的事务隔离级别,它们之间的区别在于事务的可见性和并发性

对于「读提交 RC」和「可重复读 RR」隔离级别的事务来说,它们的快照读(普通 select 语句)是通过 Read View + undo log 来实现的,它们的区别在于创建 Read View 的时机不同:

  • 「读提交 RC 」隔离级别是在每个 select 都会生成一个新的 Read View,也意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。
  • 「可重复读 RR」隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View,这样就保证了在事务期间读到的数据都是事务启动前的记录。

这两个隔离级别实现是通过「事务的 Read View 里的字段」和「记录中的两个隐藏列(trx_id 和 roll_pointer)」的比对,如果不满足可见行,就会顺着 undo log 版本链里找到满足其可见性的记录,从而控制并发事务访问同一个记录时的行为,这就叫 MVCC(多版本并发控制)。具体的实现可以看我这篇文章:

Mysql 主从复制

ySQL 的主从复制依赖于 binlog ,也就是记录 MySQL 上的所有变化并以二进制形式保存在磁盘上。复制的过程就是将 binlog 中的数据从主库传输到从库上。

这个过程一般是异步的,也就是主库上执行事务操作的线程不会等待复制 binlog 的线程同步完成。

MySQL 主从复制过程

MySQL 集群的主从复制过程梳理成 3 个阶段:

  • 写入 Binlog:主库写 binlog 日志,提交事务,并更新本地存储数据。
  • 同步 Binlog:把 binlog 复制到所有从库上,每个从库把 binlog 写到暂存日志中。
  • 回放 Binlog:回放 binlog,并更新存储引擎中的数据。

具体详细过程如下:

  • MySQL 主库在收到客户端提交事务的请求之后,会先写入 binlog,再提交事务,更新存储引擎中的数据,事务提交完成后,返回给客户端“操作成功”的响应。
  • 从库会创建一个专门的 从库 I/O 线程,连接主库的 log dump 线程,来接收主库的 binlog 日志,再把 binlog 信息写入 relay log 的中继日志里,再返回给主库“复制成功”的响应。
  • 从库会创建一个用于回放 binlog 的SQL线程,去读 relay log 中继日志,然后回放 binlog 更新存储引擎中的数据,最终实现主从的数据一致性。

在完成主从复制之后,你就可以在写数据时只写主库,在读数据时只读从库,这样即使写请求会锁表或者锁记录,也不会影响读请求的执行。

MySQL 主从架构

MySQL 主从复制还有哪些模型?

主要有三种:

  • 同步复制:MySQL 主库提交事务的线程要等待所有从库的复制成功响应,才返回客户端结果。这种方式在实际项目中,基本上没法用,原因有两个:一是性能很差,因为要复制到所有节点才返回响应;二是可用性也很差,主库和所有从库任何一个数据库出问题,都会影响业务。
  • 异步复制(默认模型):MySQL 主库提交事务的线程并不会等待 binlog 同步到各从库,就返回客户端结果。这种模式一旦主库宕机,数据就会发生丢失。
  • 半同步复制:MySQL 5.7 版本之后增加的一种复制方式,介于两者之间,事务线程不用等待所有的从库复制成功响应,只要一部分复制成功响应回来就行,比如一主二从的集群,只要数据成功复制到任意一个从库上,主库的事务线程就可以返回给客户端。这种半同步复制的方式,兼顾了异步复制和同步复制的优点,即使出现主库宕机,至少还有一个从库有最新的数据,不存在数据丢失的风险(假如1主10从,只要一个从库ack了,那么就OK了,兼顾了性能和数据安全)

CRUD 相关

1、select 每次从磁盘加载多少数据出来?

每次加载一页数据出来,每页的大小:

innodb_page_size = 16384 = 16KB (Mysql启动的时候设置的)

页目录的作用,可以避免进行全表扫描,可以根据二分查找先找到页目录,再去找到对应的数据区域。(这思路和kafka中segment的稀疏索引思路一样)

  • page构成结构:

  • 除数据外ROW额外信息存在哪些:

2、主键id 是用自增还是UUID?

推荐使用自增Id,原因如下

1、自增可以避免Page内部数据的调整。(每条record放在Page里面,假如不是自增的,那么需要调整数据)
2、自增可以避免B+树的调整。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值