Mysql

数据库三范式

  1. 每一列都已经是不可拆分的最小单元
  2. 保证每张表只描述一件事
  3. 不传递非主键的的依赖

事物的四大特性

  1. 原子性 要么执行,要么都不执行
  2. 一致性 事物执行前后,数据从一个有效状态迁移到另一个有效状态,这个有效可以是数据库月数,也可以是我们的业务月数,在ACID中,我们利用AID来保证C
  3. 隔离性 事物之间根据不同的隔离级别具有隔离属性
  4. 持久性 事物一旦提交,就不能再被修改、回滚

事物问题

  1. 脏读 读到其他事物未提交的数据
  2. 幻读 读到其他事物已提交插入的数据,数量不一致
  3. 不可重复读 读到其他事物已提交修改的数据,值不一致

事物隔离级别

  1. 串行化 所有请求串行执行,规避所有事物问题
  2. 可重复读 在同一个事物中能读到相同的数据,规避不可重读和脏读,mysql通过mvcc解决了幻读
  3. 读已提交 只会读到其他事物已经提交的数据,规避脏读
  4. 读未提交 可以读到所有状态数据
  5. readonly(oracle特有事务, 串行化、读已提交、readonly)

Mysql默认事物隔离级别

可重复读。
历史原因跟binlog有关系,以前的binlog是statement形式,就是条sql,在非可重复读场景下会有问题;

binlog模式

  • statement模式,记录每条提交的sql
  • row模式,记录每一行修改后的数据 新版本对row模式也做了优化,并不是所有的修改都会以row 来记录,像遇到表结构变更的时候就会以statement模式来记录
  • mixed模式,在mixed模式下,mysql会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也是在statement和row之间选择一种。

Mysql的引擎类型

  • innodb 支持事务 表锁、行锁、间隙锁
  • myIsam 不支持事务 表锁 适用于只读或写少,数据行少的场景
  • dbd 不支持事务 表锁 页面锁
  • memory 不支持事务 表锁

MyIsam和Innodb的区别

  • MyIsam 不支持事务;表锁;非聚簇索引;存储文件有三个,表结构、索引、数据文件
  • Innodb 支持事物;表锁、行锁;聚簇索引;存储文件两个,表机构、聚簇索引文件

Innodb事务实现

通过transaction命令或者begin命令开启,每一个事务创建的时候会分配一个唯一自增的事务ID,数据行存在一个隐藏字段就是上一次修改此行记录的事务ID

Innodb逻辑存储结构

  • 表空间 共享表空间:undolog、事务 私有表空间:表结构、索引、数据
  • 段Or表
  • 区 物理上连续的几个页
  • 页Or块 16K

text这些大文本是如何存储的

使用溢出页,在表私有空间中,有一些位置存储大型数据比如text,聚簇索引上存储数据开头的一部分数据和这个溢出页的地址

Mysql中的锁

  • 表锁 LOCK TABLES 给表显式加表锁
  • 行锁 在索引上实现的,所以如果要加行锁,必须是走索引查询的语句,否则会降级为表锁
  • 页锁
  • 间隙锁 用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁);可以防止当前读的幻读
  • 读锁 select默认不加读锁,可以使用 lock in share mode;排斥写锁
  • 写锁 update、delete、insert默认加写锁;select for update;排斥读锁和写锁
  • 意向锁 为了同时支持行锁与表锁,在行锁加读锁时,需要先申请表级意向锁,自动的

Mysql中的索引数据结构

B+Tree
Hash
fulltext 全文索引 倒排
RTree 三维空间上的索引,每个节点维护了多个指针指向空间上的相邻点

为什么用B+树

索引很大不能存储在内存,需要存储在磁盘上,这样就会有IO问题,磁盘一次可以预读页数据,要充分利用预读,索引的数据结构要尽量保证在查询过程中减少IO次数,索引存储在逻辑存储结构页上,一页为16k,查找数据过程

B+树在可以在一页上维护2048个节点,两层就可以索引400w行数据(理论上)
B+树的叶子节点上维护了指向上一条和下一条记录的指针,形成一个双向链表,范围查找效果好;

Hash 无法实现范围查找
红黑树 树高问题
B树 每个节点都存储数据,每页上的数据量就会少,就比B+树高

什么是聚簇索引

innodb的文件组成只有两个,一个索引文件、一个数据库描述文件;而数据存储在索引文件中
innodb的table的主键索引的,叶子节点,存储了这行记录的完整数据,以索引查询数据的时候,能直接拿到数据,不需要再去另外的磁盘查询数据,减少IO

Mysql执行流程

查询等过程如下:权限校验—》查询缓存—》分析器—》优化器—》权限校验—》执行器—》引擎
更新等流程如下:分析器----》权限校验----》执行器—》引擎—redo log prepare—》binlog—》redo log commit

Mysql执行计划explain

id 有顺序的编号,查询顺序,有几个select就有几行
selectType 查询类型
table 表名
type 重要标志
>const 索引一次命中,匹配一条记录
>system 表中只有一行记录
>eq_ref 唯一索引扫描,只有一条记录
>ref 非唯一索引扫描返回匹配的某个值
>range 质检所给定范围的行,使用一个索引来选择 一般用于between <>
>index 只遍历索引树
>all 全表扫描
key 使用的索引
rows 估算扫描行数
filtered 有效行与扫描行的百分比 越高越好
extra 扩展信息
>using where 检索过程使用了where过滤 性能好
>using index 使用了覆盖索引 性能好
>using filesort 使用了文件排序,sql语句用到了orderby 但是orderby的列没有索引,性能差
>using temporary 使用临时表保存结果 性能极差 多见于groupby语句

什么是覆盖索引

一次查询使用的索引包含所有返回信息,不需要回表

sql优化

  1. 尽量使用较低的隔离级别;
  2. 精心设计索引, 并尽量使用索引访问数据, 使加锁更精确, 从而减少锁冲突的机会
  3. 选择合理的事务大小,小事务发生锁冲突的几率也更小
  4. 给记录集显示加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁
  5. 不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会
  6. 尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响
  7. 不要申请超过实际需要的锁级别
  8. 除非必须,查询时不要显示加锁。 MySQL的MVCC可以实现事务中的查询不用加锁,优化事务性能;MVCC只在COMMITTED READ(读提交)和REPEATABLE READ(可重复读)两种隔离级别下工作
  9. 对于一些特定的事务,可以使用表锁来提高处理速度或减少死锁的可能

undolog redolog binlog relaylog

undolog 隐藏字段undolog指针,事务修改时,老数据进入undolog,更新数据行上的undolog指针;用于事务回滚,和控制可见性;undolog是一段存储空间,会被覆盖丢弃
redolog 事物执行期间,记录的事物修改的数据,存储A->B;数据库宕机时,回滚进行中的事务
binlog 数据库日志文件,用于记录数据库sql记录,和数据主从同步,只记录已提交的事务
relaylog 从库维护的binlog缓冲,主从同步时,binglog进入relaylog,从库执行relaylog的binlog

当前读和快照读

当前读
当前读指的是读取数据当前最新数据。update、insert、delete、select for update(排他锁)、select lock in share mode。读取数据需要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。

快照读
快照读指的是在读取数据时,生成读取快照,在同一个事物中可能会一直读取此快照的数据。快照读读到的数据可能不是最新的,可能是历史版本的数据,这些历史版本的数据就是从undo log中获取的。
事物中的select 不加锁的情况会执行快照读,快照读依赖readview来实现。
快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读。

MVCC可见性逻辑

多版本并发控制,减少数据库使用过程中的读写冲突,无锁。
ReadView 读视图,维护当前活跃的事物列表trx_list,活跃的最小事务low_limit_id,即将生成的下一个事物up_limit_id,还有一个当前事物trx_id
1.比较trx_id是否小于low_limit_id 或者为当前事物ID,如果为true,则代表修改此行数据的事物早已提交或者就是当前事务进行的修改,当前记录可见。否则进入下一步判断。
2.比较trx_id是否大于等于up_limit_id,如果为true,则代表修改此行记录的事物晚于当前读视图创建,当前记录不可见,根据DB_ROLL_PTR undo log指针找到上一条记录,从新进行可见性分析。否则进入下一步判断
3.判断trx_id是否在trx_list列表中,如果在,代表修改此行记录的事物还未提交,当前事务不可以读取当前记录,根据DB_ROLL_PTR undo log指针找到上一条记录,从新进行可见性分析。否则说明数据在readview生成的时候已经提交,当期事物可以读取当前记录。

事物隔离级别与MVCC

在RR级别下,事物进行快照读时会检查当前事物是否已经创建过ReadView,如果存在,则使用已经创建的,这也是实现可重复读的方法。
在RC级别下,事物每一次快照读都会创建一个新的ReadView,这样就会造成不可重复的和幻读的问题。

Mysql如何解决的幻读

当前读 行锁和间隙锁
快照读 MVCC

Mysql主从同步原理

事务提交之后,由主库记入binlog,并同步给从库,进入从库的relaylog,从库从relaylog中读取binlog,执行sql;
binlog 三种模式,statement rows mixed

replace和insert

replace语句在执行过程中会尝试insert,而insert过程首先会检查主键或唯一索引约束,insert语句的insert过程一旦检测到冲突会直接抛出异常,replace语句的insert过程检测到冲突会选择进行delete或直接update操作;
repace语句在执行insert过程中如果检测到主键冲突,则会删除冲突数据然后进行插入操作;如果检测到唯一约束冲突,则直接进行update操作;
Mysql对replace和insert语句有优化,多条insert操作会合并成insert into table values(),(),()…… binlog中只有一条;多条replace操作会合并成replace into table values(),(),()…… binlog中只有一条;洗数据时尽量使用这两种语法,可以有效降低主从延迟

Mysql中如何实现分页

limit offset,size 语法,会有深度分页问题,越来越慢;推荐使用 >pxId limit 1000的写法,会利用主键索引

count(*) count(1) count(col)区别

count() 和count(1) 都是统计行数,而count(col) 是统计col列非null的行数
官方文当说count(
) 和count(1)的优化是一样的
count(col)慢是因为 要从索引树中拿出来所有的该列判断是否为空,没有索引更惨,要全表扫描
反正用count(*)就对了

你可能感兴趣的:(Mysql)