数据库多个事务运行时 会发生以下问题：

1 脏读 ，2 脏写，3 不可重复读，4 幻读

1 脏读 事务B去查询了事务A修改过的数据，但是此时事务A还没提交，所以事务A随时会回滚导致事务B再次查询就读不到刚才事务A修改的数据了
2 脏写 事务B去修改了事务A修改过的值，但是此时事务A还没提交，所以事务A随时会回滚，导致事务B修改的值也没了

3 不可重复读： 在一个事务内 多次读取的结果不一致（因为可以读取别人提交的数据，导致读取结果不一致），与4的区别在于更新数据导致读取结果不一致
4 幻读：一个事务内 查询数据第一次M条 ，第二次N条，两次不一致 (感觉这个定义有问题呀，和不可重复读是一样的呀)，与3的区别在于增删数据导致读取结果不一致

undo log 版本链：

每条数据其实都有两个隐藏字段，一个是trx_id，一个是roll_pointer，这个trx_id就是最近一次更新(或者创建)这条数据的事务id，roll_pointer就是指向你了你更新这个事务之前生成的undo log。

版本链 就是上面说的roll_pointer指向关系形成的一个链条。

ReadView机制：
当在执行一个事务的时候，生成一个ReadView，这个ReadView包含以下四个重要的东西

m_ids:	指的就是当前还有哪些事务没有提交
min_trx_id：当前未提交事务id的最小值(也就是m_ids列表中的最小值)
max_trx_id：mysql下一个要生成的最大事务id
creator_trx_id：当前事务id

假设一个数据的原始事务是32，现在来了两个事务A(45),B(59)。
现在事务A开启一个ReadView，那么m_ids 是[45,59]，min_trx_id是45，max_trx_id是60，creator_trx_id是45，事务A查询的时候 先判断数据的trx_id是否小于等于自己ReadView中的min_trx_id。
如果是那么就读取这个数据，如果不小于min_trx_id，那么沿着版本链一直往下找，直到找到尽头。很明显 32 < 45 ,此时A读到原始值。
接着，事务B开始了，它蒋这行数据改为了B，并且提交事务了，那么此时数据trx_id = 59,roll_pointer指向之前的32。
此时事务A再次查询，发现59>45,于是就知道 这个版本的数据 不是自己开始事务之前提交的数据，于是沿着版本链查找到32版本，发现32满足条件 于是这次查询的结果就是32版本的数据了，和之前的查询结果一致。
如果A自己更改了数据，改为了值A，那么trx_id=45，此时A再次查找的时候发现45<=45,成立。于是这条数据就是此次查询的结果。
如果此时新开了一个事务C，事务id = 78,将数据更改为了值C，trx_id=78，那么此时A再次查询的时候发现78>45,于是沿着版本链往下找，找到值A这条数据满足条件，于是此次读取结果就是值A。

总结一下，读的时候是基于undo log 版本链来读取数据的，如果trx_id不小于等于读取事务id ，那么沿着版本链一直往下找，直到找到小于读取事务id的第一条。


基于ReadView：实现Read Committed隔离级别：
Read Committed隔离级别情况下的要点是：“每次查询的时候都生成一次ReadView”

还是以上面的以上面的数据为例，原始事务id = 32，现在来了两个事务A(45),B(59)。事务A先去查询的时候 ReadView为m_ids 是[45,59]，min_trx_id是45，max_trx_id是60，creator_trx_id是45，此时查询结果为原始值。
假设此时事务B更改了数据为值B(但是未提交)，那么数据的trx_id = 59,假设此时事务A再次查询，那么这是 ReadView为m_ids 是[45,59]，min_trx_id是45，max_trx_id是60，creator_trx_id是45，那么此时查询结果依然为原始值，因为59>45
假设此时事务B提交了事务，假设此时事务A再次查询，那么这是 ReadView为m_ids 是[45]，min_trx_id是45，max_trx_id是60，creator_trx_id是45，因为此时59不在m_ids 列表中，说明59不是活跃事务了。说明59这个事务在自己开始前就已经存在了，
自己还没结束59这个事务就已经提交了，根据RC定义，那么此时查询的结果就是值B。

可以发现RC级别下 每查询一次 就生成一次ReadView

基于ReadView：实现Repeatable Read隔离级别：

RR级别和RC级别区别就是RC每次读取都生成一次ReadView，RR不会。
其实可以发现，第一个场景例子就是RR级别嘛，由于ReadView在多次查询中都不变，即时在运行时其他事物提交了，它的m_ids列表都是不变了，所以他的多次查询结果是一致的。

由于mysql基于版本链来做查询，RR级别是可以避免幻读问题的，假设在运行时，其他事物插入了一条满足条件的数据，但是由于查询事物的ReadView不变，所以查询结果是不会拾取新增的那条数据的。这样就避免了幻读问题

最后一个脏写问题mysql是怎么解决的？加锁解决

读基于快照读，写基于当前写，并且写是加锁的，通过加锁让特定的行串行化，来避免脏写。



