-
MYISAM / INNODB 的区别
-
MYSQL(5.1的版本之后)的默认存储引擎 INNODB
-
innodb 支持粒度更小的锁,也就是行锁
-
innodb 支持事务
-
-
聚簇索引和非聚簇索引
MyISAM 是非聚簇索引,innodb是聚簇索引。 聚簇索引和非聚簇索引的一个根本的区别是,索引和真正的数据是否是存放在一起的。如果是在一起,就是聚簇索引,如果不在一起,就是非聚簇索引。
- 聚簇索引的优缺点:
由于聚簇索引的主键和数据是存放在一起的,当查询数据的时候,找到索引就找到了数据,避免了磁盘随机io。
缺点是:
- 对于插入数据的场景,如果插入的数据不是顺序插入,那么就会导致页分裂。
- 对于更新索引key的场景,会导致数据移动。这个也是不允许更新主键key值的原因。
- 对于二级索引的查询,聚簇索引需要访问一次索引找到key,回表查询一次。
-
事务的特性
ACID 是什么意思?
其中,一致性表示的是 事务只有起始状态和结束状态可以被其他事务看见,比如说,事务是 给 A -100元,给B + 100 元。那么,不一致的情况就是只给A 减去了100元,没有给B 加上100元。
-
事务的隔离等级
四个隔离等级
-
读未提交 (READ-UNCOMMITTED)
事务可以读到其他事务修改过(但是未提交的数据)
-
读提交 (READ-COMMITTED)
事务可以只能读到其他事务提交过后的事务
-
可重复读 (REPEATED-READ)
在一个事务中,多次读取一个变量,它的数值是一样的。
(事务A在开始的时候,查询了一次变量a=100。然后,事务b启动,修改了一次变量a=200,然后提交。然后,事务A还没有结束,然后,继续查询了一次变量a。这个变量是多少呢? 如果隔离等级是读提交,这个a是=200;如果隔离等级是可重复读,这个a=100。因为一个事务中,读到的变量是一样的。)
-
串行化 (SERIALIZABLE)
最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
举个例子:
-
如果事务的隔离等级是 :读未提交 那么,事务B将变量的值从1修改为2,事务A在v1的值=2。(事务A读到事务B修改但是未提交的东西。)
-
如果事务的隔离等级是:读提交 那么,事务B将变量的值从1修改为2,事务A在v1的值=1,V2=2。(事务A读到事务B修改但是未提交的东西。)
-
如果事务的隔离等级是: 可重复读 那么,事务A读到的v1=1,然后,事务b把1修改为2,然后提交。事务a在查询的时候,为了保证可重复读,那么一定要等于v2=1。(保证一个事务中读到的变量是一样的)
-
如果事务的隔离等级是:串行化 那么事务a查询的时候,加上读锁。事务b修改的时候,试图加写锁。然后被block。等待事务a结束,释放读锁,然后事务b继续执行。
隔离等级试图解决下面的几个问题:
-
脏读
(读未提交的情况下,会出现脏读)
脏读指的是:一个事务执行语句a插入一条数据,且并没有提交,然后再执行另外一条语句b修改回去。另外一个是事务分别语句a和语句b执行之后查询两次,读到的数据是不一样的。
-
不可重复读
(读提交的情况下,会出现不可重复读)
一个事务插入一条数据,然后再commit。另外一个事务分别在commit之前读和commit之后读,得到的结果是不一样的。
-
幻读
(可重复读的情况下,会出现幻读的情况)
在一个事务a,第一次查询某条记录,发现没有。这个时候,事务b插入一个记录。当事务a再次查询的时候,发现记录数量和上次不同。
不可重复读是读取了其他事务更改的数据,针对update操作。幻读是读取了其他事务新增的数据,针对insert和delete操作。
-
-
常用数据库的隔离级别:
MySQL的隔离级别是可重复读(第三级别的等级) mysql的级别要更高一点哎 ~
-
参考:
什么时候分库,什么时候分表?
基本的思路是谁是热点拆分谁,如果有一张表,它的数据记录很多,大量的查询和修改都落在这张表上,那可以分表;如果是数据库的查询QPS过高,或者是连接数过高,那么可以分库。
* 分表但没有分库 : 表的数据记录过多,读写遇到瓶颈。
(数据量太多 读的时候 遇到扫描全表的case,就会很慢)
(遇到表锁的case,写入的事务会block其他事务,导致写入慢)
* 分库但没分表
单个数据库的连接过多,qps太高
-
什么是mvcc?
多版本并发控制 (multi version concurrency control), mvcc 的目的是解决多个事务并发控制。(这本质上也是一种乐观的锁,如果采用悲观的锁,那就是每个事务不允许并行,全部串行执行,这样一定不会出现任何事务隔离等级的问题。
-
mvcc 实现的原理
mvcc给每一行记录(包括被删除的记录)都增加几个隐藏字段,分别是修改的事务trx_id, undo_log_ptr 指针(指向前面的修改记录),是否删除的标记。
通过这个几个隐藏字段携带的信息,innodb就知道这条记录是被那个事务id修改或删除的,以及这条记录在变更记录(undo log)。
理解起来可以认为是通过在每行记录后面保存两个隐藏的列来实现的,这两个列,分别保存了这个行的创建时间,一个保存的是行的删除时间。这里存储的并不是实际的时间值,而是系统版本号(可以理解为事务的ID),每开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的ID.
mvcc工作在读提交和不可重复读这两种事务的隔离等级下。
-
mvcc 解决的问题?
mvcc工作在读提交和不可重复读这两种事务的隔离等级下,可以解决脏读和不可重复读的问题。 那问题来了,mvcc是否可以解决幻读?
redo log :
mysql在执行一个修改数据的sql事务之前,为了保证事务的原子性和持久性,会采用log先行的方案。先把操作通过redo log记录下来,然后再去真正的修改数据。如果修改数据的中途,mysql重启或物理机重启,那mysql还是可以根据已经落到磁盘上面的redo log 继续完成这个事务。
例如某一事务的事务序号为T1,其对数据X进行修改,设X的原值是5,修改后的值为15,那么Redo日志为x=15,redo是物理日志。
undo log:
undo log就是mvcc中每条记录隐藏字段中的undo指针,指向这条记录的上一个历史版本。这样如果mysql需要回滚的话,可以利用undo,轻松的跳到某个版本。
redo 和 undo log记录的流程:
假设有A、B两个数据,值分别是 1 和 2,在一个事务中先后把A设置为3,B设置为4。
1.事务开始
2.记录A=1到undo log buffer
3.修改A=3
4.记录A=3到redo log buffer
5.记录B=2到undo log buffer
6.修改B=4
7.记录B=4到redo log buffer
8.到log buffer全部刷入到磁盘中后才提交数据
这里有个点,log buffer 刷入到磁盘,并不是最后要提交事物了才来一次性全部刷入到磁盘。log buffer刷入到log file是在事务进行的时候就逐步在做了。
逻辑日志和物理日志的区别:
在mysql的日志中,经常提到的一个概念是逻辑日志和物理日志。逻辑日志指的是,像是sql语句一样的操作日志。而物理日志可以理解为数据页的数据变化日志,更像是undo log。
redo log 和 undo log一般被认为是物理日志,也有一种说法是(物理+逻辑混合日志 : https://spongecaptain.cool/post/database/logicalandphicallog/), binlog是逻辑日志。
为什么是binlog?
类似于undo log,mysql把修改自己数据的sql语句记录下来,形成的日志。binlog日志只用于归档,只依靠binlog是没有crash-safe能力的。
binlog的使用场景更多的是,用于主从同步 (slave节点收到master节点同步的binlog数据,然后进行重放),其次使用mysqlbinlog工具来恢复数据.
-
使用id自增
自增的id好处是可以保证顺序插入,对于是聚簇索引的引擎,顺序的插入可以保证数据是顺序的写入,避免页分裂。
缺点是:
当需要合并表的时候,就比较痛苦了,需要额外处理id。其次,如果是要水平分表的话,不同表的id自增要考虑是否有可能重复。
-
使用uuid
uuid的优点是不会是重复的,合表的时候,不需要关心重复的问题。
缺点是:
但是uuid是字符串的格式,相对于数字类型的话,需要的存储的空间比较大, 且由于字符串的类型比较运算的成本是高于整数的。另外,由于uuid不是顺序的,插入的时候,不能保证数据是顺序写入的。
-
使用其他业务层定义的唯一id
mysql的锁分别共享锁和排他锁。排他锁中按照锁的粒度,分为行锁和表锁。表锁会锁定整张表并且阻塞其他用户对该表的所有读写操作,比如alter修改表结构的时候会锁表。行锁又可以分为乐观锁和悲观锁,悲观锁可以通过for update实现,乐观锁则通过版本号实现。
-
记录锁、间隙锁,临键锁
记录锁
select * from table where a = "xxx" for update;
间隙锁
select * from table where a between 0 and 100; // 锁定0~100的区间,并且不包括0和100
临键锁 在默认情况下,mysql的事务隔离级别是可重复读,并且innodb_locks_unsafe_for_binlog参数为0,这时默认采用next-key locks。所谓Next-Key Locks,就是Record lock和gap lock的结合,即除了锁住记录本身,还要再锁住索引之间的间隙。
-
for update 操作什么时候会是行锁,什么时候会是表锁?
-
如果明确指定主键的话,变成行锁。
select * from table where id = "xxx" for update;
-
如果指定的不是主键,不会是行锁。mysql会锁定一个区间。(变成间隙锁)
select * from table where name = "xxx" for update; // name不是主键
-
-
执行计划 explain
使用方法 explain + sql
里面的关键字段是type字段,type的不同输出表示了不同的执行情况。
const > eq_ref > ref > index > all 1. const, eq_ref, ref 走了唯一索引,表示很快找到了数据 2. index 表示值遍历了索引树,就找到了数据 3. all 全表遍历
-
慢查询日志
long_query_time 的默认值为10,意思是运行10秒以上的语句会被记录到日志中。
-
in 和 exsit 的区别
-
binlog记录的几种方案
-
同步sql操作 这种方案的缺点是对于同步关于时间相关的sql,就会问题。比如insert * from table where a = datetime();
-
同步数据状态 同步数据值的变成,缺点是会产生大量的日志记录。但是可以避免上面提到的问题。
-
mix方案 就是两种方案混合起来。
-
-
count(1) 和 count(*), count(列名) 的区别
-
count(1) 的执行效率是约等于 count(*)
-
count(列名) 查询到这个字段非空的记录数
-
-
批处理的思想
-
大量插入数据的时候
insert into T values(1,2) insert into T values(3,4) insert into T values(5,6) -- 下面的批量插入的效率会更高 insert intO T values(1,2) (3,4) (5,6)
-
大量的记录要删除
先删除索引,在删除记录
-
-
水平分表的好处
- 将不同的记录行分散到不同的表中,避免表锁相互竞争。
-
垂直分表的好处
-
将长度的很多的字段拆分出去 / 将不常用的字段拆分出去
-
将常用的查询字段组合放到一张表中
将热门和冷门的查询拆分到不同的地方,避免io竞争。
-
-
事务提交log的刷新控制
在写redo_log的时候先把数据写到redo_log缓冲区,然后异步写入磁盘,很显然,极端情况下会有丢失数据的可能。
控制这个刷盘策略的的参数叫做innodb_flush_log_at_trx_commit 这个参数有3个值:0|1|2,默认的话是1。
-
代表提交事务时不会写入磁盘,这样的话性能当然最好,但是在Mysql宕机的情况会丢失上一秒的事务的数据。
-
代表提交事务一定会进行一次刷盘,同步当然性能最差,但是也最安全。
-
代表写入文件系统的缓存,不进行刷盘。这个选项性能略差于1,Mysql宕机的话对数据没有任何影响,只有在操作系统宕机才会丢失数据,这种情况下默认Mysql每秒会执行一次刷盘。
-
-
一条sql语句的执行流程