Reference
[1] https://zhuanlan.zhihu.com/p/38969245
方法1:
对单个数据的更新,可以使用CAS(Compare-and-Swap)指令。
伙计们的操作变成下面这个过程:
伙计A看了下总账户余额是1200,然后记住这个数字,回来计算1200+50=1250,回去修改,一看总账户余额还是1200,于是成功修改为1250;
伙计B看了下总账户余额是1200,然后记住这个数字,回来计算1200-50=1150,回去修改,一看总账户余额是1250,不是原来的1200,说明数据被修改了,需要重新计算,于是记住新的值1250,回去重新计算1250-50=1200,在回去修改,一看总账户余额是1250,于是成功修改为1200。
上面的操作过程就是运用了CAS指令,修改之前先对比,数据没变化说明没有被修改过,这时候才能进行更新。
但是CAS操作时,还有一个ABA的问题。
例如:伙计A动作很快,改了一笔1200->1250,又改了一笔1250->1200;这时候伙计B回来改,看到的1200虽然数量没变,但是已经被改动两次了。
虽然上面的这种情况ABA问题不会有什么影响,但是有时候还是会出问题。
例如:总账户余额是每天记录一个数。
伙计A在今天的总账户余额上面改了一笔1200->1250,掌柜过来翻了一下总账户,翻到了昨天那页,而那页的总账户余额刚好是1200;
伙计B过来更新数据,一看还是1200,就改成了1150,这个改动是有问题的,因为伙计B改的是昨天的总账户余额。
要怎么解决这种ABA问题呢?
只需要在之前的数据基础上,再增加一个日期数据,检查的时候,需要同时检查总账户余额的数据和日期的数据,都没有变化才可以成功更新。
方法2:
引入队列,让一个任务专门来做数据的更新,避免并行运算。
这时候,就是让掌柜一个人来更新总账户余额,伙计们只需要把自己的每一笔业务结果记录在总账户下面。
伙计A有一笔业务是存50两,则在总账户下面记录上+50;
伙计B有一笔业务是取50两,则在总账户下面记录上-50;
而掌柜只需要从总账户里面,顺序的把+50,-50的操作更新到总账户余额就可以了。
这种方法的好处是,避免伙计并行更新总账户余额,发生冲突时的等待,既提高了伙计的效率也保证了数据更新的安全。
方法3:
还是需要有锁的操作,但是让运算的过程更快,减少锁冲突的频率和时间。
前面是伙计先上锁,然后看总账户余额,再回去运算,再回来更新和解锁。
把这个过程改一下,伙计带着算盘过来,先上锁,然后现场运算,更新后解锁,再回去。
这样一来,整个的读写时间变短了,锁的冲突时间也就减少了,效率和性能也就能有所提高。
方法4:
能否进一步减少锁的冲突时间,比如:将读、写的锁分开考虑,毕竟大部分业务中读的次数会远多于写的次数。
A 使用读写锁而不是互斥锁,可以提高并发读的效率,减少读时候的锁冲突。
例如:大量的业务都需要查看总账余额才可以做决定,那么就会有大量的读需求。而多个人一起读不冲突,只是在需要写的时候才独占总账余额的锁。
B 将大的数据分拆为多段的小数据,这样通过多个锁分别作用在小数据上,避免一个锁作用在大的数据中,减少冲突的概率。
例如:对总账余额的每一位设置单独的锁,而不是对整个数设置一个锁。这样的话,+50只需要得到十位数的锁,-3只需要得到个位的锁,如果是+55则要个位和十位两个锁。
C 读的时候不需要锁,而写入的时候串行化同时只能一个人更新
例如:总账余额对所有人公开的,大家可以随便看,但是修改的时候,只能由掌柜来操作。