都说用ets 写一个cache 太简单, 那就简单的搞一个吧, 具体代码就不贴了, 就说说简要的需求和怎么做(说设计有点虚的慌).

需求场景

>> 查询系统,对于主存储而言,一次写入多次查询

所以,cache 需要能实现:

UserA 在查询 RecordA 时, UserB 也需要查询RecordA, 就让UserB waiting, 待UserA 查询完成之后, 共享RecordA 的查询结果.

>> 限制单个ets 表的内存使用量,先进先出

那就需要个queue,求 queue length 的频率较大,考虑下RabbitMQ 的 lqueue

>> 限制单个Record 的内存使用量, 如果小于limit,就保留Record,反之,不保留

>> 辅助性的一些feature (reset memory limit, clean all cache, get cache informations, delete single cache ...)

Query 状态

既然UserA 在查询RecordA 时,若UserB 也需要查询,就让UserB等待.就需要保存查询的状态, cache 的结构:

{QueryTerms, QueryStatus, WaitingUser, QueryResult}

QueryTerms 即查询条件

QueryStatus 是查询状态, 正在处理查询为handling, 查询已经处理完毕为handled

WaitingUser 是等在查询的user, 若QueryStatus 为 handling, 就将 'erlang:self()' append 到WaitingUser, 若QueryStatus 为handled, QueryResult 即为需要的查询结果

QueryResult 查询结果

FIFO queue

cache 不能无休无止的消耗内存, 需要加一个memory total limit, 当超过limit 后, cache 就FIFO .

这样的话, gen_server 进程除了维持ets table 外, 还需要维护queue , 然后refresh queue len 和 memory .

refresh memory 的简单代码:

 handle_info({refresh_mem}, #state{queue_mem = UNQueueMem,
queue = Queue,
etstable = EtsTable} = State) ->
QueueMem = UNQueueMem * 1024 * 1024 / 8,
case catch ets:info(EtsTable, memory) of
Mem when erlang:is_integer(Mem) ->
if
Mem > QueueMem ->
case lqueue:is_empty(Queue) of
true ->
{noreply, State, ?HIBERNATE_TIMEOUT};
_ ->
{{value, OldQueryTerms}, NewQueue} = lqueue:out(Queue),
delete_old_ets(EtsTable, OldQueryTerms),
erlang:send(erlang:self(), {refresh_mem}),
{noreply, State#state{queue = NewQueue}, ?HIBERNATE_TIMEOUT}
end;
true ->
{noreply, State, ?HIBERNATE_TIMEOUT}
end
;
_ ->
{noreply, State, ?HIBERNATE_TIMEOUT}
end;

L1 处的 queue_mem 为 total memory limit

若超过 total memory limit 且queue 不为空, 就 queue out 并在ets table 中将Record 删除.

single cache limit

既然要作单条Record 内存使用量的限制, 就需要知道single Record 的内存占用量, 最简单的办法是:

ets:info(T, memory) ---> ets:insert(T, R) ---> ets:info(T, memory)

然后计算前后memory 的差值.

在"单进程写入/删除, 多进程读"的模式下,此方式不会出现什么问题.

多进程读写

"单进程(gen_server 进程)写入/删除,多进程读" 的方式应该是比较合理的模式,但是这种方式的弊端也显而易见:效率低,在重负载的单进程的压力增加,进程message queue 堆积,进而出现问题.(即便是能做好隔离,同样会对系统产生影响)

那多进程读写的方式呢?

多进程读写,然后将refresh memory的工作交给gen_server 进程. 这种方式,对于大多数功能,是没有问题的(得益于ets 的特性),但是对single cache limit feature 的实现,就会出现很大的影响.single cache limit 需要对ets 做三次操作:

ets:info(T, memory) ---> ets:insert(T, R) ---> ets:info(T, memory)

多进程读写的话,就很难避免在这三次操作中,穿插 delete/insert 操作, 就很难保证正确性.

这个时候, 就需要safe_fixtable 操作.在网上关于safe_fixtable 的资料比较少, 在此收集一些:

1, 坚强blog (http://www.cnblogs.com/me-sa/archive/2011/08/11/erlang0007.html)

在遍历过程中,可以使用safe_fixtable来保证遍历过程中不出现错误,所有数据项只被访问一遍.用到逐一遍历的场景就很少,使用safe_fixtable的情景就更少。不过这个机制是非常有用的,
还记得在.net中版本中很麻烦的一件事情就是遍历在线玩家用户列表.由于玩家登录退出的变化,这里的异常几乎是不可避免的.select match内部实现的时候都会使用safe_fixtable

2,  google group 的讨论(https://groups.google.com/forum/#!topic/erlang-china/OnwM5uPVjmI)


其他功能

其他的feature 就没什么好说的了, 堆码而已.

Erlang ets -- something about cache的更多相关文章

  1. Erlang ets -- something about cache continue

    上一次说到了实现一个简单cache 的基本思路和想法, http://www.cnblogs.com/--00/p/erlang_ets_something_about_cache.html 在文末, ...

  2. Erlang ETS Table

    不需要显示用锁,插入和查询时间不仅快而且控制为常量,这就是Erlang的ETS Table. 为什么而设计? Erlang中可以用List表达集合数据,但是如果数据量特别大的话在List中访问元素就会 ...

  3. erlang ets表

    一.表遍历 通过ets:first/1获取表的第一个关键字,表中下一个关键字用ets:next/2得到,直到ets:next/2返回'$end_of_table' 当多几个进程并发访问ets表时,可以 ...

  4. Erlang库 -- 有意思的库汇总

    抄自这里 首先,库存在的目的大致可分为:1.提供便利2.尽可能解决一些痛点 首先,我们先明确一下Erlang编程语言的一些痛点(伪痛点):1,单进程问题Erlang虚拟机属于抢占式调度,抢占式调度有很 ...

  5. 一次erlang 节点CPU严重波动排查

    新服务上线后观察到,CPU在10 ~ 70%间波动严重,但从每秒业务计数器看业务处理速度很平均. 接下来是排查步骤: 1. dstat -tam 大概每10s一个周期,网络流量开始变得很小,随后突然增 ...

  6. [Erlang 0126] 我们读过的Erlang论文

    我在Erlang Resources 豆瓣小站上发起了一个征集活动 [链接] ,"[征集] 我们读过的Erlang论文",希望大家来参加.发起这样一个活动的目的是因为Erlang相 ...

  7. ubuntu安装erlang

    照着园子里一篇博文安装erlang,各种错调不出来.最后发现官网有解决方案: https://www.erlang-solutions.com/downloads/download-erlang-ot ...

  8. Erlang/OTP 中文手册

    http://erldoc.com/ Open Telecom Platform application array asn1rt base64 binary calendar code dbg di ...

  9. Erlang--etc结构解析

    Erlang中可以用List表达集合数据,但是如果数据量特别大的话在List中访问元素就会变慢了;这种主要是由于List的绝大部分操作都是基于遍历完成的. Erlang的设计目标是软实时(参考:htt ...

随机推荐

  1. SCM-MANAGER-禁用用户

    用管理远用户登录到scm-manager的管理界面http://*.*.*.*:8081/ 设置目标用户为禁用 验证 非 “active” 状态 目标用户客户端不能pull 一直提示登录

  2. mac下的一些mysql操作

    #一.从终端进入mysql 不同于windows下的mysql.mac下的mysql安装路径不同,所以操作上会略有不同: 以下操作以默认安装mysql为前提. ##一(1):打开终端后,先设置路径,后 ...

  3. python命名空间的本质

    Python的命名空间是Python程序猿必须了解的内容,对Python命名空间的学习,将使我们在本质上掌握一些Python中的琐碎的规则. 接下来我将分四部分揭示Python命名空间的本质:一.命名 ...

  4. json 解析double类型多出几位小数,利用NSDecimalNumber修复 , 服务器返回的数据,用数据类型接收(int,NSNumber)?

     后台返回float类型数据8.2,实际用AFN的json解析后得到8.1999999999,该怎么处理呢? 开发中处理处理价格金额问题, 后台经常返回float.double类型, 打印或转成NSS ...

  5. 201621123006 《Java程序设计》第4周学习总结

    1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 多态.重载.继承.覆盖.super.抽象类 1.2 尝试使用思维导图将这些关键词组织起来.注:思维导图一般不需要出现过多的字. ...

  6. Ubuntu12.04 中文输入法设置

    1.ibus输入法 Ubuntu系统安装后已经自带了ibus输入法,在英语环境下默认不启动. 配置ibus自动启动可 以在ubuntu系统菜单上选择System(系统)--- Preferences( ...

  7. 20165202 2017-2018-2 《Java程序设计》第1周学习总结

    20165202 2017-2018-2 <Java程序设计>第1周学习总结 教材学习内容总结 Ubuntu环境下安装JDK 简单Java程序编写 反编译器javap.exe Git安装及 ...

  8. 为什么叫金拱门- golden arch

    不要再纠结为什么叫这么难理解的名字了.因为从golden arch直译过来的撒.金色的拱门.就叫金拱门咯. 关于M的商标的历史来源如下: "McDonald's logo" red ...

  9. WEB-INF目录下的文件访问权限

    对于Tomcat服务器而言,WEB-INF是个特殊的目录.这个目录并不属于Web应用程序可以访问的上下文路径的一部分, 对于客户端来说,这个目录是不可见的,不能通过在浏览器中直接输入地址的方式来访问. ...

  10. wireshark的一些快捷键

    恢复原来的大小   ‘crtl’+‘=’  即按住crtl建和=键(backspace左边那个键) 缩小 ‘crtl’+‘-’ 即按住crtl建和-键(backspace左边第二个键) 放大 ‘crt ...