目录
什么是MySQL?
MyISAM和InnoDB区别
索引
什么是事务?
并发事务带来哪些问题?
事务隔离级别有哪些?MySQL的默认隔离级别是?
隔离级别底层实现原理(MVCC和锁)
MySQL的事务实现:
MySQL可重复读级别下,什么情况会出现幻读?
锁机制与InnoDB锁算法
大表优化
分库分表之后,id 主键如何处理?
MySQL 是一种关系型数据库,在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展。MySQL的默认端口号是3306。
两者的对比:
READ COMMITTED
和 REPEATABLE READ
两个隔离级别下工作;MVCC可以使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。MySQL索引使用的数据结构主要有BTree索引 和 哈希索引 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。
相比MyISAM,索引文件和数据文件是分离的.
InnoDB: 其数据文件本身就是索引文件。其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引
其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。
事务是逻辑上的一组操作,要么都执行,要么都不执行。
事物的四大特性(ACID):
可能会导致以下的问题。
不可重复读和幻读区别:
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了。
SQL 标准定义了四个隔离级别:
InnoDB 存储引擎在 REPEATABLE-READ(可重读) 事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生.
InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的 SERIALIZABLE(可串行化) 隔离级别。i
nnoDB 存储引擎默认使用 REPEAaTABLE-READ(可重读) 并不会有任何性能损失。
InnoDB 存储引擎在 分布式事务 的情况下一般会用到 SERIALIZABLE(可串行化) 隔离级别。
锁的分类
2) 什么时候会加锁?
在数据库增删改查四种操作中,insert、delete和update都是会加排它锁(Exclusive Locks)的,而select只有显式声明才会加锁:
3) 四种隔离级别
READ UNCOMMITTED
隔离级别下, 读不会加任何锁。而写会加排他锁,并到事务结束之后释放。
实例1:
查-写:查并没有阻止写,表明查肯定并没有加锁,要不写肯定就阻塞了。写很明显,会加排它锁的。
实例2: 写-写:阻塞,表明,写会加排它锁。
READ COMMITTED
顾名思义,事务之间可以读取彼此已提交的数据。
InnoDB在该隔离级别(READ COMMITTED)写数据时,使用排它锁, 读取数据不加锁而是使用了MVCC机制。
因此,在读已提交的级别下,都会通过MVCC获取当前数据的最新快照,不加任何锁,也无视任何锁(因为历史数据是构造出来的,身上不可能有锁)。
遗留了不可重复读和幻读问题: MVCC版本的生成时机: 是每次select时。这就意味着,如果我们在事务A中执行多次的select,在每次select之间有其他事务更新了我们读取的数据并提交了,那就出现了不可重复读,即:重复读时,会出现数据不一致问题。
REPEATABLE READ
READ COMMITTED级别不同的是MVCC版本的生成时机,即:一次事务中只在第一次select时生成版本,后续的查询都是在这个版本上进行,从而实现了可重复读。
但是因为MVCC的快照只对读操作有效,对写操作无效
SERIALISABLE
该级别下,会自动将所有普通select
转化为select ... lock in share mode
执行,即针对同一数据的所有读写都变成互斥的了,可靠性大大提高,并发性大大降低。
读-写,写-写均互斥。
ACID特性。原子性和隔离性基于undo log,持久性基于redo log。具体展开可以再讲讲redo log的两阶段提交。
如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。
其实可重复读无法保证完全的隔离性。如果在事务A中,第一次select使用快照读(使用MVCC),同时另外一个事务B在第一次select的范围内插入了数据,那么如果A在第二次读时使用了当前读(for update/in share mode),此时就会出现幻读问题,因为当前读并不会使用MVCC,而是从数据库读取最新数据。
MyISAM和InnoDB存储引擎使用的锁:
表级锁和行级锁对比:
InnoDB存储引擎的锁的算法有三种:
相关知识点:
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:
限定数据的范围
务必禁止不带任何限制数据范围条件的查询语句。比如:当用户在查询订单历史的时候,我们可以控制在一个月范围内;
读/写分离
经典的数据库拆分方案,主库负责写,从库负责读;
垂直拆分
根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。
简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。
水平拆分
保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。
水平拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 水平拆分最好分库 。
一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。
下面补充一下数据库分片的两种常见方案:
因为要是分成多个表之后,每个表都是从 1 开始累加,这样是不对的,我们需要一个全局唯一的 id 来支持。
生成全局 id 有下面这几种方式: