MySQL架构
客户端将sql语句发送给Mysql服务器,然后服务器先从查询缓存中查找,如果有就直接返回,如果没有则就对sql语句进行分析解析生成语法树,然后优化器对语法树进行优化,生成执行计划,调用存储引擎的api,进行数据查找。查找完返回mysql服务器,然后通过服务器的筛选,返回给客户端。
并发控制
读写锁
MySQL的表就相当于邮件,如果两个人同时往邮件内写内容,可能会造成数据不一致,mysql的表就是类似的道理,如果两个线程同时写,或者一个读一个写可能会造成数据不一致的问题,在处理这类问题的时候,mysql是利用共享锁和排他锁来解决的,排他锁就是写锁,共享锁就是读锁,读锁和写锁互斥,可以同时读
锁粒度
如果把锁的粒度放在表的基础上,就是对表进行上锁,性能可能会非常低下,但是如果是对表中的一个行记录上锁可能会提高性能,因为不同的线程可能会访问不同的行,如果是用表锁,那么两个线程访问不同的行就没办法并发了。锁粒度越小,性能越高,并发越高。当然也要根据具体问题具体分析
表锁
对整个表进行上锁,一个线程在进行读取时会获得表的读锁,一个线程在进行更新写入删除时,会获得表的写锁
行锁
对行记录进行上锁,是存储引擎层的实现
事务
事务可以理解为一组操作,必须同时成功,或者同时失败
ACID
原子性
一个事务是一个不可分割的单元,必须同时成功或者失败
一致性
一个事务执行后,和执行前,表中数据必须和原来的内容保持一致
隔离性
一个事务内的操作对另一个事务是隔离的。对另一个事务是不可见的
持久性
一旦事务提交后,其所做的修改必须持久化到数据库中,当然这通常不会百分之百的
隔离级别
Read uncommited (读未提交):一个事务还没有提交,其所做的修改对另一个事务可见(脏读)
Read commited(读已提交):一个事务只有提交后,其所做的修改对另一个事务可见(不可重复读)就是一个事务先执行了一个查询语句,然后没有结束,这时候另一个事务提交了一个修改。前面的事务再执行上一次查询语句时就会和上一次执行的结果不一样
Repeatable read (可重复读):就是解决了不可重复读的问题,(可能是通过版本快照来解决的(个人理解))一个事务对于同样的记录,每次查询都是一致的。但是如果另一个事务插入了内容,可能会造成幻读的问题,看到前一个查询没有看到的新行
serializable( 可串行化):事务会对操作到的行上锁,保证每个事务是串行执行的
死锁
当两个事务同时持有对方想拥有的资源,就会造成 死锁
mysql的解决办法,让拥有更少排他锁的事务进行回滚,当等待锁的时间超时了放弃锁请求
事务日志
让事务做出的修改行为记录到磁盘上的事务日志上,而真正的修改数据的行为,再缓存中执行,等待操作系统把缓存中的内容慢慢刷回磁盘
如果数据还没刷回磁盘就断电了,存储引擎也有办法恢复数据,不同存储引擎实现方式不一样
Mysql中的事务
默认是autocommit 的
也就是说每一条语句都是一个事务
可以通过set autocommit 来设置不自动提交,这样就得需要手动创建事务,
start transaction
或者所有的查询都是在一个事务中,直到显示的调用了commit或者rollback 就提交事务,然后再创建一个新的事务。还有一些命令是在使用之前系统会自动调用commit,比如alter table
在事务中避免使用混和引擎
原因是有些引擎支持事务,有些不支持,比如myisam ,如果事务出现问题需要回滚,而不支持事务的存储引擎就不能会回滚
隐式和显示锁定
在执行sql语句中 存储引擎会给根据需要对访问的行进行上锁,当执行commit和rollback后会释放锁
也可以通过调用 lock in share mode 和for update 进行上锁
MVCC (多版本并发控制)
MVCC 是通过在每一行的后面增加版本号,一个是创建版本号,一个是删除版本号,每一个事务创建时版本号都会自动加1 ,如果事务新增了一行,那么这行的创建版本号就是当前事务的事务版本号,如果事务删除一个行,就会在这个行的删除版本号上改成自己的事务版本号,如果更新一行,则会在表上新增一行,然后创建版本号是当前事务版本号,然后原来的行的删除版本号对应当前事务的版本号
查询时,只有事务的版本号 大于等于 行的创建版本号才可以查询,事务的版本号小于行的删除版本号。