- 浏览: 982401 次
- 性别:
- 来自: 广州
最新评论
-
qingchuwudi:
有用,非常感谢!
erlang进程的优先级 -
zfjdiamond:
你好 这条命令 在那里输入??
你们有yum 我有LuaRocks -
simsunny22:
这个是在linux下运行的吧,在window下怎么运行escr ...
escript的高级特性 -
mozhenghua:
http://www.erlang.org/doc/apps/ ...
mnesia 分布协调的几个细节 -
fxltsbl:
A new record of 108000 HTTP req ...
Haproxy 1.4-dev2: barrier of 100k HTTP req/s crossed
原文地址: http://blog.socklabs.com/2008/02/06/dynamically_sizing_a_fragmente.html
Not too long ago Mark Zweifel introduced me to Erlang. There are plenty of websites that explain what it is, how it works and where it came from so I'm not going to go into those details right here.
What I've really been having fun with is mnesia, the native data storage application. With it you can do some really powerful things like advanced data distribution, federation, complex queriers, full/compartmental replication and the list goes on and on.
The documentation is pretty good and Joe Armstrong's book Programming Erlang: Software for a Concurrent World has a huge collection of example snippets putting many of the concepts in action. What isn't covered includes a lot of the advanced mnesia subjects. These include fragmented tables, multi-node datastores and events.
So over the past few days I've been plowing through a lot of this and figuring out how it works. The first on the was federated/fragmented tables.
Consider a grid of three erlang nodes connected to each other with a common cookie. For the sake of this example each node lives in its own directory on the same server, but this example could apply to nodes on more than one machine. In this example the hostname is set to 'icedcoffee'.
ic:~/tmp/erl1 $ erl -sname node1 -setcookie 622c84b69ee448a07d80de5cbeb13e3d
ic:~/tmp/erl2 $ erl -sname node2 -setcookie 622c84b69ee448a07d80de5cbeb13e3d
ic:~/tmp/erl3 $ erl -sname node3 -setcookie 622c84b69ee448a07d80de5cbeb13e3dTo create our setup we want to define a record and create a fragmented table over nodes 1, 2 and 3. We start by starting the initial connection to node2 and node3 from node1 in the erlang shell through net:ping/1. Then store the list of nodes in a variable and pass it to mnesia:create_schema/1. Then, on each node we start the mnesia application through mnesia:start/0.
erl1 erl> net:ping('node2@icedcoffee').
pong
erl1 erl> net:ping('node3@icedcoffee').
pong
erl1 erl> StorageNodes = [node() | nodes()].
[node1@icedcoffee, node2@icedcoffee, node3@icedcoffee]
erl1 erl> mnesia:create_schema(StorageNodes).
ok
erl1 erl> mnesia:start().
ok
erl2 erl> mnesia:start().
ok
erl3 erl> mnesia:start().
ok
Once mnesia is running on our grid we can verify that everything is running as expected using mnesia:system_info/1 and mnesia:info/0.
erl1 erl> mnesia:system_info(running_db_nodes).
[node1@icedcoffee, node2@icedcoffee, node3@icedcoffee]
erl1 erl> mnesia:info().
...
[{'node1@icedcoffee',disc_copies},
{'node2@icedcoffee',disc_copies},
{'node3@icedcoffee',disc_copies}] = [schema]
...
At this point the initial fragmented mnesia table needs to be created and verified across these three nodes. We start by using rd/1 to define a record and then mnesia:create_table/2 to create the table. The table in this demonstration is a simple key/value pair dictionary. When we create our table will will create one fragment for each node (3 fragments) and set the table as disc-only.
erl1 erl> rd(dictionary, {key, value}).
dictionary
erl1 erl> FragProps = [{node_pool, StorageNodes}, {n_fragments, 3}, {n_disc_only_copies, 1}].
[{node_pool,[node1@icedcoffee, node2@icedcoffee, node3@icedcoffee]}, {n_fragments,3}, {n_disc_only_copies,1}]
erl1 erl> mnesia:create_table(dictionary, [{frag_properties, FragProps}, {attributes, record_info(fields, dictionary)}]).
{atomic,ok}
We can verify that the table was created using mnesia:table_info/2. Then verify that each node has one fragment of the table. That is done with mnesia:info/0 and mnesia:table_info/2.
erl1 erl> mnesia:table_info(dictionary, frag_properties).
[{base_table,dictionary},
{foreign_key,undefined},
{hash_module,mnesia_frag_hash},
{hash_state,{hash_state,3,2,1,phash2}},
{n_fragments,3},
{node_pool,[node1@icedcoffee, node2@icedcoffee, node3@icedcoffee]}]
erl1 erl> mnesia:info().
...
[{node1@icedcoffee,disc_only_copies}] = [dictionary_frag3]
[{node2@icedcoffee,disc_only_copies}] = [dictionary_frag2]
[{node3@icedcoffee,disc_only_copies}] = [dictionary]
...
erl1 erl> Info = fun(Item) -> mnesia:table_info(dictionary, Item) end.
#Fun<erl_eval.6.35866844>
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{node1@icedcoffee,1},
{node2@icedcoffee,1},
{node3@icedcoffee,1}]
The next step is to populate the table with data and verify the data distribution across the fragments and nodes.
erl1 erl> WriteFun = fun() -> [mnesia:write({dictionary, K, -K}) || K <- lists:seq(1, 30)], ok end.
#Fun<erl_eval.20.117942162>
erl1 erl> mnesia:activity(sync_dirty, WriteFun, mnesia_frag).
ok
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_size], mnesia_frag).
[{dictionary,3},{dictionary_frag2,15},{dictionary_frag3,12}]
At this point we have distributed mnesia table over a set of nodes. We've essentially trippled the number of transactions that we can handle compared to running a non distributed-fragmented mnesia store. But lets consider the situation that comes when we need to grow to another node, either for mnesia load or table size.
The demonstration continues by lighting an additional node.
ic:~/tmp/erl4 $ erl -sname node4 -setcookie 622c84b69ee448a07d80de5cbeb13e3d
What we do now is start mnesia on node4 and then reconfigure node1 to recognize node4.
erl4 erl> mnesia:start().
ok
erl1 erl> net:ping('node4@icedcoffee').
pong
erl1 erl> mnesia:change_config(extra_db_nodes, [node4@icedcoffee]).
{ok,['node4@icedcoffee']}
On node4 when we run mnesia:info/0 we will now see all of the other nodes on our grid.
erl4 erl> mnesia:info().
...
[{'node1@icedcoffee',disc_copies},
{'node2@icedcoffee',disc_copies},
{'node3@icedcoffee',disc_copies},
{'node4@icedcoffee',ram_copies}] = [schema]
...
What you'll notice is that the schema on node4 is still a ram copy. This isn't compatible with the disc-only table setup we have on our other nodes. Before we can continue adding the node to the table schema, we need to transform that using mnesia:change_table_copy_type/3.
erl4 erl> mnesia:change_table_copy_type(schema, node(), disc_copies).
{atomic,ok}
erl4 erl> mnesia:info().
...
[{'node1@icedcoffee',disc_copies},
{'node2@icedcoffee',disc_copies},
{'node3@icedcoffee',disc_copies},
{'node4@icedcoffee',disc_copies}] = [schema]
...
Now that all of the nodes are in order we can add the node to the dictionary table schema and add another fragment to the table. This is done with mnesia:change_table_frag/2 calling the add_node and add_frag forms of this function.
erl1 erl> mnesia:change_table_frag(dictionary, {add_node, 'node4@icedcoffee'}).
{atomic,ok}
erl1 erl> NodeLayout = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{'node4@icedcoffee',0},
{'node1@icedcoffee',1},
{'node2@icedcoffee',1},
{'node3@icedcoffee',1}]
erl1 erl> mnesia:change_table_frag(dictionary, {add_frag, NodeLayout}).
{atomic,ok}
On the initial call to mnesia:change_table_frag/2 the second parameter is the {add_node, 'node4@icedcoffee'} tuple. The second call creates a list that represents the node layout that is then fed into the third call to mnesia:change_table_frag/2 that sends the {add_frag, NodeLayout} tuple telling mnesia to add another table fragment to the table. With that done we can see that our table has been adjusted and rebalanced accordingly.
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{'node1@icedcoffee',1},
{'node2@icedcoffee',1},
{'node3@icedcoffee',1},
{'node4@icedcoffee',1}]
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_size], mnesia_frag).
[{dictionary,3},
{dictionary_frag2,8},
{dictionary_frag3,12},
{dictionary_frag4,7}]
If we wanted to add a fragment to a specific node we use the mnesia:change_table_frag/2 function but instead of passing a list we pass the node that we want to adjust.
erl1 erl> mnesia:change_table_frag(dictionary, {add_frag, ['node4@icedcoffee']}).
{atomic,ok}
erl1 erl> mnesia:change_table_frag(dictionary, {add_frag, ['node4@icedcoffee']}).
{atomic,ok}
erl1 erl> mnesia:change_table_frag(dictionary, {add_frag, ['node4@icedcoffee']}).
{atomic,ok}
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{'node1@icedcoffee',1},
{'node2@icedcoffee',1},
{'node3@icedcoffee',1},
{'node4@icedcoffee',4}]
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_size], mnesia_frag).
[{dictionary,1},
{dictionary_frag2,6},
{dictionary_frag3,6},
{dictionary_frag4,7},
{dictionary_frag5,2},
{dictionary_frag6,2},
{dictionary_frag7,6}]
Not too long ago Mark Zweifel introduced me to Erlang. There are plenty of websites that explain what it is, how it works and where it came from so I'm not going to go into those details right here.
What I've really been having fun with is mnesia, the native data storage application. With it you can do some really powerful things like advanced data distribution, federation, complex queriers, full/compartmental replication and the list goes on and on.
The documentation is pretty good and Joe Armstrong's book Programming Erlang: Software for a Concurrent World has a huge collection of example snippets putting many of the concepts in action. What isn't covered includes a lot of the advanced mnesia subjects. These include fragmented tables, multi-node datastores and events.
So over the past few days I've been plowing through a lot of this and figuring out how it works. The first on the was federated/fragmented tables.
Consider a grid of three erlang nodes connected to each other with a common cookie. For the sake of this example each node lives in its own directory on the same server, but this example could apply to nodes on more than one machine. In this example the hostname is set to 'icedcoffee'.
ic:~/tmp/erl1 $ erl -sname node1 -setcookie 622c84b69ee448a07d80de5cbeb13e3d
ic:~/tmp/erl2 $ erl -sname node2 -setcookie 622c84b69ee448a07d80de5cbeb13e3d
ic:~/tmp/erl3 $ erl -sname node3 -setcookie 622c84b69ee448a07d80de5cbeb13e3dTo create our setup we want to define a record and create a fragmented table over nodes 1, 2 and 3. We start by starting the initial connection to node2 and node3 from node1 in the erlang shell through net:ping/1. Then store the list of nodes in a variable and pass it to mnesia:create_schema/1. Then, on each node we start the mnesia application through mnesia:start/0.
erl1 erl> net:ping('node2@icedcoffee').
pong
erl1 erl> net:ping('node3@icedcoffee').
pong
erl1 erl> StorageNodes = [node() | nodes()].
[node1@icedcoffee, node2@icedcoffee, node3@icedcoffee]
erl1 erl> mnesia:create_schema(StorageNodes).
ok
erl1 erl> mnesia:start().
ok
erl2 erl> mnesia:start().
ok
erl3 erl> mnesia:start().
ok
Once mnesia is running on our grid we can verify that everything is running as expected using mnesia:system_info/1 and mnesia:info/0.
erl1 erl> mnesia:system_info(running_db_nodes).
[node1@icedcoffee, node2@icedcoffee, node3@icedcoffee]
erl1 erl> mnesia:info().
...
[{'node1@icedcoffee',disc_copies},
{'node2@icedcoffee',disc_copies},
{'node3@icedcoffee',disc_copies}] = [schema]
...
At this point the initial fragmented mnesia table needs to be created and verified across these three nodes. We start by using rd/1 to define a record and then mnesia:create_table/2 to create the table. The table in this demonstration is a simple key/value pair dictionary. When we create our table will will create one fragment for each node (3 fragments) and set the table as disc-only.
erl1 erl> rd(dictionary, {key, value}).
dictionary
erl1 erl> FragProps = [{node_pool, StorageNodes}, {n_fragments, 3}, {n_disc_only_copies, 1}].
[{node_pool,[node1@icedcoffee, node2@icedcoffee, node3@icedcoffee]}, {n_fragments,3}, {n_disc_only_copies,1}]
erl1 erl> mnesia:create_table(dictionary, [{frag_properties, FragProps}, {attributes, record_info(fields, dictionary)}]).
{atomic,ok}
We can verify that the table was created using mnesia:table_info/2. Then verify that each node has one fragment of the table. That is done with mnesia:info/0 and mnesia:table_info/2.
erl1 erl> mnesia:table_info(dictionary, frag_properties).
[{base_table,dictionary},
{foreign_key,undefined},
{hash_module,mnesia_frag_hash},
{hash_state,{hash_state,3,2,1,phash2}},
{n_fragments,3},
{node_pool,[node1@icedcoffee, node2@icedcoffee, node3@icedcoffee]}]
erl1 erl> mnesia:info().
...
[{node1@icedcoffee,disc_only_copies}] = [dictionary_frag3]
[{node2@icedcoffee,disc_only_copies}] = [dictionary_frag2]
[{node3@icedcoffee,disc_only_copies}] = [dictionary]
...
erl1 erl> Info = fun(Item) -> mnesia:table_info(dictionary, Item) end.
#Fun<erl_eval.6.35866844>
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{node1@icedcoffee,1},
{node2@icedcoffee,1},
{node3@icedcoffee,1}]
The next step is to populate the table with data and verify the data distribution across the fragments and nodes.
erl1 erl> WriteFun = fun() -> [mnesia:write({dictionary, K, -K}) || K <- lists:seq(1, 30)], ok end.
#Fun<erl_eval.20.117942162>
erl1 erl> mnesia:activity(sync_dirty, WriteFun, mnesia_frag).
ok
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_size], mnesia_frag).
[{dictionary,3},{dictionary_frag2,15},{dictionary_frag3,12}]
At this point we have distributed mnesia table over a set of nodes. We've essentially trippled the number of transactions that we can handle compared to running a non distributed-fragmented mnesia store. But lets consider the situation that comes when we need to grow to another node, either for mnesia load or table size.
The demonstration continues by lighting an additional node.
ic:~/tmp/erl4 $ erl -sname node4 -setcookie 622c84b69ee448a07d80de5cbeb13e3d
What we do now is start mnesia on node4 and then reconfigure node1 to recognize node4.
erl4 erl> mnesia:start().
ok
erl1 erl> net:ping('node4@icedcoffee').
pong
erl1 erl> mnesia:change_config(extra_db_nodes, [node4@icedcoffee]).
{ok,['node4@icedcoffee']}
On node4 when we run mnesia:info/0 we will now see all of the other nodes on our grid.
erl4 erl> mnesia:info().
...
[{'node1@icedcoffee',disc_copies},
{'node2@icedcoffee',disc_copies},
{'node3@icedcoffee',disc_copies},
{'node4@icedcoffee',ram_copies}] = [schema]
...
What you'll notice is that the schema on node4 is still a ram copy. This isn't compatible with the disc-only table setup we have on our other nodes. Before we can continue adding the node to the table schema, we need to transform that using mnesia:change_table_copy_type/3.
erl4 erl> mnesia:change_table_copy_type(schema, node(), disc_copies).
{atomic,ok}
erl4 erl> mnesia:info().
...
[{'node1@icedcoffee',disc_copies},
{'node2@icedcoffee',disc_copies},
{'node3@icedcoffee',disc_copies},
{'node4@icedcoffee',disc_copies}] = [schema]
...
Now that all of the nodes are in order we can add the node to the dictionary table schema and add another fragment to the table. This is done with mnesia:change_table_frag/2 calling the add_node and add_frag forms of this function.
erl1 erl> mnesia:change_table_frag(dictionary, {add_node, 'node4@icedcoffee'}).
{atomic,ok}
erl1 erl> NodeLayout = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{'node4@icedcoffee',0},
{'node1@icedcoffee',1},
{'node2@icedcoffee',1},
{'node3@icedcoffee',1}]
erl1 erl> mnesia:change_table_frag(dictionary, {add_frag, NodeLayout}).
{atomic,ok}
On the initial call to mnesia:change_table_frag/2 the second parameter is the {add_node, 'node4@icedcoffee'} tuple. The second call creates a list that represents the node layout that is then fed into the third call to mnesia:change_table_frag/2 that sends the {add_frag, NodeLayout} tuple telling mnesia to add another table fragment to the table. With that done we can see that our table has been adjusted and rebalanced accordingly.
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{'node1@icedcoffee',1},
{'node2@icedcoffee',1},
{'node3@icedcoffee',1},
{'node4@icedcoffee',1}]
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_size], mnesia_frag).
[{dictionary,3},
{dictionary_frag2,8},
{dictionary_frag3,12},
{dictionary_frag4,7}]
If we wanted to add a fragment to a specific node we use the mnesia:change_table_frag/2 function but instead of passing a list we pass the node that we want to adjust.
erl1 erl> mnesia:change_table_frag(dictionary, {add_frag, ['node4@icedcoffee']}).
{atomic,ok}
erl1 erl> mnesia:change_table_frag(dictionary, {add_frag, ['node4@icedcoffee']}).
{atomic,ok}
erl1 erl> mnesia:change_table_frag(dictionary, {add_frag, ['node4@icedcoffee']}).
{atomic,ok}
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{'node1@icedcoffee',1},
{'node2@icedcoffee',1},
{'node3@icedcoffee',1},
{'node4@icedcoffee',4}]
erl1 erl> mnesia:activity(sync_dirty, Info, [frag_size], mnesia_frag).
[{dictionary,1},
{dictionary_frag2,6},
{dictionary_frag3,6},
{dictionary_frag4,7},
{dictionary_frag5,2},
{dictionary_frag6,2},
{dictionary_frag7,6}]
发表评论
-
OTP R14A今天发布了
2010-06-17 14:36 2677以下是这次发布的亮点,没有太大的性能改进, 主要是修理了很多B ... -
R14A实现了EEP31,添加了binary模块
2010-05-21 15:15 3030Erlang的binary数据结构非常强大,而且偏向底层,在作 ... -
如何查看节点的可用句柄数目和已用句柄数
2010-04-08 03:31 4814很多同学在使用erlang的过程中, 碰到了很奇怪的问题, 后 ... -
获取Erlang系统信息的代码片段
2010-04-06 21:49 3475从lib/megaco/src/tcp/megaco_tcp_ ... -
iolist跟list有什么区别?
2010-04-06 20:30 6529看到erlang-china.org上有个 ... -
erlang:send_after和erlang:start_timer的使用解释
2010-04-06 18:31 8386前段时间arksea 同学提出这个问题, 因为文档里面写的很不 ... -
Latest news from the Erlang/OTP team at Ericsson 2010
2010-04-05 19:23 2013参考Talk http://www.erlang-factor ... -
对try 异常 运行的疑问,为什么出现两种结果
2010-04-05 19:22 2842郎咸武<langxianzhe@163.com> ... -
Erlang ERTS Async基础设施
2010-03-19 00:03 2517其实Erts的Async做的很不错的, 相当的完备, 性能又高 ... -
CloudI 0.0.9 Released, A Cloud as an Interface
2010-03-09 22:32 2476基于Erlang的云平台 看了下代码 质量还是不错的 完成了不 ... -
Memory matters - even in Erlang (再次说明了了解内存如何工作的必要性)
2010-03-09 20:26 3439原文地址:http://www.lshift.net/blog ... -
Some simple examples of using Erlang’s XPath implementation
2010-03-08 23:30 2050原文地址 http://www.lshift.net/blog ... -
lcnt 环境搭建
2010-02-26 16:19 2614抄书:otp_doc_html_R13B04/lib/tool ... -
Erlang强大的代码重构工具 tidier
2010-02-25 16:22 2486Jan 29, 2010 We are very happy ... -
[Feb 24 2010] Erlang/OTP R13B04 has been released
2010-02-25 00:31 1387Erlang/OTP R13B04 has been rele ... -
R13B04 Installation
2010-01-28 10:28 1390R13B04后erlang的源码编译为了考虑移植性,就改变了编 ... -
Running tests
2010-01-19 14:51 1486R13B03以后 OTP的模块加入了大量的测试模块,这些模块都 ... -
R13B04在细化Binary heap
2010-01-14 15:11 1508从github otp的更新日志可以清楚的看到otp R13B ... -
R13B03 binary vheap有助减少binary内存压力
2009-11-29 16:07 1668R13B03 binary vheap有助减少binary内存 ... -
erl_nif 扩展erlang的另外一种方法
2009-11-26 01:02 3218我们知道扩展erl有2种方法, driver和port. 这2 ...
相关推荐
Dynamically sizing grid rows/columns 90 Creating a scrollable user interface 92 Creating a border around panels and elements 94 Placing elements in exact positions 96 Adding/removing elements to ...
关于A Mapping Flow for Dynamically Reconfigurable Multi-Core System-on-Chip论文的介绍文档,介绍了论文中的主要内容及其关系
### 动态自适应网格技术在潜在奇异性解中的高效应用 #### 摘要与背景 本文介绍了一种高效动态自适应网格生成方法,旨在解决时间依赖问题中出现的复杂结构甚至奇异性行为。该方法的核心是通过求解一组非线性椭圆偏...
标题中的"A class to dynamically read data from any ODBC data source"是指一个可以动态地从任何ODBC数据源读取数据的类。ODBC(Open Database Connectivity)是微软开发的一种数据库访问接口,它允许应用程序通过...
DB - Unbounded Pipelining in Dynamically Reconfigurable Paxos Clusters.pdf Consensus is an essential ingredient of a faulttolerant distributed system systems. When equipped with a consensus ...
### 服务化架构在动态可重构工作流中的应用 #### 引言 随着近年来业务流程管理系统(BPMS)被广泛应用于处理日益复杂的信息化系统,如何有效地管理这些系统的生命周期、协调分布式执行环境下的业务流程以及支持动态...
这篇教程主要介绍的是如何在运行时动态替换工具栏,这对于软件开发者来说是一个非常实用的技术,尤其是在需要根据用户需求或特定场景灵活调整界面元素的应用中。动态更换工具栏可以使应用程序更加灵活,提供更好的...
CODBCDynamic是一个面向ODBC(Open Database Connectivity)数据源的类,它允许程序员以动态的方式从各种数据库中读取数据。ODBC是微软提供的一种标准接口,使得应用程序能够与多种数据库管理系统(DBMS)进行交互,...
【船级社】 ABS Design and Installation of Dynamically Installed Piles Dynamically_Installed_Piles_Corrigenda-Mar18.pdf
NULL 博文链接:https://linshiquan.iteye.com/blog/513084
### Dynamically Programmable DRU for V5:关键技术点解析 #### 概述 在现代多服务光学网络中,为了能够支持各种数据传输速率,需要具备能够灵活调整工作频率的收发器。通常情况下,高速串行输入/输出(I/O)技术...
Add responsibilities to objects dynamically Facade A single class that represents an entire subsystem Flyweight A fine-grained instance used for efficient sharing Proxy An object representing ...
"Dynamically Centering and Alignment.zip"这个压缩包文件显然包含了关于如何在HTML5 Canvas上实现这一目标的教程或代码示例。Canvas是JavaScript提供的一种强大的图形绘制API,通过它可以进行像素级别的图形操作。...
Create Access data source name dynamically动态的创建数据源名
【船级社】 ABS Design and Installation of Dynamically Installed Piles_Colors.pdf