-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
如何从理论上避免这类并行任务交错执行时的冲突问题 #32
Comments
可能会有人抱怨这是 中的执行流程的协调问题:
|
https://www.v2ex.com/t/908047#r_12562608 https://www.v2ex.com/t/908047#r_12564068
然后在每次 但不论 UPSERT 还是 IGNORE 都是从数据库层面缓解问题,他不是保证永不发生 DUPLICATE 错误,而是保证发生 DUPLICATE 错误后您的程序也能跑(因为一个改成了 UPDATE ,一个将 ERR 降级到 WARN ) 而我更需要避免的是这种类似 DUPLICATE 造成了数据冗余,但又完全符合数据库层的 UNIQUE 约束的问题: 对此问题我当然可以选择写一个基于window function的 https://www.v2ex.com/t/908047#r_12564332 https://www.v2ex.com/t/908047#r_12564368 https://www.v2ex.com/t/908047#r_12565781
如果要从数据库层面解决可以尝试 7.
|
https://www.v2ex.com/t/908047#r_12587293
PS: 这些是基于单体服务的设计. 我不喜欢用 https://www.v2ex.com/t/908047#r_12589945
哪怕再怎么分布式(实际上同一个进程里的多线程也已经是分布式了所以才会有这些并行竞争问题),不论是无主还是有主的分布式网络,最终都得往一个单点(接受 INSERT 写操作的单主数据库网络)上竞争资源(插入什么行),那这就需要协调来避免竟态(同时插入导致表里存了重复行,但这会被数据库本身的 UNIQUE 约束给拦截所以导致各个线程收到 duplicate entry 错误,但我又不希望某个线程只是因为插入的一大批行中有一些已经存在了就导致整个事务回滚),除非能让分布式数据库也变成多主网络( apache hadoop 那样),那每个任务节点就可以只向固定的数据库节点 INSERT (但仍然需要在接受 INSERT 写操作的所有数据库节点之间不断同步数据从而保证不会有相同 key 但 value 却不同的记录,也就是保证数据一致性)
根据 https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-intention-locks 的那张表格 |
https://www.v2ex.com/t/908047#r_12595620
PS: https://www.v2ex.com/t/908047#r_12596633
我遇到的不是幻读而是“滞后”读,您可以从图 1 中看出来线程 2 读取进程锁时线程 1 已经释放了线程 1 此前插入并锁的行 a ,而线程 2 此前从数据库中读时还不存在行 a ,所以导致线程 2 从两处取得(时机上一个太早一个太晚)的事实都是不存在行 a
准确地说进程锁缓存着的行在数据库中有 3 种状态:即将 INSERT (还没 COMMIT ),已经 INSERT ( COMMIT 了,只有这个状态下是
是
的确可以提前不去生成已经被进程锁占据着的行的数据
所以阁下的意思还是建议使用您最初提出的
根据 https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-gap-locks
而这里的 a 所在的字段是具有 UNIQUE 约束的并且 SELECT 的 WHERE 子句使用=而不是< > BETWEEN 等 range operator ,所以只有 row lock 而没有 gap lock
这应该暗示了
这可能也就是我在生产中只要使用 mysql 默认的
建议无脑降低事务隔离级别至 |
蛮怪的。我不懂数据库但是,既然
这应该也是不对的吧?允许 |
我的理解是这里需要的行为实际上是一个原子的 RMW操作 ——如何插入这一行是依赖于 相同的行是否已被插入 这个要读取到的条件的。这个读操作+插入操作整体上必须是原子的/事务的。
其中
实际上维基百科关于上面说的RMW操作的 词条 也已指出, CAS 、 LL/SC 具有更高的 共识数 ,不可能只用 原子读 、原子写 之类的具有更低 共识数 的操作实现,无论做多少个这样的操作。 关于这个方案再多说几点。
总之,这是一个很通用的原理。我猜对于数据库的分布式访问来说应该也是有原理相当的构造的。只是发生在更长的延时、更大的数据、更复杂的数据结构。 |
在图 SQL中的
实际上对于mysql innodb而言
但这一切对数据库用户而言都是无关的(抽象隔离),用户只需要知道
实际运行时环境的线程比这的2个更多,并且在
如果阁下的
我没有说把事务隔离级别从 |
是,所以阁下也看到了我后来需要给
而仅靠一个与mysqld进程毫无关系的进程范围全局锁是不可能保证
所以我们的
而对于SQL的 START TRANSACTION;
SELECT ...;
INSERT ...;
COMMIT; 那么此时如果某个语句显式声明了锁,如
而对于这种传统的阻塞锁设计而言只要复数个线程请求同一资源的频率足够高就很容易导致
所以传统数据库厂商无法把CPU指令集层面提供的
只要引入了对
传统老牌RDBMS中基本没有什么无锁数据结构,毕竟其内部结构实现过于复杂恶俗使得他们无从下手改造成无锁的,这也是为什么v2ex的 @daxiguaya 于 https://www.v2ex.com/t/908047#r_12595620 指出
|
而我也大量使用了
然而很明显
https://www.red-gate.com/simple-talk/blogs/inside-the-concurrent-collections-concurrentdictionary/ 进一步指出:
private void GetBucketAndLockNo(
int hashcode, out int bucketNo, out int lockNo, int bucketCount) {
// the bucket number is the hashcode (without the initial sign bit)
// modulo the number of buckets
bucketNo = (hashcode & 0x7fffffff) % bucketCount;
// and the lock number is the bucket number modulo the number of locks
lockNo = bucketNo % m_locks.Length;
}
所以就有了基于 进一步的我在 |
TL;DR: 我在mysql中使用
事实核查:截止2023年1月,我仍然无法直接在生产环境中删除进程锁,因为我观察到在删除后仍然会造成这种 |
其实你只要说这句就够了,其它诸如持久化等在这个问题当中似乎是无关的吧。
不难理解当
实际上是可能的,只不过大概不是你要的效果。那就是所有需要原子操作的地方获取全局锁。意味着不论这些原子操作访问了什么,都无法并行。
显然用 CAS 或者 LL/SC 很容易实现 test-and-set ,反过来则不行。 除了这种完全舍弃并行的做法以外,既然这个事务性是跟 读取集 写入集 相关联的,那么我当然认为这种事务约束理应由数据库而不是调用者实现。
我想这并不是一回事。共识数跟操作的宽度并没有必然关系吧。
也就是说不修改对象,而是每次都创建新的对象,然后对指针做 CAS 。在 GC 环境下是可行的,在非 GC 环境下则会面临释放时机的问题,还有 ABA问题 。 |
这很合理。但是……
但不太能理解的是为什么它一定要让其它事务阻塞等待,而不是先返回不可靠的
我上面说 |
其实你只要说这句就够了,其它诸如intel/arm risc/cisc指令集之争等在这个问题当中似乎是无关的吧。
在 回顾经典之 请注意本讨论串中的所有
另外请注意尽管
https://dev.mysql.com/doc/refman/8.0/en/commit.html 对此早有预言:
移出去会导致单条
在 然而so人早已道明真相: https://stackoverflow.com/questions/1171749/what-does-a-transaction-around-a-single-statement-do
我的评价是:疑似当代 https://en.wikipedia.org/wiki/Cargo_cult https://en.wikipedia.org/wiki/Cargo_cult_programming https://stevemcconnell.com/articles/cargo-cult-software-engineering/
这就是
开头的例子里也不是只有一个
无,所以需要乐观并发控制,如MSSQL中基于一个单调自增的
然而更现实的问题是乐观并发控制是用于协调
这就是阁下之后所提到的 https://en.wikipedia.org/wiki/ABA_problem 而乐观并发控制本质上也是为了解决这个
然而将单个值上升到集合层面就麻烦的多(什么pythonic)
然而很明显没人想要
然而testandset同样是一种atomic操作,他的名字就已经暗示了他是把两个常见的原本是独立的原子操作给封装成又一个原子操作
https://en.wikipedia.org/wiki/Consensus_(computer_science)#Consensus_number 的表格中也进一步指出:
事实核查:截止2023年1月,仍然没有RDBMS实现了杨博文阁下所提出的这种全新的具有颠覆性的基于changeset的事务隔离机制
您在写
那阁下实际上是在对机器字长32/64位的指针做CAS,而CPU指令集当然会提供能对机器字长长的数据进行CAS的指令
为什么这里会有ABA
疑似
什么整数? |
我呃呃,你要不自杀吧 |
西兔人您来辣 |
#32 (comment) 早已做出循环论证:
而 从实用角度讲数据库用户期望的是获取可靠的值,但却拿到了不可靠的值,那用户该如何进行后续的假设?
也就是mysql默认事务隔离级别
然而问题在于
我局的是
最容易遇到的还是死锁,其次是这种活锁,并且mysql无法主动检测出一直有多个事务在争夺同一资源(行集合)并介入其中(比如暂时把资源改成serializability的以便让事务们缓缓通过),除非阁下愿意像老DBA那样247高强度盯着netdata收集的metrics然后手动分析 |
提请 |
我还在大做特做生信,只不过之前因为某些互联网混沌言论一转虚无了。在这边之后我除了项目的事情几乎没有和外界有联系,所以几乎一个人都不认识 远离魔怔,最幸福的人,,, |
是的,但我完全不知道在数据库领域中相近的概念叫什么,于是只好就我了解到这些概念的领域来说。
然而不论您称呼它 dirty read 还是 phantom read ,我都仍然不知道那都是什么意思。 |
Interlocked 操作应该是封装的处理器提供的指令。 x86 的指令本就提供原子性,而且大都可以直接加前缀
听上去有点有趣,希望能搞明白是为什么就好了。我是觉得你这里理应是不需要多一个进程锁的。
比起说是过度应用一致性,我看不如说是不理解一致性。
是的,它是乐观的。然后这个问题的话……我知道了,它通过
不这不是。 ABA 问题,顾名思义,就是说 CAS 的时候读到的值跟之前读到的值是一样的,所以 CAS 会成功,但其实这个值已经被其它线程修改过又改回来了,不应该让这个 CAS 成功。如果这里的正确行为只依赖于被 CAS 的这个值本身的话这是不成问题的,成问题的情况是虽然这个共享变量本身是一样的但因为修改过所以已经不能当作仍然满足条件了。最典型的就是它是个指针,被修改过又改回来了,但它指向的东西已经不一样了,这种变化却不能被这个指针变量上的 CAS 捕获。
读取的时候当然不应该修改
当然了。但组成它的两个操作本身只有 共识数 1 ,而 test-and-set 具有 共识数 2 ——如果您只有两个线程的话,让它们彼此等待对方就好啦。
我不相信。况且您都能说出 changeset 这个词,怎么想这东西都应该早已出现在 RDBMS 领域当中了吧。
显然我在写伪代码。而且我显然不喜欢这样写代码,只不过在照顾某位依赖机器翻译的人罢了。
很简单:某个线程写入了这个指针,然后把不再被用到的旧指针释放了。然后某个线程又做了一遍这个过程。但之前被释放了的指针可能又被分配到,于是此期间一直没有读过这个变量的另一个线程 compare 到了跟它之前读到的相同的指针,但这个相同的指针指向的值其实已经不一样了。所以 ABA 。
跟
您好。原来您就是他们先前说的 西兔 。久仰大名。 |
远离邪恶组织四叶重工,做最幸福的人,,,
他没说他是否业已照会了新创lys神吧,您进了新创无际开发群之后没见到西兔人吗? |
没有已知证据表明我有见到。 |
而
enwiki进一步澄清:
因此LLSC主要是为了解决CAS可能遇到的ABA问题
反转了不是所有的
中的措辞
需要替换为
那也没有更好的办法表达并行事件之间的关联了
阁下是指在现代多核cpu中由于每个core都有着自己的L1cache所以跑在不同core上的指令有可能在读取同一个地址上不同的L1cache值(也就是desync)吗?我的建议是滥用 |
xp时有有
我也认为引入
本来把多个SQL语句套进一个事务里就只是为了让他们变成一个原子操作,使得这些语句所造成的影响(
与此同时截止2023年1月,
一个朴素的
因为乐观并发控制依赖于一个已有的
这就是
然而乐观并发控制本就依赖于观察 所以 https://www.v2ex.com/t/908047#r_12564068 的 @codehz 早已道明:
他所说的
然而又有新的问题:
https://en.wikipedia.org/wiki/Compare-and-swap#ABA_problem 进一步指出:
然而DCAS中的额外自增就类似乐观并发控制中使用的自增
回顾经典之logrotate:
https://en.wikipedia.org/wiki/ABA_problem#Tagged_state_reference :
https://en.wikipedia.org/wiki/ABA_problem 指出:
enwiki同时声称:
真这样做最现实的问题这些行基本上就没法被其他事务读取了(如果自增
经典mutex阻塞锁
RDBMS中的changeset是MVCC中的
企业级orm如EFCore中的changeset是changetracking的结果集: https://learn.microsoft.com/en-us/ef/core/change-tracking/
真这样做可能会违反
而从现在的分布式网络角度来看更容易遇到的是不知道
这就像在使用乐观并发控制时要判断
然而我也看不懂中文编程之 wenyan-lang/wenyan#617
不开机翻我也慢慢读懂(30~100words/min),而开机翻更快(500~700字/分钟)也方便大段引用复制粘贴 |
所以 ldarx 就是 LL/SC 的 intrinsic 咯。 std::mutex g_mtx;
std::map<int, int> g_map;
if (_xbegin() == 0xFFFF) {
// proceed to transactional access to global data
g_map.insert(3, 4);
_xend();
} else {
// fallback to mutex
std::unique_lock lock(g_mtx);
g_map.insert(3, 4);
} 里面的所有读写都会被追踪事务性。然后 _xend 的时候,如果没有冲突的话写入会生效,相当于成功的 LL/SC ;反之如果失败的话处理器会把所有状态回滚到 _xbegin 的地方,然后 _xbegin 会返回一个表示失败的返回值。(是的, if 里已经执行过的代码变成就像没发生一样)
就是当前事务读到尚未提交的事务中已经做出的
已经
也就是跟 non-repeatable read 差不多,但是针对插入行而不是修改行。 |
比画时序图要严密但是不直观的,是描述操作之间的 happens-before 约束吧。 A happens-before B 就是说 B 能看到 A 的副作用。 happens-before 具有传递性。
这是不对的。实际上
我觉得应该不会实现成 P/Invoke 或者 syscall 了对应的系统 API 。这些 Interlocked 操作是很轻量的,基本上是单个原生指令的封装。实现成 P/Invoke 甚至 syscall 的话就太慢了。理应是直接产生对应的汇编指令的。
……原来是这样啊。我有点明白了。 这个部分之前没看明白,现在才明白了。那我觉得如果你不想那么“补救”的话,那这个问题可以分成以下几个部分:
|
类比数据库的事务隔离级别的话,应该是相当于最高的 |
这跟数据库引擎的实现里面有没有采用 CPU 指令集提供的无锁原子操作其实关系不大。这更多是关于它的接口采取怎样的设计的问题。单个 SQL 语句发生在远远超过单个 CPU 指令的时间跨度上。(显然需要执行十万甚至九万个指令来 parse 并执行您的一个 SQL 语句)
建议阅读该词条引用 Wait-Free Synchronization 。
|
然而如果此时其他并行线程读取了
然而数据库事务的
数据库事务的
否
实际上
在数据库事务中
是
是 SELECT a, b FROM t; -- (1, 2)
UPDATE t SET b = 2+1 WHERE a = 1 因为您想要 UPDATE t SET b = 2 WHERE a = 1 所造成的影响,如果另一个事务最终
但您可以在此使用乐观并发控制的思维改成 UPDATE t SET b = 2+1 WHERE a = 1 AND b = 2 这样在另一个事务 https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html 也详细说明了为什么会这样(尽管这段来自对
所以实践中很少有人用
以及在各种非传统的DBMS如列存储数据库(如yandex的clickhouse)中根本没有实现事务(主要是为了性能以及实现这些过于复杂),也就是说一切
然而在 https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html 进一步指出:
但又指出
这也就解释了上文中的
事务的所谓
mysql innodb中对MVCC的实现是undo log:
因此
这得看两个事务A和B分别执行
无法,因为事务B会阻塞等待A的
https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html 早已指出
而
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-shared-exclusive-locks 进一步指出:
|
然而从字面上看
您也可以画一大堆
事实核查:截止2023年1月,
但 https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockedcompareexchange 的确是 根据 我横竖睡不着,仔细看了半夜,才从 https://en.wikipedia.org/wiki/Shared_Source_Common_Language_Infrastructure 中看出字来,每叶都写着
然而如果要求对两个int的读是同一个原子操作那的确有
简化这个表的字段到
如果 - 4, a
+ 4, a
+ 3, a
+ 2, b
+ 1, a 而如果 - 4, a
- 4, a
- 3, a
+ 2, b
- 1, a 然而我想要的是 - 4, a
- 4, a
+ 3, a
+ 2, b
+ 1, a
即便要查询出这种模式都无法只通过
而是需要类似 https://stackoverflow.com/questions/34599599/group-by-adjacent-rows-based-on-two-columns/34599655#34599655
补救其实是事务执行一段时间后再来定期检测然后回退这些
所以本帖的标题才叫
是
是
无
是
数据库 并且根据 #32 (comment)
DML甚至会绕过事务隔离级别对普通(无
什么叫
提交什么?其他事务中的 |
#32 (comment) 早已提出质疑:
|
是,如同无锁数据结构的内部实现也并不一定非要用cpu提供的这些原子操作指令,他也可以继续用传统的mutex阻塞就像concurrentdict: #32 (comment)
回顾经典之
截止2023年1月,DBA面试题八股文仍在考您mysql中的myisam table lock和innodb record/gap/nextkey lock: https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-insert-intention-locks 其原因正是由于失败的抽象隔离导致数据库用户遇到任何
经典阅读理解26页长的于1991年发表于TOPLAS的论文 而阁下引用的这篇论文pdf也是经典后期ocr然后叠加classifier输出的文本框嵌入pdf实现可选中复制的文本,例如用于google books数据集的 https://github.com/tesseract-ocr/tesseract : |
我也有点好奇这个问题该怎么解决。因为这个锁显然是和数据库有关的,或者更宽泛地说,某一个有限的并且具有抢占性质的资源。 一般来说反应式或者异步的advocator会鼓吹异步的无锁开发的优越性,但是显然的,这种语境下的锁和资源锁是两回事。如果涉及到对有限的资源的依赖和随时更新的话,那还是会遇到这个issue里提到的时序问题。 假设在一个DBMS里面可以通过设定事务的等级来解决这个问题,那也顶多只能说是DBMS帮我实现了这个锁(顺便实现了C10K)。 那么如果是一个在自己的容器里面要去实现的资源呢?自己去实现MVCC嘛。 如果是完全用异步的写法的话还会有个问题,因为不允许阻塞进程了,所以不能用Java壬常用的无脑加锁的办法来解决,那就更麻烦了。但是你也不能说比如说我这个组件用了这种写法就强迫别人也异步吧(什么传染性) |
是。这就是主要的差别,客户端跑在 CPU 里所以 CPU 可以让客户端把已经知道的事情忘掉,而数据库引擎当然办不到。
然而 Intel® 64 and IA-32 Architectures Software Developer’s Manual 16.3.6 中早已指出:
并且我合理怀疑它并不是
一样的,这里单个核心独占的 L1 就起到了这个 SNAPSHOT 的作用。如何在这个 SNAPSHOT 跟共享的主存储器之间同步是本来就要处理的问题,事务内存只是扩充了暴露给程序员的接口,让程序员可以更有效地利用处理器早就实现了的机制。
这倒是很合理,而且跟 CAS 是一个原理。
想必正好适合
这种情况下如果事务A采取上面说的
能否因为事务B的修改而导致这一写入
但是
既然如此,那么如果: A事务 SELECT a, b FROM t; -- (1, 2) B事务 UPDATE t SET b = 5 WHERE a = 1;
COMMIT; A事务 UPDATE t SET b = 6 WHERE b = 5;
SELECT a, b FROM t; -- (1, 2) or (1, 6) ? 既然
|
虽然
而且就 MSVC 而言它确实同时也是 compiler intrinsic 。意思是说编译器会直接内联对应的汇编指令,不会真的写一个函数调用,除非您设置编译选项来禁用 compiler intrinsics 。
是自由,但它既然用了
其实不能。您仔细看一下,两个形参只有一个是 可以进一步想一想。所谓 换一种方式来说就是。您原子地读两个不同地址,可是如果写入者不能够原子地写两个不同的地址,只能够原子地写一个地址的话,那么您并无法区分, |
从我看来这不是一个关于 具有抢占性质的资源 的问题,而是一个关于 所需的原子语义并没有被数据库层实现 的问题。
这难道不该反过来说吗?如果某个组件采取同步的阻塞的写法,那这会传染给它的用户,用户想异步也不好异步了,除非为这段代码开一个线程然后在主线程里异步等待它。 |
是 https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html 对此早有预言:
|
原文发表于 https://www.v2ex.com/t/908047
假设有一个进程,其内部有一个全局的锁和两个(或多个)互相不知道对方存在的异步任务正在运行
和一个外部数据库中的某个表,假设表只有一个字段并且是
UNIQUE
约束。数据库每SESSION
的隔离级别是READ COMMITTED
任务的目的是向表中插入一些行
START TRANSACTION
),然后排除掉表中已有的即将插入的已排除了表中已有行
的行COMMIT
表明成功插入后因此当存在两个并行执行的这个任务时可能有符合如下uml时序图的流程:
利用 https://www.websequencediagrams.com 在线渲染:
DUPLICATED
表示线程2
试图插入已经被线程1
插入了的行,因此违反了数据库层的UNIQUE
约束对上图填充实际数据后
可以看出在符合这个时序图的流程中进程锁和数据库层事务都无法阻止这种冲突,因为
线程2
访问数据库表中已有行的时机早于线程1``COMMIT
他的INSERT
,所以线程2
无法预见线程1
将在未来插入行a
(由于READ COMMITED
事务隔离级别)线程2
访问进程锁的时机又晚于线程1
完成COMMIT
和释放进程锁中的行a
,所以线程2
也不知道此前线程1
已经插入了行a
提出的解决方法:
1.延后
线程2
查询数据库的时机:如果让
线程1
等待线程2
释放了进程锁之后才开始查询表,那么线程1
就可以从表中得知行a
此前已经被插入了(但线程1
并不知道这是线程2
插入的)这实际上就是
serializability
(linearizability
和serializability
的区别),即完全放弃了并行度,同一时间只能有一个任务在工作。这也意味着进程锁此时也是完全无用的,因为他的主要目的是让其他线程知道当前有哪些行即将插入表(但还没有COMMIT
)2.延后
线程1
释放进程锁的时机:如果让
线程1
等待线程2
查询进程锁之后再去释放锁,那么线程2
就可以知道不应该插入行a
缺点:
线程1
必须知晓线程2
的存在并等待线程2
开始查询进程锁后线程1
才能释放锁并完成他的任务销毁线程。实际上线程不应该知道其他线程也在同时运行,他们本应只需要与进程范围的全局锁通信FIFO stack
(见下)来缓解3.数据库事务隔离级别从
READ COMMITED
降至READ UNCOMMITED
回顾经典之
可见降至
READ UNCOMMITED
后允许dirty read
的发生,也就是对于如下时序:线程2
可以在线程1
已经向数据库发送了INSERT
,但还没发送COMMIT
从而提交事务之前就得知行a
已经被插入了缺点:
对于最初的时序流程
仍然不适用,因为没有任何约束使得
线程2
不能在线程1
向数据库发送INSERT
之前就查询表,自然也就没有发生dirty read
4.缓存最近插入的行的进程范围全局
FIFO stack
如果
线程1
保证在释放进程锁的行a
之前先将其push,那么线程2
就可以在pop时发现行a
已经被插入,尽管这时进程锁中表明行a
没有被锁(即正准备插入但尚未COMMIT
)缺点:
线程1
push行a
后又有线程3push或pop了其他行,那么线程1
仍然不知道行a
已经被插入。因此实践中需要使用List
等可随机访问的结构来在所有历史中查找已插入的行(这相当于直接去数据库查表)5.2PC(两阶段提交)或2PL(两阶段锁)以协调任务
6.外部程序控制锁以协调任务,如各类message queue或zookeeper
由于我对分布式/并行计算理论不甚了解,所以希望v2ex的各位v友们能够补充更多理论上以及实践中如何处理这类问题的方案
The text was updated successfully, but these errors were encountered: