作者:450651324_43c723 | 来源:互联网 | 2023-07-14 14:10
IhaveatableinmnesiaandIneedtoupdateindividualfieldsintherecordsinit.AccordingtoE
I have a table in mnesia and I need to update individual fields in the records in it. According to Erlang : Mnesia : Updating a single field value in a row if I do something like:
我在mnesia中有一个表,我需要更新其中的记录中的各个字段。根据Erlang:Mnesia:如果我执行以下操作,则连续更新单个字段值:
update_a(Tab, Key, Value) ->
fun() ->
[P] = mnesia:wread({Tab, Key}),
mnesia:write(Tab, P#rec{a=Value}, write)
end.
Now as I understand, the above code reads a record P
based on a Key
, acquiring a write lock on the record, so that no other transactions modify this record while it is being read and written back (or in short, updated). So far so good.
根据我的理解,上面的代码根据Key读取记录P,获取记录上的写锁定,以便在读取和写回(或简称,更新)时,没有其他事务修改此记录。到现在为止还挺好。
Now my requirement is that I need to able to read records based on both the Key
and one other field in the table and then perform an update on it. A function that will do this looking up is mnesia:match_object
. The problem now is that, the function only supports a read lock, not a write lock, according to http://www.erlang.org/doc/man/mnesia.html#match_object-3.
现在我的要求是我需要能够根据表中的Key和另一个字段读取记录,然后对其执行更新。将要查找的函数是mnesia:match_object。现在的问题是,根据http://www.erlang.org/doc/man/mnesia.html#match_object-3,该函数仅支持读锁,而不支持写锁。
The consequence of this is that, suppose in the above function I were to use mnesia:match_object, I will get a (group of) record(s), all with read locks. After I read the records, I need to perform some checks on the retrieved data and then write back the updated record only if a condition is satisfied. Now, assume there are two parallel transactions T1 and T2 initiated by two different sources running. Both T1 and T2 access the same record, at the same time. Since they are read locked, both T1 and T2 will be able to read the records parallely. Both T1 and T2 will perform the same check on the same record, and if the condition matches, both will proceed to execute the update. But, in my code, if T1 and T2 were to have executed serially, T1 would have made changes to the record and in T2, it would have read these changed records and the condition would have failed and no update would have been made.
这样做的结果是,假设在上面的函数中我使用的是mnesia:match_object,我将获得一组(一组)记录,所有记录都带有读锁。在我读取记录之后,我需要对检索到的数据执行一些检查,然后仅在条件满足时写回更新的记录。现在,假设有两个并行事务T1和T2由两个不同的源运行启动。 T1和T2都同时访问相同的记录。由于它们被读锁定,因此T1和T2都能够并行读取记录。 T1和T2都将对同一记录执行相同的检查,如果条件匹配,则两者都将继续执行更新。但是,在我的代码中,如果T1和T2已经连续执行,T1将对记录进行更改,而在T2中,它将读取这些已更改的记录,并且条件将失败并且不会进行更新。
In short, I need to write lock records that are returned by mnesia:match_object. The documentation clearly states only read lock is supported. Are there are any alternatives?
简而言之,我需要编写由mnesia:match_object返回的锁记录。文档明确指出只支持读锁定。有没有其他选择?
UPDATE: I've been experimenting a little bit, and a possible solution I thought could be to use compound keys. Suppose I have data written to a table like:
更新:我已经尝试了一点,我认为可能的解决方案是使用复合键。假设我将数据写入表中,如:
mnesia:transaction(fun() -> mnesia:write(mytable, #rec{i={1,2}, a=2, b=3}, write) end).
Is there any way to lookup entries, using don't cares?
有没有办法查找条目,使用不关心?
I tried these, but both returned empty results:
我试过这些,但都返回空结果:
mnesia:transaction(fun()-> mnesia:read(mytable, {1,'_'}, read) end).
mnesia:transaction(fun()-> mnesia:read(mytable, {1,_}, read) end).
4 个解决方案
1
You don't have to worry about it. From the mnesia documentation:
你不必担心它。来自mnesia文档:
Read locks may be shared, which means that if one transaction manages to acquire a read lock on an item, other transactions may also acquire a read lock on the same item. However, if someone has a read lock no one can acquire a write lock at the same item. If some one has a write lock no one can acquire a read lock nor a write lock at the same item.
读锁可以是共享的,这意味着如果一个事务设法获取对项的读锁,则其他事务也可以获取对同一项的读锁。但是,如果有人有读锁定,则没有人可以在同一个项目上获得写锁定。如果某个人有写锁定,则没有人可以在同一个项目中获取读锁定或写锁定。
If a transaction has a read lock on an object, that object can't be edited by another transaction.
如果事务对对象具有读锁定,则该对象不能由另一个事务编辑。
Say you have two transactions, T1 and T2, which are executing in parallel:
假设您有两个并行执行的事务T1和T2:
- T1 does
mnesia:match_object
, and acquires a read lock on all the returned objects.
- T1执行mnesia:match_object,并获取所有返回对象的读锁定。
- T2 does an equivalent
mnesia:match_object
, and acquires a read lock on the same objects.
- T2执行等效的mnesia:match_object,并获取相同对象的读锁定。
- T2 attempts to acquire a write lock on of the objects (to edit it).
- T2尝试获取对象的写锁定(以编辑它)。
- Mnesia automatically aborts T2, to be retried later.
- Mnesia自动中止T2,稍后重试。
- T1 acquires a write lock on the objects, and edits them.
- T1获取对象的写锁定,并对其进行编辑。
- T1 finishes.
- T1完成。
- Mnesia retries T2.
- Mnesia重试T2。
Note that T2 may be retried several times, depending on how long T1 takes to complete (ie. release its locks).
请注意,T2可能会重试几次,具体取决于T1完成所需的时间(即释放其锁定)。
According to my tests, the locking behavior of mnesia:match_object
isn't consistent. For example, mnesia:match_object(mytable, {mytable, 2, '_'}, LockType)
will lock only the record with Key 2, but mnesia:match_object(mytable, {mytable, '_', test}, LockType)
locks the entire table.
根据我的测试,mnesia:match_object的锁定行为不一致。例如,mnesia:match_object(mytable,{mytable,2,'_'},LockType)将仅锁定具有键2的记录,但是mnesia:match_object(mytable,{mytable,'_',test},LockType)锁定整个桌子。
Also note that the documentation isn't correct, mnesia:match_object(Table, Pattern, write)
does work, and seems to follow the same pattern as 'read', ie. if you specify the key, only the matching record will be write-locked; if you don't specify the key, the entire table will be write-locked.
另请注意,文档不正确,mnesia:match_object(Table,Pattern,write)确实有效,并且似乎遵循与'read'相同的模式,即。如果指定密钥,则只有匹配的记录会被写入锁定;如果您未指定密钥,则整个表将被写入锁定。
You can test it yourself by doing something like this:
您可以通过执行以下操作来自行测试:
test() ->
mnesia:transaction(fun()->
Table = mytable,
Matches = mnesia:match_object(Table, {Table, 2, '_'}, write),
io:format("matched: ~p~n", [Matches]),
spawn(fun()->mnesia:transaction(fun()->
io:format("trying to read~n",[]),
io:format("read: ~p~n", [mnesia:read(Table, 2, read)])
end) end),
timer:sleep(1000),
RereadMatches = lists:map(fun(#mytable{id=Id}) -> mnesia:read(Table, Id, write) end, Matches),
io:format("reread matches: ~p~n", [RereadMatches])
end).
By changing the pattern and lock type passed to match_object
, and the key number and lock type passed to mnesia:read
in the spawned process (or by using mnesia:write
), you can test the various locking behaviors.
通过更改传递给match_object的模式和锁定类型,以及传递给mnesia的键号和锁类型:在生成的进程中读取(或使用mnesia:write),可以测试各种锁定行为。
Addendum: See this post by Ulf Wiger on the same topic.
附录:见Ulf Wiger关于同一主题的这篇文章。
Addendum 2: See the section on "Isolation" in the Mnesia user guide.
附录2:请参阅Mnesia用户指南中的“隔离”部分。
Edit: The above was all done on a set-type table, match_object
locking behavior might be different on a bag-type table.
编辑:以上内容都是在set-type表上完成的,match -object锁定行为可能在bag-type表上有所不同。