0

これは、トランザクションの「ロック」がないことによる問題を示すデモです。setTimeout を使用して非同期/同時実行をシミュレートします。C、Go、Rust などの言語で並行性を扱ったことがないため、実装の詳細でどのように機能するかはよくわかりませんが、MVCCの概念を理解しようとしています。

const db = {
  table1: {
    records: [
      { id: 1, name: 'foo', other: 'hello' },
      { id: 2, name: 'bar', other: 'world' },
    ]
  }
}

function readTable1(id) {
  return db.table1.records.find(x => x.id === id)
}

function writeTable1(id) {
  const record = readTable1(id)
  return new Promise((res, rej) => {
    console.log('transaction 1 start')
    setTimeout(() => {
      record.other = 'qwerty'

      setTimeout(() => {
        record.name = 'asdf'
        console.log('transaction 1 done')
        res()
      }, 1000)
    }, 1000)
  })
}

function wait(ms) {
  return new Promise((res) => setTimeout(res, ms))
}

async function test1() {
  writeTable1(1)
  console.log(readTable1(1))
  await wait(1100)
  console.log(readTable1(1))
  await wait(2200)
  console.log(readTable1(1))
}

test1()

記録する

transaction 1 start
{ id: 1, name: 'foo', other: 'hello' } // read
{ id: 1, name: 'foo', other: 'qwerty' } // read
transaction 1 done
{ id: 1, name: 'asdf', other: 'qwerty' } // read

トランザクションがレコードを処理している途中で、同時に読み取ることができる実レコードを変更します。ロックはありませんが、MVCC はロックなしで (複数のバージョンのレコードを使用して) 実行します。次に、MVCC がどのように機能すると思うかを実装しようとします。私の理解を正していただけることを願っています。これがそれです。

const db = {
  table1: {
    records: [
      [{ id: 1, name: 'foo', other: 'hello' }],
      [{ id: 2, name: 'bar', other: 'world' }],
    ]
  }
}

function readTable1(id) {
  const idx = db.table1.records.findIndex(x => x[0].id === id)
  return [idx, db.table1.records[idx][0]]
}

// this is a long transaction.
function writeTable1(id) {
  const [idx, record] = readTable1(id)
  // create a new version of record for transaction to act on.
  const newRecordVersion = {}
  Object.keys(record).forEach(key => newRecordVersion[key] = record[key])
  db.table1.records[idx].push(newRecordVersion)

  return new Promise((res, rej) => {
    console.log('transaction 2 start')
    setTimeout(() => {
      newRecordVersion.other = 'qwerty'

      setTimeout(() => {
        newRecordVersion.name = 'asdf'
        console.log('transaction 2 done')

        // now "commit" the changes
        commit()

        res();
      }, 1000)
    }, 1000)
  })

  function commit() {
    db.table1.records[idx].shift()
  }
}

function wait(ms) {
  return new Promise((res) => setTimeout(res, ms))
}

async function test1() {
  writeTable1(1)
  console.log(readTable1(1)[1])
  await wait(1100)
  console.log(readTable1(1)[1])
  await wait(2200)
  console.log(readTable1(1)[1])
  console.log(db.table1.records)
}

test1()

それはこれを出力しますが、これは正しいようです。

transaction 2 start
{ id: 1, name: 'foo', other: 'hello' }
{ id: 1, name: 'foo', other: 'hello' }
transaction 2 done
{ id: 1, name: 'asdf', other: 'qwerty' }
[
  [ { id: 1, name: 'asdf', other: 'qwerty' } ],
  [ { id: 2, name: 'bar', other: 'world' } ]
]

これは正しいですか、一般的にどのように機能しますか? 主に、実際の実装ではレコードごとにいくつのバージョンが作成されますか? 一度に 2 つ以上のバージョンを作成できますか? もしそうなら、それは一般的にどのような状況で起こりますか? タイムスタンプはどのように機能しますか? ウィキページのタイムスタンプについて読みましたが、実装方法が実際には登録されていません。また、増加するトランザクション ID。つまり、基本的にこれら 3 つの部分 (バージョン管理、タイムスタンプ、およびトランザクション ID) がどのように組み合わされるかです。

JavaScript でのタイムスタンプとバージョン管理のある種のシミュレーションを探しているので、実装レベルの大まかな概算のようなものでありながら、一般的な概念を高いレベルで理解していることを確認できます。MVCC とは何かを知り、いくつかの論文を読むだけでは、MVCC の実装方法を知るには十分ではありません。

私の例では、トランザクション中に2 つのバージョンのレコードしか存在しません。それ以上必要な場合があるかどうかはわかりません。また、タイムスタンプを挿入する方法がわかりません。

4

1 に答える 1

0

簡単な答え: 「データベース」でのマルチバージョン同時実行制御には、いくつかの異なる要素が含まれます。さまざまなベンダーが、これらの各機能をいくつかの異なる方法で実装しています。

  • MVCC (RDBM と No-SQL DB の両方) を使用するデータベースのリストと、最初に MVCC をサポートしたバージョンは次のとおりです: https://en.wikipedia.org/wiki/List_of_databases_using_MVCC
  • これは、MSSQL (Microsoft のエンタープライズ RDBMS データベース) に対する適切な応答です。

https://dba.stackexchange.com/questions/174791/does-sql-server-use-multiversion-concurrency-control-mvcc

SQL Server は本当にどこでも MVCC を実装していますか?

はい、SQL Server 2005 以降。

SQL Server の用語は、「行のバージョン管理の分離レベル」です。Locking and Row Versioningから始まる製品ドキュメント ツリーを参照してください。特に、行のバージョン管理 (RCSI) を使用した読み取りコミット分離とスナップショット分離 (SI) という 2 つの別個の "MVCC" 実装があることに注意してください。

そして、もしそうなら、それは with (tablock, holdlock) の考えとどのように調和しますか?

そのヒントの組み合わせを使用すると、テーブル全体へのアクセスがシリアル化されます。これは利用可能な同時実行オプションが最も少ないため、これらのヒントを使用することは非常にまれです。特定の用途を RCSI または SI 分離に置き換えることができるかどうかは、特定の状況によって異なります。その点について詳しく説明してほしい場合は、具体的な例を挙げてフォローアップの質問をすることができます。

また、SQL Server の分離レベルに関する一連の記事もお読みください。

これは大きな話題であり、簡潔で単純な「フリーサイズ」という返事はありません。

「JavaScriptでMVCCをシミュレートする」限り:

  • Javascript は本質的にシングルスレッドです。言語自体には「スレッド」または「ロック」の概念はありません。
  • 「約束」(使用しているように)は、非同期コールバックを「注文」する優れた方法です
  • あなたが達成しようとしていることについて、より詳細な情報が必要です。
于 2022-02-13T02:23:20.813 に答える