请选择 进入手机版 | 继续访问电脑版

Erlang笔记 -- Mnesia:Erlang数据库简单使用

[复制链接]
科达工艺 发表于 2021-1-2 19:46:25 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
1. 创建数据库



  • 在当前节点创建
  1. PS E:\ERL\Mnesia> erlEshell V8.3  (abort with ^G)1> node().nonode@nohost2> mnesia:create_schema([node()]).ok3> init:stop().ok4>PS E:\ERL\Mnesia> ls    目次: E:\ERL\MnesiaMode                LastWriteTime         Length Name----                -------------         ------ ----d-----       2020/10/21     11:38                Mnesia.nonode@nohostPS E:\ERL\Mnesia>
复制代码
mnesia:create_schema(NodeList)会在NodeList(它必须是一个包罗有效Erlang节点的列表)里的所有节点上都初始化一个新的Mnesia数据库。
Mnesia完成初始化并创建了一个名为Mnesia.nonode@nohost的目次布局来生存数据库。


  • 在特定节点创建
  1. PS E:\ERL\Mnesia> erl -sname dylanEshell V8.3  (abort with ^G)(dylan@DESKTOP-FRA3DA2)1> mnesia:create_schema([node()]).ok(dylan@DESKTOP-FRA3DA2)2> init:stop().ok(dylan@DESKTOP-FRA3DA2)3>PS E:\ERL\Mnesia> ls    目次: E:\ERL\MnesiaMode                LastWriteTime         Length Name----                -------------         ------ ----d-----       2020/10/21     11:46                Mnesia.dylan@DESKTOP-FRA3DA2d-----       2020/10/21     11:38                Mnesia.nonode@nohost
复制代码
别的,可以在启动erl时通过参数-mnesia dir Dir指定命据库目次的位置。


  • Mnesia常用方法
    mnesia:start(). 启动mnesia数据库
    mnesia:stop(). 关闭mnesia数据库
    mnesia:info(). 显示mnesia数据库状态
2. 创建表

可以对Mnesia表举行多种方式的设置。首先,表可以位于内存或磁盘里。其次,表可以位于单台呆板上,也可以在多台呆板之间复制。


  • 内存表
    它们的速度非常快,但是内里的数据是易失的,所以如果呆板瓦解大概停止了DBMS,数据就会丢失。
  • 磁盘表
    磁盘表应该不会受到系统瓦解的影响(前提是磁盘没有物理损坏)。当Mnesia事务写入一个表而且这个表是生存在磁盘上时,实际上是事务数据首先被写入了一个磁盘日志。
要创建一个表,则调用mnesia:create_table(Name, ArgS),此中ArgS是一个由{Key,Val}元组构成的列表。如果表创建乐成,create_table就会返回{atomic, ok},否则返回{aborted, Reason}。常用参数:


  • Name
    表的名称(一个原子)。按惯例是一个Erlang纪录的名称,表里的各行是这个纪录的实例。
  • {type, Type}
    指定了表的范例。Type是set、ordered_set或bag中的一个。
  • {disc_copies, NodeList}
    NodeList是一个Erlang节点列表,这些节点将生存表的磁盘副本。使用这个选项时,系统还会在执行这个利用的节点上创建一个表的内存副本。
    可以既在一个节点上生存disc_copies范例的副本表,又在另一个节点上生存该表的差别范例。这种做法能满意以下要求:

  • 读取利用非常快,并在内存里执行;
  • 写入利用在恒久性存储介质里执行。


  • {ram_copies, NodeList}
    NodeList是一个Erlang节点列表,这些节点将生存表的内存副本。
  • {disc_only_copies, NodeList}
    NodeList是一个Erlang节点列表,这些节点将只生存表的磁盘副本。这些表没有内存副本,访问起来会比力慢。
  • {attributes, AtomList}
    这个列表包罗表里各个值的列名。要创建一个包罗Erlang纪录xxx的表,可以用{attributes, record_info(fields, xxx)}这种语法(也可以显式指定一个纪录字段名列表)。
常用的表属性组合:



  • mnesia:create_table(shop, [{attributes, record_info(fields, xxx)}]).
    它会在单个节点上创建一个常驻内存的表。
    如果节点瓦解了,表就会丢失。
    它是所有表里最快的一种。
    内存必须能容纳这个表。
  • mnesia:create_table(shop, [{attributes, record_info(fields, xxx)}, {disc_copies, node()]}).
    它会在单个节点上创建一个常驻内存的表和一个磁盘副本。
    如果节点瓦解了,表就会从磁盘规复。
    表的读访问很快,但写访问较慢。
    内存最好能容纳这个表。
  • mnesia:create_table(shop, [{attributes, record_info(fields, xxx)}, {disc_only_copies, node()]}).
    它只会在单个节点上创建一个磁盘副本。
    它用于那些因为太大而无法放入内存的表。
    它的访问速度比带有内存副本的方案更慢。
  • mnesia:create_table(shop, [{attributes, record_info(fields, xxx)}, {ram_copies, node(), soneOtherNode()]}).
    它会在两个节点上各创建一个常驻内存的表。
    如果两个节点都瓦解了,表就会丢失。
    内存必须能容纳这个表。
    可以在任何一个节点上访问这个表。
  • mnesia:create_table(shop, [{attributes, record_info(fields, xxx)}, {disc_copies, node(), soneOtherNode()]}).
    它会在多个节点上创建磁盘副本。
    无论哪个节点瓦解,我们都能规复过来。
    纵然所有节点都瓦解了,表也不会丢失。
首先,通过执行test_mnesia:do_only_once()创建一个数据库及数据表:
  1. 1> test_mnesia:do_only_once().stopped=INFO REPORT==== 30-Oct-2020::15:06:32 ===    application: mnesia    exited: stopped    type: temporary2> mnesia:info().===> System info in version "4.14.3", debug level = none  mnesia:start().ok4> mnesia:info().---> Processes holding locks  Processes waiting for locks  Participant transactions  Coordinator transactions  Uncertain transactions  Active tables  System info in version "4.14.3", debug level = none     mnesia:create_schema([node()]),    mnesia:start(),    create_table(),    mnesia:stop().%% 创建数据表create_table() ->    mnesia:create_table(shop,   [{attributes, record_info(fields, shop)}]),    mnesia:create_table(cost,   [{attributes, record_info(fields, cost)}]).
复制代码
先通过test_mnesia:reset_tables()初始化数据表。
  1. 5> test_mnesia:reset_tables().{atomic,ok}
复制代码
3. 查询数据



  • 查询所有行:
  1. 7> test_mnesia:demo(select_shop).[{shop,potato,2456,1.2}, {shop,apple,20,2.3}, {shop,orange,100,3.8}, {shop,pear,200,3.6}, {shop,banana,420,4.5}]
复制代码
test_mnesia:demo(select_shop)代码如下:
  1. demo(select_shop) ->    do(qlc:q([X || X  qlc:e(Q) end,    {atomic, Val} = mnesia:transaction(F),    Val.
复制代码
它在一个Mnesia事务内调用了qlc:e(Q)。Q是一个已编译的QLC查询,而qlc:e(Q)会执行这个查询,并把查询到的所有效果以列表的形式返回。返回值{atomic, Val}的意思是事务乐成并得到了Val值。Val是这个事务函数的值。
需要注意:
qlc:q/1的参数必须是一个字面上的列表推导,不能是通过求值得出的。举个例子,下面的代码与示例里的代码不是等价的。
  1. Val = [X || X  test_mnesia:demo(select_some).[{potato,2456}, {apple,20}, {orange,100}, {pear,200}, {banana,420}]
复制代码
test_mnesia:demo(select_some)代码如下:
  1. demo(select_some) ->    do(qlc:q([{X#shop.item, X#shop.quantity} || X  test_mnesia:demo(reorder).[apple,orange,pear]
复制代码
test_mnesia:demo(reorder)代码如下:
  1. demo(reorder) ->    do(qlc:q([X#shop.item || X  test_mnesia:demo(join).[apple]
复制代码
test_mnesia:demo(join)代码如下:
  1. demo(join) ->    do(qlc:q([X#shop.item || X  test_mnesia:demo(select_shop).[{shop,tomato,100,1.0}, {shop,potato,2456,1.2}, {shop,apple,20,2.3}, {shop,orange,100,3.8}, {shop,pear,200,3.6}, {shop,banana,420,4.5}]13> test_mnesia:add_shop_item(orange,100,1.0).{atomic,ok}14> test_mnesia:demo(select_shop).[{shop,tomato,100,1.0}, {shop,potato,2456,1.2}, {shop,apple,20,2.3}, {shop,orange,100,1.0}, {shop,pear,200,3.6}, {shop,banana,420,4.5}]
复制代码
shop表的主键是表内的第一列,也就是shop纪录里的item字段。这个表属于“异键”范例。如果新创建的纪录和数据表里的某一行具有相同的主键,就会覆盖那一行,否则就会创建一个新行。
5. 删除行

要移除某一行,需要知道该行的对象ID(Object ID,简称OID)。它由表名和主键的值构成。
  1. 15> test_mnesia:remove_shop_item(tomato).{atomic,ok}16> test_mnesia:demo(select_shop).[{shop,potato,2456,1.2}, {shop,apple,20,2.3}, {shop,orange,100,1.0}, {shop,pear,200,3.6}, {shop,banana,420,4.5}]
复制代码
6. Mnesia事务

Mnesia接纳一种悲观锁定(pessimistic locking)的计谋。每当Mnesia事务管理器访问一个表时,都会根据上下文情况实验锁定纪录甚至整个表。如果它发现这大概导致死锁,就会立刻中止事务并打消之前所做的改动。
如果因为其他历程正在访问数据而导致事务一开始就失败了,系统就会举行短时间的等待,然后再次实验执行事务。这么做的一种效果就是事务fun里的代码大概会被执行许多次。
出于这个原因,事务fun里的代码不应该做任何带有副作用的事情。举个例子:
  1. F = fun() ->    ...    io:format("STH Done"),    ...end,mnesia:transaction(F).
复制代码
也许会得到大量输出,因为这个fun大概会被多次重试。
有2点需要注意:

  • 对mnesia:write/1和mnesia:delete/1的调用只应该出现在由mnesia:transaction/1处置惩罚的fun内部。
  • 永远不要编写代码来显式捕捉Mnesia访问函数(mnesia:write/1和mnesia: delete/1等)里的异常错误,因为Mnesia的事务机制自己就依赖这些函数在失败时抛出异常错误。如果捕捉这些异常错误并试图自行处置惩罚它们,就会粉碎事务机制。
别的,可以通过调用mnesia:abort(Reason)终止一个事务。
7. 表检察器

要检察我们生存在Mnesia里的数据,可以使用observer应用步调里内建的表检察器。用下令observer:start()启动observer。
8. 完整代码

  1. %% test_mnesia.erl-module(test_mnesia).-compile(export_all).-include_lib("stdlib/include/qlc.hrl").-record(shop, {item, quantity, cost}).-record(cost, {name, price}).do_only_once() ->        mnesia:create_schema([node()]),    mnesia:start(),        create_table(),        mnesia:stop().%% 创建数据表create_table() ->        mnesia:create_table(shop,   [{attributes, record_info(fields, shop)}]),    mnesia:create_table(cost,   [{attributes, record_info(fields, cost)}]).start() ->    mnesia:start(),    mnesia:wait_for_tables([shop,cost,design], 20000).%% 等待所有数据表停当%% 重置表数据reset_tables() ->    mnesia:clear_table(shop),%% 删除表中的数据    mnesia:clear_table(cost),    F = fun() ->                lists:foreach(fun mnesia:write/1, example_tables())        end,    mnesia:transaction(F).        example_tables() ->    [%% The shop table     {shop, apple,   20,   2.3},     {shop, orange,  100,  3.8},     {shop, pear,    200,  3.6},     {shop, banana,  420,  4.5},     {shop, potato,  2456, 1.2},     %% The cost table     {cost, apple,   1.5},     {cost, orange,  2.4},     {cost, pear,    2.2},     {cost, banana,  1.5},     {cost, potato,  0.6}    ].%% 查询%% SQL equivalent%%  SELECT * FROM shop;demo(select_shop) ->    do(qlc:q([X || X     do(qlc:q([{X#shop.item, X#shop.quantity} || X     do(qlc:q([X#shop.item || X     do(qlc:q([X#shop.item || X  qlc:e(Q) end,    {atomic, Val} = mnesia:transaction(F),    Val.%% 添加数据add_shop_item(Name, Quantity, Cost) ->    Row = #shop{item=Name, quantity=Quantity, cost=Cost},    F = fun() ->                mnesia:write(Row)        end,    mnesia:transaction(F).%% 删除数据remove_shop_item(Item) ->    Oid = {shop, Item},    F = fun() ->                mnesia:delete(Oid)        end,    mnesia:transaction(F).
复制代码
注:本博客为通过《Erlang步调设计 第二版》学习Erlang时所做的条记。学习更详细的内容,发起直接阅读《Erlang步调设计 第二版》。

来源:https://blog.csdn.net/Dylan_2018/article/details/111305019
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


专注素材教程免费分享
全国免费热线电话

18768367769

周一至周日9:00-23:00

反馈建议

27428564@qq.com 在线QQ咨询

扫描二维码关注我们

Powered by Discuz! X3.4© 2001-2013 Comsenz Inc.( 蜀ICP备2021001884号-1 )