mysql——事务以及锁

一、事务的特性(ACID),原子性,一致性,隔离性,持久性;

二、Mysql的事务隔离级别

1、读未已提交(READ UNCOMMITTED):

      一个事务可以读取到未提交的数据(比如只进行了更新操作),产生脏读,幻度。

2、读已提交(READ COMMITTED):

     事务读取到已经提交的数据,多次select,查询结果不一致,产生幻度。

3、可重复读(REPEATABLE READ) :

    一个事务中多次读取数据相同,可能产生幻读,但是mysql的可重复读解决了幻读的问题,事务读取可以分为:快照读使用与select 语句,比如:select id from user where id = 1 (MVCC:READVIEW 和 版本链解决)、当前读,比如:select id from user where id = 1 for update (一个事务内读取数据,间隙锁),可以参考一下链接加深理解:https://blog.****.net/qq_40918324/article/details/104617714

4、串行化(SERIALIZABLE):

三、1、 什么是版本链:

 在innoDB存储引擎的表,有三个隐藏列分别是:row_id,trx_id,roll_pointer,其中row_id不是必要的,如果有主键索引,则列号不一定存在。

trx_id:记录当前事务的事务号。

roll_pointer:指向前一个事务号,(通过指针找到修改前的记录)。

2、ReadView:

①  对于读已提交和可重复读,需要使用上述的版本链,核心问题:找到哪个事务是正确的,可见的。那么怎么判断哪个事务版本是可用的呢,就需要ReadView了,我发现其实很多人都在说mvcc但是却不知道readView,真的是很奇怪。

②  ReadView 主要由四部分组成:

    1、m_ids:记录当前活跃的所有事务,相当于一个数组,[1,2,3,4]

     2、min_trx_id: 记录当前活跃事务中最小的事务号。

     3、max_trx_id:记录下一个应该开启的事务的事务号,事务号是递增的,其实就是时间戳。

     4、creator_trx_id:表示生成该ReadView的事务号。

注意max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比方说现在有id为1,2,3这三个事务,之

后id为3的事务提交了。那么一个新的读事务在生成ReadView时,m_ids就包括1和2,min_trx_id的值就是1,

max_trx_id的值就是4。

③ 有了readView之后,再查询语句的时候就可以判断哪些事务的数据是可以被查到的了:

    1、比如查询的数据的事务id  <  min_trx_id  表示数据在查询前没有操作过,可以操作。

     2、如果数据的事务id == creator_trx_id 则表示当前事务在查询数据,可以操作。

     3、如果数据的事务id > max_trx_id 则表示查询时,数据又被其他的事务操作过,不能查询数据,需要根据版本链查询。

     4、如果min_trx_id < 数据的事务id <max_trx_id, 需要判断是否在m_id,并且不在m_id 列表中则代表,查询时已经被提交过,可以查询。

④ 对于读已提交和可重复读,在生成ReadView的规则不同,导致了他们读取数据时候的区别:

   1、读已提交:每次执行select的时候都会生成一个readView,记录当前活跃的事务id。所以他可以读取到最新的别提交过的数据。

   2、可重读度:他只会在第一次执行select 语句的时候生成ReadView,所以多次执行ReadView相同所以查询数据相同。

四、mysql innoDB的行锁与表锁

 一、行锁:

      1、行锁分为: ① LOCK_REC_NOT_GAP:单个行记录上的锁。   

                ② LOCK_GAP:间隙锁,只会锁住查询数据之间的间隙。

                ③ NEXT_KEY_LOCK:结合单行锁和间隙锁的锁。

       2、对于读已提交,不存在间隙锁,只会对表中查询出来的数据加锁。

       3、对于可重读度,对于主键索引和二级索引不会加间隙锁。对于普通索引和没有索引的列,会对查询出来的数据的间隙加锁(可能会造成死锁)。

       4、这里的锁对于二级索引,首先会对索引字段加锁,再下来同时会对,索引页中的主键id进行加锁,为什么会加两次锁?

           ① 当查询数据有二级索引列时,直接判断加锁快速返回。

           ② 当当前查询没有二级索引列,但是时主键索引对应的数据时,保证数据被加锁。

五、mysql的redolog和undolog

      数据是通过什么方式保存到数据库中呢?数据存储过程中是如何保证脏页落盘呢?如何实现数据回滚?如何实现数据恢复呢?

 其实也就是mysql如何保证持久性,原子性和一致性的。

   ① mysql更新操作会先把数据从磁盘加载到内存,进行数据修改,然后再从内存(用户态)到内存空间(内核态)最后再到mysql的磁盘空间保证了数据持久化。

   1>buffer pool,数据会从磁盘加载到buffer pool进行数据修改。

   2> redolog 分成两部分: 1、redolog buffer 2、redolog file

       1、数据在写入redolog file之前会先写入redolog buffer,因为写入缓存的效率更快,当发生commit或者到达固定时间后才会写入redolog file,每次mysql重启是都会判断更具redolog恢复数据(保证持久化),这里还有一个lns的概念后面说,具体的写入规则 由 innodb_flush_log_at_trx_commit 0|1|2 控制,如下图:

 0 、  事务提交时,每次都会写入logbuffer ,但是只会定时(每秒)写入(调用fsync)osbuffer ,在写入到磁盘文件,可能会丢失1 秒内的数据。

1、mysql默认规则,事务提交时每次都写入 logbuffer、osbuffer、刷新到磁盘。数据完整但是性能低下。

2、事务提交时每次都写入osbuffer,但是每秒执行一次写入磁盘操作。

mysql——事务以及锁

   ② mysql的脏页落盘(持久化):

          1>脏页: buffer pool中的数据与磁盘中数据不一致的数据(mysql加载数据以页为单位的)成为脏页。

           2>检查点:到达一个检查点是才会触发脏页落盘。在innodb中,数据刷盘的规则只有一个:checkpoint

                Innodb存储引擎中checkpoint分为两种:

innodb存储引擎中checkpoint分为两种:

  • sharp checkpoint:在重用redo log文件(例如切换日志文件)的时候,将所有已记录到redo log中对应的脏数据刷到磁盘。
  • fuzzy checkpoint:一次只刷一小部分的日志到磁盘,而非将所有脏日志刷盘。有以下几种情况会触发该检查点:
    • master thread checkpoint:由master线程控制,每秒或每10秒刷入一定比例的脏页到磁盘。
    • flush_lru_list checkpoint:从MySQL5.6开始可通过 innodb_page_cleaners 变量指定专门负责脏页刷盘的page cleaner线程的个数,该线程的目的是为了保证lru列表有可用的空闲页。
    • async/sync flush checkpoint:同步刷盘还是异步刷盘。例如还有非常多的脏页没刷到磁盘(非常多是多少,有比例控制),这时候会选择同步刷到磁盘,但这很少出现;如果脏页不是很多,可以选择异步刷到磁盘,如果脏页很少,可以暂时不刷脏页到磁盘
    • dirty page too much checkpoint:脏页太多时强制触发检查点,目的是为了保证缓存有足够的空闲空间。too much的比例由变量 innodb_max_dirty_pages_pct 控制,MySQL 5.6默认的值为75,即当脏页占缓冲池的百分之75后,就强制刷一部分脏页到磁盘。

由于刷脏页需要一定的时间来完成,所以记录检查点的位置是在每次刷盘结束之后才在redo log中标记的。

③ double write: 因为mysql一页数据为16k,而磁盘最小为4k,所以一页数据需要写多次,期间如果出现失败就需要doblewrite来保证持久化成功,大概也是先写入缓存再写入磁盘。mysql——事务以及锁

参考链接:https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html