行锁

MySQL选择语句支持锁定匹配的行,对于读和写(选择……更新选择……共享模式锁定).这也发生在Collection.find ()而且Table.select ()方法,该方法通过将操作绑定到特定类型的锁,允许对集合/表进行安全和事务性的文档/行更新。

有两种类型的锁。〇共享锁lockShared ()-允许并行事务一致地从给定的集合文档或表行中读取,通过等待具有写操作的未提交事务。并行写操作将失败,除非没有活动的事务。〇专用锁lockExclusive ()-另一方面,允许并行事务无缝且一致地读写给定集合文档或表行。

考虑集合testSchema.testCollection载有下列文件:

[{“_id”:“1”,“一个”:1},{" _id ":“2”,“一个”:1},{" _id ":“3”,“一个”:1}]

以下场景适用于使用默认模式的行锁时(在使用表时也应该如此)。

使用排他锁在两个会话中写入数据

当两个事务使用排他锁时,来自两个事务的写入和更新最终都会被确认,即使它们是并行运行(直至完成)的。

Const mysqlx = require('@mysql/xdevapi');const collectionA = session . getschema ('testSchema').getCollection('testCollection');const collectionB = sessionB.getSchema('testSchema').getCollection('testCollection');const transactionona = sessionA.startTransaction() .then() =>{返回collectionA. const transactionona = sessionA.startTransaction() .then() => {找到(_id =: id) .bind (' id ', ' 1 ') .lockExclusive () . execute ();}) .then(result => {return collectionA.})。修改(_id =: id) .bind (' id ', ' 1 ')这里(a, result.fetchOne()。A + 1) .execute();}) .then(() =>{返回session .commit();});const transactionB = sessionB.startTransaction() .then() =>{返回collectionB. const transactionB = sessionB.startTransaction()找到(_id =: id) .bind (' id ', ' 1 ') .lockExclusive () . execute ();}) .then(result => {return collectionB.})。modify('_id = :id') .bind('id', '1') .set('a', result.fetchOne().a + 1) .set('b', 'foo') .execute(); }) .then(() => { return sessionB.commit(); }); Promise.all([transactionA, transactionB]) .then(() => { return collectionA.find('_id = :id') .bind('id', '1') .execute(); }) .then(result => { console.log(result.fetchAll()); // [{ _id: '1', a: 3, b: 'foo' }] });

在两个使用共享锁的会话中写入数据,没有活动事务

当两个事务被绑定到同一个共享锁时,只有当它们以系列(一个接一个)的方式运行(直到完成)时,它们的写入和更新才会最终被确认。

Const mysqlx = require('@mysql/xdevapi');const collectionA = session . getschema ('testSchema').getCollection('testCollection');const collectionB = sessionB.getSchema('testSchema').getCollection('testCollection');sessionA.startTransaction() .then() =>{返回集合。find('_id =:id') .bind('id', '1') . lockshared () .execute() .then(result => {return collectionA. find('_id =:id') .bind('id', '1') . lockshared () .execute()修改(_id =: id) .bind (' id ', ' 1 ')这里(a, result.fetchOne()。A + 1) .execute();}) .then(() =>{返回session .commit();});}) .then(() =>{返回sessionB.startTransaction() .then(() =>{返回collectionB.}找到(_id =: id) .bind (' id ', ' 1 ') .lockShared () . execute ();}) .then(result => {return collectionB.})。修改(_id =: id) .bind (' id ', ' 1 ')这里(a, result.fetchOne()。A + 1) .set('b', 'foo') .execute(); }) .then(() => { return sessionB.commit(); }); }) .then(() => { return collectionA.find('_id = :id') .bind('id', '1') .execute(); }) .then(result => { console.log(result.fetchAll()); // [{ _id: '1', a: 3, b: 'foo' }] });

使用共享锁和活动事务在两个会话中读取数据

当两个事务被绑定到同一个共享锁时,如果其中一个事务没有提交,则从另一个事务中读取的数据将被阻塞,直到第一个事务提交为止。

Const mysqlx = require('@mysql/xdevapi');const pTimeout = require('p-timeout');const collectionA = session . getschema ('testSchema').getCollection('testCollection');const collectionB = sessionB.getSchema('testSchema').getCollection('testCollection');Const docs = [];sessionA.startTransaction() .then() =>{返回集合。找到(_id =: id) .bind (' id ', ' 1 ') .lockShared () . execute () (() = > {collectionA返回。修改(_id =: id) .bind (' id ', ' 1 ')这里(' a ', 2)这里(“b”,“foo”). execute ();});}) .then(() =>{返回sessionB.startTransaction() .then() =>{//读将阻塞,直到活动事务被提交。返回pTimeout (collectionB。查找('_id =:id')。绑定(' id ', ' 1 ')。lockShared ().execute(doc => docs.push(doc)), 2000); }); }) .catch(err => { if (err.name !== 'TimeoutError') { throw err; } return sessionA.commit(); }) .then(() => { return sessionB.commit(); }) .then(() => { console.log(docs); // [{ _id: '1', a: 2, b: 'foo' }] });

在两个会话中读取数据,不需要任何类型的锁

当两个事务没有绑定到任何类型的锁时,如果两个事务都写/更新给定的文档,即使在提交之后,它也将包含不一致的数据。

Const mysqlx = require('@mysql/xdevapi');const collectionA = session . getschema ('testSchema').getCollection('testCollection');const collectionB = sessionB.getSchema('testSchema').getCollection('testCollection');Const docs = [];sessionA.startTransaction() .then() =>{返回集合。修改(_id =: id) .bind (' id ', ' 1 ')这里(' a ', 2)这里(“b”,“foo”). execute ();}) .then(() =>{返回sessionB.startTransaction() .then(() =>{返回collectionB.}找到(_id =: id) .bind (' id ', ' 1 ') . execute (doc = > docs.push (doc));})}) .then(() =>{返回session .commit();}) .then(() =>{返回sessionB.commit();}) .then(() => {console.log(docs); // [{ _id: '1', a: 2, b: 'foo' }] });

使用共享锁和活动事务在两个会话中写入数据

当两个事务绑定到同一个共享锁时,如果其中一个事务未提交,则从另一个事务写操作将失败,并出现死锁错误。

Const mysqlx = require('@mysql/xdevapi');const collectionA = session . getschema ('testSchema').getCollection('testCollection');const collectionB = sessionB.getSchema('testSchema').getCollection('testCollection');sessionA.startTransaction() .then() =>{返回集合。找到(_id =: id) .bind (' id ', ' 1 ') .lockShared () . execute ();}) .then(() =>{返回sessionB.startTransaction() .then(() =>{返回collectionB.}找到(_id =: id) .bind (' id ', ' 1 ') .lockShared () . execute ();});}) .then(() =>{返回承诺。所有([collectionA。修改('_id =:id')。绑定(' id ', ' 1 ')。set('a', 2).execute(),修改('_id =:id')。绑定(' id ', ' 1 ')。Set ('a', 3).set('b', 'foo').execute()]); }) .catch(err => { console.log(err.message); // 'Deadlock found when trying to get lock; try restarting transaction' });

NOWAIT而且跳过锁

方法可以重写行锁的默认行为NOWAIT而且跳过锁选项.这些选项可通过mysqlx。LockContention财产。

NOWAIT当没有任何正在进行的事务时,其工作方式类似于默认模式,而当有正在进行的事务时,读取将失败。

Const mysqlx = require('@mysql/xdevapi');const collectionA = session . getschema ('testSchema').getCollection('testCollection');const collectionB = sessionB.getSchema('testSchema').getCollection('testCollection');sessionA.startTransaction() .then() =>{返回集合。找到(_id =: id) .bind (' id ', ' 1 ') .lockShared () . execute () (() = > {collectionA返回。修改(_id =: id) .bind (' id ', ' 1 ')这里(' a ', 2)这里(“b”,“foo”). execute ();});}) .then(() =>{返回sessionB.startTransaction() .then(() =>{返回collectionB.}find('_id =:id') .bind('id', '1') .lockShared(mysqlx.LockContention.NOWAIT) .execute();});}) .catch(err => {console.log(err.message); // 'Statement aborted because lock(s) could not be acquired immediately and NOWAIT is set.' });

跳过锁将允许读取,冒着处理不一致数据的风险。

Const mysqlx = require('@mysql/xdevapi');const collectionA = session . getschema ('testSchema').getCollection('testCollection');const collectionB = sessionB.getSchema('testSchema').getCollection('testCollection');sessionA.startTransaction() .then() =>{返回集合。找到(_id =: id) .bind (' id ', ' 1 ') .lockShared () . execute () (() = > {collectionA返回。修改(_id =: id) .bind (' id ', ' 1 ')这里(' a ', 2)这里(“b”,“foo”). execute ();});}) .then(() =>{返回sessionB.startTransaction() .then(() =>{返回collectionB.}find('_id =:id') .bind('id', '1') .lockShared(mysqlx.LockContention.NOWAIT) .execute();});}) .then(result => {console.log(result. fetchall ()); // [] });