mysql基础

242 阅读7分钟

mysql

基础架构

引用mysql45讲
建议

  • 尽量使用长链接(建立连接很耗时)
  • 执行大操作后,重新连接或者初始化连接资源(因为 mysql 使用的临时内存是存在连接对象里面的,只有当连接释放了才释放,所以可能导致 oom)
  • 对于更新大的表,尽量关闭缓存,因为一个表更新的时候,和这个表相关的缓存都会清理掉。

日志

WAL 技术:先写日志,在写磁盘
两阶段提交:保证两个日志的状态保持一致

引用mysql45讲
引用mysql45讲

bin log

  • binlog 会记录所有的逻辑操作,并且是采用“追加写”的形式,主要用来做归档

redo log

  • redoLog 是重做日志文件是记录数据修改之后的值,用于持久化到磁盘中。redo log 包括两部分:一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。由引擎层的 InnoDB 引擎实现,是物理日志,记录的是物理数据页修改的信息,比如“某个数据页上内容发生了哪些改动”。当一条数据需要更新时,InnoDB 会先将数据更新,然后记录 redoLogBuffer 中,然后找个时间将 redoLog 的操作执行到磁盘上的文件上。

两种日志的不同

  • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
  • redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
  • redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

事务

ACID:原子性、隔离性、一致性、持久性

读未提交

一个事务还未提交,所做的改变就能被其它事务看到

读提交

一个事务提交后,所做的改变才能被其它事务看到
每个 sql 语句都会创建一个视图

可重复读

一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
会在事务启动时创建一个视图,整个事务存在期间都用这个视图

串行读

顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

产生的问题

  • 脏读
    脏读:事务 A 读取了事务 B 更新的数据,然后 B 回滚操作,那么 A 读取到的数据是脏数据
  • 不可重复读
    事务 A 多次读取同一数据,事务 B 在事务 A 多次读取的过程中,对数据作了更新并提交,导致事务 A 多次读取同一数据时,结果不一致。(可以通过加给扫描到的行加锁或换隔离性更高的事务来解决)
  • 幻读
    A 事务读取了 B 事务已经提交的新增数据(仅指新插入的行)。注意和不可重复读的区别,这里是新增,不可重复读是更改(或删除)。select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。(通过加入间隙锁来解决)

索引

索引原理

  • hash索引
    优缺点:只适合等值查询即(key-value对),不适合范围查找
  • 二叉树
    优点:效率高 缺点:索引还需要存储到磁盘中,树越高。访问磁盘次数就越多
  • b+树索引
    将平衡二叉树变为n叉树,减少了磁盘访问次数
  • 索引模型
    主键索引:也称为聚簇索引,叶子结点存储的是整行数据
    普通索引:也称为2级索引,叶子结点存储的是主键索引的值
    区别:主键索引直接搜索主键索引树,非主键索引先是搜索非主键索引树,找到主键值,再去主键索引树上搜索,这个过程称为回表。所以应该尽量使用主键搜索。

索引失效

主要原因还是对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。

  • 条件字段函数操作
  • 隐式类型转换(如:string和int相比)
  • 隐式编码转换 (解决方案:更改表的字符集,修改sql语句 CONVERT转换字符集)

索引优化

  • 自增索引
    每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录。 主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。
  • 覆盖索引
    减少回表次数
  • 最左原则
    索引是按照定义里出现的字段排序的(如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的)
  • 索引下推
    在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
  • change buffer
    当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InooDB会将这些更新操作缓存在change buffer中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行change buffer中与这个页有关的操作(merge)。通过这种方式就能保证这个数据逻辑的正确性。
    主键索引为了判断唯一性(必须把数据业读到内存中),所以不能使用change buffer 普通索引可以用上change buffer 所以对于写多读少的业务,使用普通索引 如果写完就伴随着查询,则数据先进入到change buff,然后再把数据页读到内存汇总 随后在触发merge过程,反而会增加维护代价。则可以关闭change buff。

全局锁

MySQL提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句
全局锁的典型使用场景是,做全库逻辑备份。也就是把整库每个表都select出来存成文本。

缺点
如果你在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆; 如果你在从库上备份,那么备份期间从库不能执行主库同步过来的binlog,会导致主从延迟。 改进 可以通过mysqldump来一致性备份。主要实现是启动一个事务,来拿到一致性视图(和可重复读一下),所以只支持有事务的引擎。

表锁

MySQL里面表级别的锁有两种:一种是表锁,一种是元数据锁(MDL)

MDL不需要显式使用,在访问一个表的时候会被自动加上,当对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁。

间隙锁

间隙锁锁的是两个值的中间 跟间隙锁存在冲突关系的,是“往这个间隙中插入一个记录”这个操作。
for update 语句不仅加上了行锁还会加上间隙锁,间隙锁和行锁合称 next-key lock,每个 next-key lock 是前开后闭区间。

锁优化

如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。 控制并发度减少死锁检测耗时。(限流)

主从

mysql 学习思维导图

本文使用 mdnice 排版