基础的事务流程(数据不在多个tikv)
事务的存储流程。
Begin
<3,xxx>——<3,frank>
Commit;
1.新begin,然后从pd中获得时间戳TSO,也就是start_ts
2.把修改的数据读取出来,读到tidb server的内存中,内存中先修改,
3.commit进行两阶段提交
一阶段:prewrite:内存中修改的数据写入到tikv节点中,然后将锁信息写入到tikv中。
细:key+tso+value(3_100,frank)写信息存到default的cf中,update和delete只放新值。给修改的第一行的key加个主锁,锁信息放到lock的cf中 <3,(W,pk,3,100……)> 3是key,W是写锁 ,pk表示主锁,key,100是事务开始时间start_ts,后面的不加锁,依附于第一行。
二阶段:commit:pd组件中要时间TSO(commit_ts),还会写个提交信息
细:pd获取时间commit_ts,在write的cf中写入信息<3_110,100> key+start_ts+commit_ts。再做个锁清理,在lock的cf中插入3,(D,pk,3,100……) D表示锁被删了。
tikv中有3类cf
Default 修改的数据
Lock 锁信息
Write 提交信息
在commit之前,begin之后,锁信息别人感知的到吗
锁信息在内存中,不写到tikv节点中,别人感知不到锁,就是乐观事务。
锁信息提前从内存中写入tikv节点,别人也能在commit前感知到锁,就是悲观事务。
读
先到write的cf中看,最近一次修改时间,拿着key+start_ts到default的cf中找到value。
如果在write的cf中没看到值,但是lock的cf中有,那就不能读(有误,待改正)
写
如果数据小于255字节,会直接存储到write的cf中,大于255字节就是正常写到default的cf或者自己指定的cf。
分布式事务流程(数据在多个tikv)
Begin
<1,tom>——<1,jack>
<2,andy>——<2,candy>
Commit;
1.只写新值进去。<1,tom>——<1,jack>只写<1,jack>
2.只有第一行需要加主锁,后面的都是指向锁。
3.update和delete只放新值(put和del)
两行数据放在不同的tikv node上。
1.新begin,然后从pd中获得时间戳TSO,也就是start_ts
2.把修改的数据读取出来,读到tidb server的内存中,内存中先修改,
3.commit进行两阶段提交
一阶段:prewrite(预写):内存中修改的数据写入到tikv节点中,然后将锁信息写入到tikv中。
细:
第一行<1,jack>(放在tikv node1中):
key+tso+value(1_100,jack)写信息存到default的cf中,update和delete只放新值。给修改的第一行的key加个主锁,锁信息放到lock的cf中 <1,(W,pk,1,100……)>,1是key,W是写锁 ,pk表示主锁,key,100是事务开始时间start_ts,后面的不加锁,依附于第一行。
第二行<2,candy>(放在tikv node2中):
key+tso+value(2_100,candy)写信息存到default的cf中,update和delete只放新值。给修改的第二行的key加个锁指向,锁信息放到lock的cf中 <2,(W,@1,2,100……)> ,2是key,W是写锁 ,@1表示锁的指向,指向key1,2表示key,100是事务开始时间start_ts,后面的不加锁,依附于第一行。
二阶段:commit(提交):pd组件中要时间TSO(commit_ts),还会写个提交信息
细:
第一行:
pd获取时间commit_ts,在write的cf中写入信息<1_110,100> key+start_ts+commit_ts。再做个锁清理,在lock的cf中插入1,(D,pk,1,100……) D表示锁被删了。
第二行:
在write的cf中写入信息<2_110,100> key+start_ts+commit_ts。再做个锁清理,在lock的cf中插入2,(D,@1,2,100……) D表示锁被删了。
Tso 120时间 读
看tikv node1的write的cf,看结束时间离120最近的是110,然后开始时间是100,再到default里找value。
2节点一样。
原子性:
如果tikv node1提交了,tikv node2没提交,并且tikv node2宕了。
读取,发现tikv node2的write的cf中没数据,发现lock中有个指向1的锁,然后去node1的lock的cf发现锁已经被删了。在node2的write中补上<2,110_100>,在lock中补上<2,(D,@1,2,100……)>
简单来说:宕了起来后,default中的key和value(数据还在)还在,从1中找到锁的记录,回到2补全commit的二阶段。
MVCC(多版本并发控制)
TSO在120的时候
读取key=1,2,4
1 jack
2 candy
4 tony
事务1完成,事务2未提交,所以读在write里读到的就是上面的值。
假设全部是悲观事务。即使没commit,别人也能感觉到锁。可见图中lock的cf。
假设当前TSO=120
Key=1
想读
在write的cf中读 key(1)离当前时间(120)最近的commit_ts(110)的行,获取到了start_ts(100),拿着开始时间(100)和key(1)去default的cf找value(jack),成功获取值。
想写
在write的cf中读key(1)离当前时间(120)最近的commit_ts(110)的行,获取到了start_ts(100),
拿着开始时间(100)和key(1)去lock的cf找到锁<1,(W,pk,1,115,…..)> ,发现lock中的start_ts(115)大于write的cf中的put<1_110,100>的100,所以有锁且没提交,不能写,造成堵塞。
Key=2
想读
在write的cf中读 key(2)离当前时间(120)最近的commit_ts(110)的行,获取到了start_ts(100),拿着开始时间(100)和key(2)去default的cf找value(candy),成功获取值。
想写
在write的cf中读key(2)离当前时间(120)最近的commit_ts(110)的行,获取到了start_ts(100),拿着开始时间(100)和key(2)去lock的cf找锁,没发现锁,能写,不造成堵塞。
Key=4
想读
在write的cf中读 key(4)离当前时间(120)最近的commit_ts(90)的行,获取到了start_ts(80),拿着开始时间(80)和key(4)去default的cf找value(tony),成功获取值。
想写
在write的cf中读key(4)离当前时间(120)最近的commit_ts(90)的行,获取到了start_ts(80),拿着开始时间(80)和key(4)去lock的cf找到锁<4,(W,@1,115,…..)> ,根据@1去找到key=1的锁,发现有锁存在,所以有锁且没提交,不能写,造成堵塞。