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 ()); // [] });