Mysql隔离级别默认是repeatable read,他是不可以解决不可重复读,不可重复读是用mysql里面的mvcc解决,mvcc全称是mulit-version Concurrent Controller多版本并发控制,里面有个版本链readView。
一条数据记录都是由隐藏列、真实数据、额外数据,额外数据指描述真实数据的数据,也称呼为元数据,里面有最长字段长度列表,null值列表,头部信息,里面放着delete_mark,innoDB删除并不是真的删除,这也是插入数据回滚的时候,那个id会自动自增的原因,其中最长字段长度列表也可以细分,细分的话如果char在字符集为可变的时候,char的描述信息也会存在变长字段长度列表,字符集asicc是1个字符集,gbk是1~2个字符集,utf8是1~3个字符集。
真实数据是用户存储的数据,现在行格式默认是dynamic,以前是compact,以前的行格式是当页分裂的时候,真实数据列存储一部分真实数据了,其他的都存储页码号,指向真实数据的地址值,dynamic只存储页码号,真实数据存储在页里。
隐藏列主要有三个字段,row_id,trx_id,roll_pointer。Row_id不是必须的,只有当表里不存在主键,并且没有唯一列的时候,innoDB会默认会表提供一个row_id,trx_id和roll_pointer是必须的,一个是事务id,一个是指向回滚链表里的页,当指向的是delete或者update页的时候,里面会有个old_roll_pointer指向前一次事务发生的页,这个页正常是insert用于回滚。
扯远了,回到四个隔离级别,read uncommitted ,readcommitted,repeatable read,serialization,分别有幻读,脏读,不可重复读的问题。
而read committed和repeatable都是用mvcc来解决不可重复读,他们两的区别是read committed每次会生成一个read view,而repeatable read会复用之前生成的read view链表,然后从版本链中挑出可以显示给用户的数据。
ReadView的组成主要是由一条用户可见数据,也就是数据页上的数据,和以前改过的数据undo日志数据组成,第一条就是可见数据,后面的是历史数据。M_ids会把当前系统活跃的读写事务id存在这里。还有个最小值和最大值,判断当前事务读取那条数据,主要就判断这个readView里的trx_id与事务id的大小关系,当前事务id大于readView里的记录,说明当前事务在readView之后才开启,不可访问,反之小于则可以访问。
而锁分为排它锁和共享锁,共享锁上锁之后,其他事务只可以读不可写,排他锁上锁之后,其他事务不可以读也不可以写,根据颗粒度的不同又分为行锁和表锁,行锁和表锁有什么关系呢,当事务给行上了排它锁,也就是x锁,这时候会给表级别的锁上个ix锁,这个是在其他事物给表级别上锁之前,判断是否有行锁未释放使用的,总不能遍历所有行锁,当发现有其他行锁获取锁的时候,则会吧自己的事务设置为is_waiting为false,直到行锁释放,自己才会变成true。其实表锁挺鸡肋的,在特殊情况下下才会用到,比如系统恢复崩溃,一般 都是service的元数据锁来代替行锁的。
还有auto-insc锁,我们都知道可以设置自增的主键,可以用innoDB-auto-inscrent-mode系统参数来控制,当为1的时候,用auto-insc锁,2为轻量级别的锁,当为0的时候,两个锁混着来,auto-insc锁会吧新增的数据锁定,其他事物想新增必须等其释放锁,注意这个是对单个sql语句上锁,根前面读事务上锁不一样,这个sql语句结束,锁就释放,前面的都是必须事务提交擦释放锁。轻量级别的锁是知道新增的多少数据,这时候就不需要阻塞新增,这样可以避免等待,提升性能。