JQuery日记6.5 Javascript异步模型(二)

mnesia在频繁操作数据的过程可能会报错:** WARNING ** Mnesia is overloaded: {dump_log, write_threshold},可以看出,mnesia应该是过载了。这个警告在mnesia dump操作会发生这个问题,表类型为disc_only_copies 、disc_copies都可能会发生。

如何重现这个问题,例子的场景是多个进程同时在不断地mnesia:dirty_write/2

mnesia过载分析

1、抛出警告是在mnesia 增加dump worker的时候

mnesia_controller.erl

抛出警告是当Worker的#dump_log.opt_reply_to 未定义,仔细看这里的代码,这一步先检查了dumper_queue里的worker

所以,mnesia抛出过载警告有2个条件:

1)当worker的#dump_log.opt_reply_to 未定义

2)dumper_queue有相同操作(InitBy)的worker

2、那什么样的worker的#dump_log.opt_reply_to 未定义?

代码也在mnesia_controller.erl,这里add的worker的dump_log.opt_reply_to 未定义,而{async_dump_log, InitBy} 就是 mnesia:dirty_write/2的过程中调用mnesia_controller:async_dump_log(write_threshold) 产生的。

就是说,mnesia:dirty_write/2会触发异步dump操作,而只有异步的dump会导致mnesia抛出过载警告

3、看一下,mnesia什么时候会修正worker?

代码也在mnesia_controller.erl,在dump完成时,mnesia会修改worker的dump_log.opt_reply_to,然后移出dumper_queue

从上面可以得到结论,mnesia:dirty_write/2的操作是会触发异步dump操作,每次dump操作mnesia都会加到dumper_queue队列,mnesia通过检查dumper_queue是否存有相同操作的worker来检查是否过载

mnesia dump分析

mnesia数据存储实际上使用的是ets和dets,对于ram_copies类型的表使用ets;disc_copies表也使用ets,通过dump将数据保存到*.DCD(disc copy data)文件来持久化,中间可能会用*.DCL(disc copy log)转储;而disc_only_copies表使用的是dets,保存的文件为*.DAT。

表类型不同,mnesia记录数据的过程也不同,这里先讨论mnesia 记录disc_copies数据的过程。

1、mnesia 记录disc_copies数据有2个过程:

1)操作先记录到日志文件LATEST.LOG,然后再dump到*.DCD文件,同时清除LATEST.LOG

2)把修改同步到ets表中

2、mnesia disc_copies表数据dump过程

1)将日志文件LATEST.LOG重命名为PREVIOUS.LOG,然后再新建一个空的日志文件LATEST.LOG

2)分析PREVIOUS.LOG文件中的内容,将disc_copies的表实际修改写到*.DCL文件

3)比较*.DCL和*.DCD的大小,当filesize(*.DCL) > filesize(*.DCD) / dc_dump_limit,把*.DCL的记录存储到*.DCD文件中。dc_dump_limit默认为4,可以通过-mnesia dc_dump_limit Number设置

3、mnesia什么时候会dump

1)定时触发

mnesia启动后,mnesia_controller进程设置定时器,触发dump

mnesia_controller.erl:

默认值为180000,可以通过 -mnesia dump_log_time_threshold 300000 设置。

2)一定次数的操作后触发

每次数据操作,mnesia都会调用mnesia_log:log/1或者mnesia_log:slog/1进行日志记录,记录一次日志就将trans_log_writes_left的值减1,当这个值为0时,触发dump

mnesia_log.erl:

mnesia_dumper.erl :

默认值为1000,可以通过 -mnesia dump_log_write_threshold 50000 设置。

3)手动dump

手动调用 mnesia:dump_log/0  可以强制mnesia 完成dump,而这个dump是同步的

mnesia.erl:

mnesia_controller.erl:

解决mnesia过载

结合上面的分析再谈谈mnesia过载问题,dict_copies表写数据的时候,mnesia会写记录到ets表和日志文件LATEST.LOG,然后定时或定量dump做持久化。通过dump_log_write_threshold /dump_log_time_threshold 可以控制持久化的频率。mnesia在dump数据的时候,如果上一个worker进程dump没完成,就抛出过载警告。对此,dump_log_write_threshold的值表示mnesia经历过多少数据操作做一次持久化,dump_log_time_threshold的值表示mnesia多长时间做一次持久化。

这里再谈谈,为何同一时间只能有一个dumper?

dump的过程是先将日志文件重命名为PREVIOUS.LOG,然后分析PREVIOUS.LOG的数据做持久化,如果同时有第二个dump,将会替换掉第一个dump的PREVIOUS.LOG,影响第一个dump的持久化。那么,聪明的你就会这么想,为何不重命名为XXX.LOG,每次重命名都不同?事实上,如果同时有两个dumper,mnesia仅保证第二个dump能正常进行,放弃掉第一个dump的数据。所以,mnesia出现过载警告的时候,数据有可能会丢失。

这里,我做过了一项测试,修改mnesia的代码,将所有异步dump去掉,改用定时手动dump。还是原来的例子,发现第一个dump还没完成日志文件的分析和持久化,而新的日志文件已经增长到快2G。

dump的过程在文件io层面上其实是,一边在没有控制的追加数据,一边又在分析文件和有序写入,这个过程是在挑战磁盘io的读写极限啊。所以,就算现在有多个dumper,结果只会让cpu和硬盘更加抓狂。

另外,别太过依赖dump_log_write_threshold/dump_log_time_threshold这两个参数,改大了就有用吗?

这两个参数改大了,就是说,dump的频率就会降低,那么等待dump的数据就会更多,dump花的时间将会越长,到头来还是不能解决到问题。这两个参数的意义在于平缓写入速度,避免一时间大量数据写入造成数据丢失。但是,如果每时每刻都是高密度写入,硬盘也承受不了,一般到了这个局面,问题应该从数据缓冲和持久化的设计上去解决,而不是想着换一个数据库去解决。

这里有一点经验可以分享一下:

1、在mnesia没报过载错误的时候,不建议去改动,调节这些参数会影响持久化

2、可以多个进程读mnesia的数据,但写数据的过程只交给少数几个进程去完成

参考:

http://blog.csdn.net/mycwq/article/details/28660813

http://my.oschina.net/hncscwc/blog/161763

JQuery日记6.5 Javascript异步模型(二)

时间: 2024-10-09 09:07:43

JQuery日记6.5 Javascript异步模型(二)的相关文章

JQuery日记6.5 Javascript异步模型(一)

在了解JQuery的异步队列实现之前,有必要去了解javascript的异步模型. Javascript的异步其实并不算严格意义上的异步,js的异步是指让某段代码片段在将来再执行,而不是让执行流不必等待继续向下进行. 在多线程的语言中最容易想到的异步方式就是在当前线程中,新创建一个线程让某段代码片段运行在新创建的线程中,从而使当前线程继续向下进行. 而任何一本书关于js的书都会告诉我们js是运行在单线程里的,这个线程称为UI线程,从名字就知道这个线程不光用于运行js代码,还负责事件的处理和UI的

JQuery日记6.5 Javascript异步模式(一)

理解力JQuery前实现异步队列,有必要理解javascript异步模式. Javascript异步其实并不严重格异步感,js使某些片段异步方式在将来运行,流不必等待继续向下进行. 在多线程的语言中最easy想到的异步方式就是在当前线程中,新创建一个线程让某段代码片段执行在新创建的线程中,从而使当前线程继续向下进行. 而不论什么一本书关于js的书都会告诉我们js是执行在单线程里的,这个线程称为UI线程,从名字就知道这个线程不光用于执行js代码,还负责事件的处理和UI的绘制. 在游览器内部维护着一

Javascript异步编程之二回调函数

上一节讲异步原理的时候基本上把回掉函数也捎带讲了一些,这节主要举几个例子来具体化一下.在开始之前,首先要明白一件事,在javascript里函数可以作为参数进行传递,这里涉及到高阶函数的概念,大家可以自行google一下. 传统的同步函数需要返回一个结果的话都是通过return语句实现,例如: function foo() { var a = 3, b = 2; return a+b; } var c = foo(); console.log(c); //5 就是说后面的代码console.lo

JavaScript异步编程设计快速响应的网络应用

JavaScript已然成为了多媒体.多任务.多内核网络世界中的一种单线程语言.其利用事件模型处理异步触发任务的行为成就了JavaScript作为开发语言的利器.如何深入理解和掌握JavaScript异步编程变得尤为重要!!!<JavaScript异步编程设计快速响应的网络应用>提供了一些方法和灵感. 一.深入理解JavaScript事件 1. 事件的调度 JavaScript事件处理器在线程空闲之前不会运行(空闲时运行). var start = new Date(); setTimeout

Javascript异步编程之setTimeout与setInterval详解分析(一)

Javascript异步编程之setTimeout与setInterval 在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程( 注意: 特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛多多原谅!) 1. setTimeout与setInterval详细分析基本原理. 接下来这篇博客会总结setTimeout和setInterval基本点,对于上面三点会分三篇博客分别来总结,对于知道上面三点的人,但是又不是非常了解全面知识点的码农来说,没有关系的,我们可以慢慢来学习,来理解,或

JavaScript异步编程(2)- 先驱者:jsDeferred

原文出处: linkFly   欢迎分享原创到伯乐头条 JavaScript当前有众多实现异步编程的方式,最为耀眼的就是ECMAScript 6规范中的Promise对象,它来自于CommonJS小组的努力:Promise/A+规范. 研究javascript的异步编程,jsDeferred也是有必要探索的:因为Promise/A+规范的制定基本上是奠定在jsDeferred上,它是javascript异步编程中里程碑式的作品.jsDeferred自身的实现也是非常有意思的. 本文将探讨项目js

Javascript 异步加载详解(转)

本文总结一下浏览器在 javascript 的加载方式. 关键词:异步加载(async loading),延迟加载(lazy loading),延迟执行(lazy execution),async 属性, defer 属性 一.同步加载与异步加载的形式 1. 同步加载 我们平时最常使用的就是这种同步加载形式: <script src="http://yourdomain.com/script.js"></script> 同步模式,又称阻塞模式,会阻止浏览器的后续

js异步模型

前言:虽然关于js异步模型的文章已经是相当多,前端们对其也都是有各自的理解.本文的目的是梳理一下我对js异步模型的理解,以及希望给其他前端在这方面提供一个参考.本文只讲js异步模型,下一篇文章再去扯promise/a标准,jquery的promise和deferred对象.本文主要说的是浏览器端的js异步.下面罗列一下关于这方面的资料:JavaScript异步编程:设计快速响应的网络应用(这本书是为数不多专门讲js异步的书),别人写的关于js异步的文章(js异步编程1,js异步编程2),浏览器内

JavaScript异步交互

JavaScript 异步编程简介 回调函数和异步执行 所谓的异步指的是函数的调用并不直接返回执行的结果,而往往是通过回调函数异步的执行. 我们先看看回调函数是什么: var fn = function(callback) { // do something here ... callback.apply(this, para); }: var mycallback = function(parameter) { // do someting in customer callback }: //