1. MySQL的架构与历史

1. MySQL逻辑架构

1.1 连接管理与安全性

每个客户端连接都会在服务器进程中拥有一个线程, 这个连接的查询只会在这个单独的线程中执行, MySQL服务维护一个线程池

1.2 优化与执行

MySQL会解析查询SQL, 并创建解析数, 然后对SQL进行如下各种优化

  • 重写查询
  • 决定表的读写顺序
  • 选择合适的索引

对于SELECT语句, 在解析查询之前, 服务器会先检查查询缓存

1.3 存储引擎

优化器并不关心表使用的是什么存储引擎, 单存储引擎对优化查询是有影响的, 优化器会想存储引擎发起请求, 存储引擎会提供容量或者某个操作的开销信息, 以及表数据的统计信息

2. 并发控制

多个查询同一时刻修改数据, 都会产生并发控制问题, 可以在服务器层和存储引擎层进行并发控制

2.1 读写锁
  • 读锁(共享锁), 多个连接可以同时读取同一条记录
  • 写锁(排它锁), 写锁会堵塞其他的读锁和写锁
2.2 锁粒度

任何时候, 在给定的资源上, 锁定的数据量越少, 则系统的并发程度越高, 只要相互不发生冲突即可, 问题是枷锁也需要消耗资源, 锁的各种操作, 包括获取锁, 检查锁是否已经解除, 释放锁等操作都会增加系统开销

所谓的锁策略, 就是在锁的开销和数据的安全性之间寻求平衡, MySQL提供了多种选择, 每种MySQL存储引擎都可以实现自己的锁策略和锁粒度

  • 表锁, 最基本的所策略, 开销最小的策略
  • 行级锁, 最大程度的支持并发处理, 同时也带来了最大的锁开销

3. 事务

事务是一组原子性的SQL查询, 会增加MySQL服务器的开销, 事务的ACID特性:

  • 原子性(atomicity)
    • 一个事务是一个不可分割的最小工作单元, 要么全部提交成功, 要么全部失败回滚, 不可能只执行其中的一部分
  • 一致性(consistency)
    • 数据库完整性约束的一致性, 也就是数据符合设置的约束(例如:唯一约束,外键约束等)
    • 业务一致性(例如:转账前后,金额总数一致,), 由开发人员保证
  • 隔离性(isolation)
    • 事务在提交之前, 对于其他事务是不可见的
  • 持久性(durability)
    • 事务提交之后, 所做的修改就会永久保存到数据库中
3.1 隔离级别

SQL标准中定义了四种隔离级别, 规定了一个事务所做的修改, 哪些在事务内和事务间是可见的, 哪些是不可见的, 较低级别的隔离通常可以执行更高的并发, 系统开销也更低

  • READ UNCOMMITTED(读未提交)
    • 最低级别事务
    • 当前事务没有提交, 对其他事务也是可见的
    • 事务可以读取到未提交的数据, 被称为脏读
  • READ COMMITED(读已提交)
    • 避免了脏读
    • 事务从提交开始, 所做的修改对于其他事务都是不可见的
    • 会导致不可重复读问题, 同一个事务执行两次相同的查询, 可能得到不一样的结果
  • REPEATABLE READ(可重复读)
    • MySQL默认事务隔离级别
    • 避免了脏读和不可重复读
    • 保证了同一个事务中, 多次去读同样的记录, 结果是一致的
    • 会导致幻读问题, 某个事务读取多条记录时, 另外一个事务插入了新的记录, 可能会产生幻读行
  • SERIALIZABLE(串行化)
    • 最高的隔离级别
    • 强制事务串行执行
    • 可能会导致大量的超时和锁征用问题, 读操作也会被加锁
3.2 死锁

死锁是指两个或者多个事务占用同一资源, 并锁定对方占用资源, 可能会导致死锁

事务1:

start transaction ;
update s_user set name = 'zhangsan' where id = 1;
update s_user set name = 'lisi' where id = 2;
commit;

事务2:

start transaction ;
update s_user set name = 'lisi' where id = 2;
update s_user set name = 'zhangsan' where id = 1;
commit;

上面两个事务, 在高并发情况下, 都先执行了第一条UPDATE, 更新了一行数据, 同时也锁定了该行数据, 然后每个事务都尝试执行第二条UPDATE语句, 却发现被对方锁定了, 导致了死锁

为了解决死锁问题, 数据库系统实现了各种死锁检测和死锁超时机制, 例如:InnoDB存储引擎, 可以检测到死锁循环依赖, 并立即返回一个错误

锁的行为和顺序和存储引擎相关, 同样的顺序执行SQL语句, 有些存储引擎会产生死锁, 有些则不会

死锁发生以后, 只有部分或者完全回滚其中一个事务, 才能打破死锁

3.3 事务日志

事务日志可以帮助提高事务的效率, 使用了事务日志之后

  • 存储引擎在修改表的数据时, 只需要修改内存中的数据, 然后事务日志持久化到磁盘上的事务日志中
  • 事务日志持久化之后, 内存中修改的数据会慢慢修改到硬盘中
  • 如果事务日志已经持久化, 但数据没有被写回磁盘, 此时系统崩溃, 那么存储引擎在重启之后, 能够自动恢复这部分修改的数据
3.4 MySQL中的事务

MySQL提供了两种事务型存储引擎

  1. InnoDB
  2. NDB Cluster

还有一些第三方存储引擎也支持事务

  1. XtraDB
  2. PBXT
自动提交(AUTOCOMMIT)

MySQL默认此启用自动提交模式, 如果不显示的开始一个事务, 那么每个查询都被当成一个事务执行提交操作, 可以通过设置autocommmit变量来启用或者禁用自动提交模式

set autocommit = 1;

如上: 1或者ON表示启用, 0或者OFF表示禁用

当autocommit=0时, 所有查询都在一个事务中, 直到显示执行commit或者rollback回滚, 然后又开始一个新的事务

autocommit对于MyISAM引擎或者内存表, 不会有任何影响, 对于这类表来说, 没有COMMIT或者ROLLBACK的概念, 也就说一直处于autocommit启用的状态

还有一些命令, 在执行之前会强制执行commit提交当前的活动事务, 例如: 执行DDL(数据库定义语言), ALERT TABLE等导致大量数据修改的操作, 另外还有LOCK TABLES等其他语句与会导致同样的结果

set session transaction isolation level read committed;

通过执行set session transaction isolation leve 命令设置隔离级别, 隔离级别在下一个事务开始的时候生效, 也可以在配置文件中, 设置整个数据库的隔离级别

在事务中混合使用存储引擎

MySQL事务是由下层的存储引擎实现的, 所以在同一个十五中使用多种存储引擎是不可靠的

隐式和显示锁定

InnoDB采用的两阶段锁定协议, 在事务执行的过程中, 随时可以执行锁定, 锁只有在执行COMMIT或者ROLLBACK的时候才会释放, 并且所有的锁是在同一时刻被释放

  • InnoDB隐式和显示锁定
    • 隐式锁定: 一般的锁定都是隐式锁定, InnoDB会根据隔离级别在需要的时候自动锁定
    • 显示锁定:这些语句不属于SQL规范
      • SELECT ... LOCK IN SHARE MODE
      • SELECT ... FOR UPDATE
  • MySQL LOCK和UNLOCK语句
    • LOCK和UNLOCK语句由服务器层实现, 和存储引擎无关, 它们有自己的用途, 不能代替事务处理, 如果需要使用事务, 还是应该选择事务型存储引擎
    • 如果使用了InnoDB存储引擎, 还显示使用LOCK TABLES语句, 不但没有必要, 还会严重影响性能, 实际上使用InnoDB的行级锁更好
    • LOCK TABLES和事务间相互影响的话, 在某些MySQL版本中甚至会出现无法预料的结果

4. 多版本并发控制

MySQL大多数事务型存储引擎实现都不是简单的行级锁, 基于提升并发性能的考虑, 一般都实现了并发版本控制(MVCC)

MVCC是行级锁的一个变种, 它在很多情况下避免了加锁操作, 因此开销更低, 虽然实现机制有所不同, 但大都实现了非阻塞的读操作, 写操作也只锁定必要的行

InnoDB存储引擎的MVCC实现:

  1. MVCC只在REPEATABLE READ和READ COMMITTED两个隔离级别下工作, READ UNCOMMITTED和SERIALIZABLE和MVCC不兼容
    • READ UNCOMMITTED总是读取最新的数据行
    • SERIALIZABLE会对所有读取的行都加锁
  2. 每行记录后面保存三个隐藏列
    • 6个字节的事务标识DB_TRX_ID, 插入或更新行的最后一个事务的ID, 删除视为更新, 将其标记为已删除
    • 7个字节的该行回滚段指针DB_ROLL_PTR, 指向回滚的具体内容
    • 6个字节的行标识DB_ROW_ID, 自增ID
    • 每开启一个新的事务, DB_TRX_ID就会自动递增, 事务开始时刻的DB_TRX_ID会作为事务的版本号, 用来和查询到的每行记录的版本号进行比较

5. MySQL存储引擎

5.1 InnoDB存储引擎

InnoDB采用MVVC支持高并发, 实现了四个标准隔离级别, 其默认级别是REPEATBLE READ(可重复读), 并且通过间隙锁策略防止幻读的出现

InnoDB内部做了很多优化, 包括

  1. 从磁盘读取数据时采用的可预测性预读
  2. 能够自动在内存中创建加速读操作的自适应hash索引
  3. 创建缓冲区, 加速插入操作
  4. 支持热备份
5.2 MyISAM存储引擎

MyISAM不支持事务和行级锁, 支持表锁, 崩溃后无法安全恢复

MyISAM压缩表, 减少磁盘空间占用, 减少磁盘IO, 提高查询性能, 压缩表也支持索引, 但是索引是只读的

5.3 内建的其他存储引擎
  1. Archive
  2. Blackhole
  3. CSV
  4. Memory
  5. Merge
  6. NDB
5.4 第三方存储引擎
  1. OLTP
5.5 选择合适的存储引擎

默认选择InnoDB存储引擎, 特殊场景, 可以选择其他存储引擎

  1. 日志型应用, 这类应用对插入速度有很高的要求, 可以选择MyISAM, 因为开销低, 而且插入速度特别快
  2. 只读或者大部分情况只读, 如果不接MyISAM崩溃恢复的问题, 选用MyISAM是合适的
  3. 事务型系统, InnoDB是最佳选择
  4. 大数据量, 采用InnoDB, 提前做好系统规划, 合理选择硬件, 做好物理设计

你可能感兴趣的:(1. MySQL的架构与历史)