数据库的日志
数据库在更新时,会产生binlog、redo log、undo log。
binlog,
server层产生的逻辑日志(主从复制时使用)。
redo log
InnoDB引擎产生的物理日志,保证持久化,ACID中的D(崩溃修复用日志)。
InnoDB“日志优先与数据”,记录redo log视为数据已更新。
当数据写入磁盘后,redo log删除。
redo log存储在4个1GB的文件中,并且循环写入。
write pos是当前日志写入点。
check point是擦除点,数据更新到硬盘时被擦除。
当write pos追上check point时,事务无法提交,需要等待check point推进。
只要redo log不丢失,数据就不会丢失。
undo log
InnoDB引擎产生的逻辑日志,保证原子性、隔离性(例如:事务回滚,快照读)。
位于表空间中的undo segment中。
客户端之间的影响
客户端执行SQL时,会产生各种行锁、表锁、元数据锁。
一个客户端产生的锁,会干扰其他客户端SQL语句的执行。
两个客户端之间可能产生死锁。
事务可能会产生隐式的锁,造成性能问题。
MYSQL锁的种类
按照粒度分,MYSQL的锁可以分为全局锁、表级锁、行锁。
全局锁,锁住所有表,整个库无法修改,只能读取。
表级锁,又可以分为表锁(数据锁)和元数据锁(metadata lock)。
行锁,会锁住数据行,分为共享锁和独占锁。
MYSQL-全局锁
FTWRL(Flush tables with read lock)。
此命令使整个库处于只读状态。
主要用途保证备份的一致性。
不要随意使用,杀伤性极大,建议在备库使用。
MYSQL-表锁(数据锁)
命令:lock tables xxx read/write。
上读锁后其他人不能改,上了写锁后其他人即不能读又不能写。
表锁是非常重的锁,在InnoDB中很少用。
MYSQL-元数据锁(metadata lock)
元数据指的是表的结构、字段、数据类型、索引等。
事务访问元数据时,会自动给表增加MDL读锁。
事务修改元数据时,会自动给表增加MDL写锁。
MYSQL-行锁
行锁有两种类型,有很多种叫法,其实都是一回事
读锁/写锁。
共享锁/排他锁。
共享锁/独占锁。
S锁/X锁。
以读锁/写锁为例
读锁
自己要读,别人不可以写。
写锁
自己要写,不允许别人读和写。
只有读锁和读锁可以兼容(重复上锁),其他均不兼容。
MYSQL-Next-Key Lock
Next-Key实际上是通过间隙锁和行锁共同实现的一种加锁方式,只在可重复读(repeatable read)隔离级别下生效。
next可以理解为间隙,Key为行锁。
加锁时以Next-Key为基本单位进行加锁。
实例表结构
唯一索引-等值查询间隙锁锁(被查询值不存在时)
因为11不存在所以仅需要锁住(10,20)这个间隙。
当11存在时仅锁住11这一条记录,因为时唯一索引。
非唯一索引-等值锁(被查询值存在时)
Tx A事务语句,不涉及回表,走的是索引覆盖。因此Tx B事务可以对id=20这条记录加写锁(加在主索引上)。但是在执行插入(12,12,12)是会被阻塞,原因是辅助索引C已经被Tx A增加了间隙读锁。
主键索引-范围锁(前后范围都存在)
Tx A执行后会锁住[20,30]。如果Tx B执行的是下图的查询语句会被阻塞。如果Tx B执行的是下图的插入语句则成功。
非索引字段查询
非唯一索引-范围锁(前后范围都存在)
MYSQL-MVCC(多版本并发控制)
通过undo log来实现,MYSQL每次开启一个事务时会生成一个事务id,并且该id是自增的。同时undo log上的所有修改操作都会记录对应的事务id。
补充两个读取的方式
快照读
将数据库在过去某个时刻的快照应用在查询上,使得:这次查询只能看到别的事务生成快照前提交的数据。
例如:以上图为例子,一个查询事务,事务id为160,处于158与190之间。如果采用快照读的方式只能读取到比160事务id小的并且是已经提交的数据。
当前读
每次查询最新的已经提交的数据。采用当前读的方式有,select ... for update、update、insert、delete操作。