使用async i / o编写数据库

时间:2018-02-13 04:54:22

标签: node.js database libuv

我最近遇到了libuv,这是一个低级库,可让nodejs执行异步魔术。这让我思考。我想澄清这些方面:

  1. Nodejs具有异步i / o调用。但是,如果我将API调用到(远程)数据库,则对db的实际读/写将是同步的,但节点不必等待它。有可能使db本身可以在异步中写入磁盘吗?是否有任何数据库使用libuv进行实际的异步i / o?

  2. Javascript是着名的单线程。我知道nodejs运行时不需要 - 如果我有4个cpu核,我可以启动4个实例。但是,如果我使用libuv以支持线程的语言编写另一个Web框架,那么它不具备异步i / o和多线程的所有优点吗?这样的事情已经存在吗?

1 个答案:

答案 0 :(得分:1)

你混淆了两个概念。在对服务进行查询时,您可以异步等待(通过epoll / kpoll / libuv ...)并不意味着您的查询在另一方面是非阻塞的,反之亦然。这并不意味着,在你的事件循环中,事物“感觉”是异步的,它们确实是真的。

让我们回到事件循环是什么以及nodeJS如何发挥其魔力。我觉得这是一个很好的开始。

事件循环的可见部分是代码编写方式的变化 - 从大多数同步到大多数异步。不可见的部分是这个异步代码尽可能地抛出在事件循环上,在后台,它会检查要做的事情 - IO,定时器等。这不是一个新想法,它可以完成它的工作(提供并发性非常好。

libuv的文档实际上非常具有描述性。 Over there描述了他们所采用的设计选择,并从中得出了这个流程图:

The libuv flow

请注意,他们没有说明他们已经做了任何真正的异步 - 因为他们没有。底层系统调用保持同步。只是感觉就像它不是。这是关键的外卖。

关于数据库上的磁盘I / O,我在海牙发表了一段时间的谈话,坦率地说,大多数关键的I / O都是阻塞的。例如,你不能去“嘿,我会同时更新磁盘快照仅附加txlog!” - 因为,如果其中一个失败了,你就会遇到一个严重的,严重的回滚问题,可能还有未知状态。

关于问题2,我会给出代码示例,但我不确定您熟悉哪种语言。最重要的是,当一些东西越过一个线程边界时,事情变得地狱。一个非常天真的例子就是这样 - 假设你的事件循环有两个如下定时器:

  • 定时器1,每0.5秒触发一次,递增给定的状态变量A
  • 定时器2,每当有人提供用户输入时触发,将状态变量除以2。

假设您在单线程上运行。即使您的事件循环感觉异步,它也是完全顺序的 - 当计时器2运行时,计时器1将从不运行。

现在添加第二个线程,从中运行定时器2。如果没有一个警卫,那么在某个地方出现问题的可能性很大。

为了能够以天真的方式划分2个东西(不利用专用于此类东西的CPU指令),必须检索变量,将其除以2,然后将其重新设置在内存中。

同样,增量也是一个三阶段的过程(再次,采取天真的方法)。

一旦这两个计时器发生冲突,你就可以获得如下的疯狂竞争条件:

THREAD 1          | THREAD 2
   <- A=1         |
 Local:A=1+1=2    |  <- A=1
                  |  Local: A=1*2=2
     A=2 ->       |  A=2 ->

线程2在线程1的计算中途开始运行,检索到错误的状态变量值(因为线程1尚未更新变量),并将其乘以2.你应该有3,但实际上你最终得到了2。

为了防止这种情况,有很多方法和工具。如今,大多数处理器体系结构都有原子指令(例如Intel),开发人员可以利用那些,如果他们知道他们需要的地方。你可以拥有一大堆工具 - 互斥体,读/写锁,信号量等......以减少或消除这些问题,成本时< / em>你知道 你需要它们。

毋庸置疑,要概括这一点远非微不足道。