源码版本1.22
数据结构
关键源码:
|
|
通过read map(只读)和dirty map + 互斥锁两个map来实现读写分离(空间换取时间) entry的三种状态:
- 存活态:正常指向元素
- 软删除态:指向nil(向软删除的数据插入或者更新时会直接通过read map的一个CAS操作进行更新)
- 硬删除态:指向固定的全局变量expunged(在dirty map中已经物理上删除了)
读流程
-
首先读取read map,如果amended状态为false,表示read map中已经是全量数据,这时就看能不能再read map中找到数据,然后如果找到的数据他的entry对应为nil或者expunged也表示没找到。
-
如果read map中没找到并且amended为true时,表示read map中没找到并且read map中不是全量数据时。这时会去dirty map中查找(会加锁),在查找之前会进行一个对read map的
二次检查(防止在此期间其他goroutine的操作让read map的数据覆盖了),如果read中的amended为false时,就会去read中查找并判断返回。如果read map不可用,就读取dirty中的数据判断返回,并把misses++(到达阈值(跟dirty长度相等)时将read中数据进行覆盖)。
覆盖流程
关键源码misslocked:
|
|
如果misses大于等于dirty中数据的数量时,会使用dirty覆盖read并将amended变为false。此时dirty为nil,misses计数器清零。
写流程(插入或更新)
-
如果read中存在key并且entry不是expunged,就可以通过CAS的操作来进行一个更新(直接将entry指向新插入的数据)
-
如果read中不存在或者是硬删除态,就会进入dirty中,同样会先进行一个read的二次检查,如果read中没有,就看dirty中是否存在,存在就进行更新操作(修改dirty),不存在就进行插入操作。插入时要判断amended,如果为false,就会执行dirtyLocked流程,并将amended置为true。
dirtyLocked流程
关键源码dirtyLocked:
|
|
当将dirty覆盖给read之后,dirty应该为nil,如果执行时为nil说明已经执行过了,可以直接返回。如果不是nil,就通过for循环线性的将read中的非删除态数据拷贝回dirty中,并将其中的软删除态的数据更新为硬删除态。
dirtyLocked的拷贝是线性的,性能不好。
诱发dirtyLocked的原因:写多读少 ,大量的新数据的插入,同时很多读操作在read中未命中。造成misslocked,然后又一次写操作越过了read和dirty,就会进入dirtyLocked流程。
删除流程
-
如果read中存在key,通过CAS的操作将对应的value置为nil(软删除态)
-
如果read中不存在,去dirty中查找,会先进行二次检查。如果read中不存在并且amended为true,则去dirty中查找删除(物理删除)并返回旧值(方法要返回),然后将misses++。如果read中存在或者amended为false,就会去read中判断删除。
遍历流程
-
首先获取read,然后判断amended,如果amended为true,就会将dirty中的数据覆盖到read中,并将dirty置为nil并将misses归零。
-
然后遍历read并执行传入的函数,通过返回值判断何时终止。