by shigemk2

当面は技術的なことしか書かない

4.5 基盤となるコンポーネント #read_aosa

BerkeleyDBの話。

4つのサブシステム

  • バッファマネージャ
  • ロックマネージャ
  • ログマネージャ
  • トランザクションマネージャ

すべて自前のAPIを有する 独立して使うこともできるが、共通のオブジェクトハンドルであるenvironmentハンドルを共有するようになった 共有メモリをサポート

ハンドル

http://d.hatena.ne.jp/maeyan/20091227/1261848549

ハンドルとは、各アプリやその中で使われているオブジェクト等に対し起動時に割り振られる数字のことです。一意な数字であり、アプリごとにハンドルがツリー構造をとって親子関係でつながっています

設計講座6

ライブラリを設計するときは名前空間を利用することが肝要

設計講座7

  1. 複数回登場する機能があるなら共有関数を作る
  2. 汎用目的のルーチンを開発するときはそのルーチン用のテストを書く
  3. 書くのが難しいコードは個別に書いて保守する

4.6 バッファマネージャー Mpool #read_aosa

BerkeleyDBの続き

Berkeley DB Mpoolサブシステムについて

Man page of MPOOL

ファイルページを扱うインメモリのバッファプール

  • ソフトウェアを実行する際、使用するプログラムやデータのすべてをメモリ上に読み込み、ハードディスクなどの外部記憶装置を使わない
  • 高速に実行できる反面大容量のDBの場合は大量のメモリが必要となる

インメモリとは 〔 オンメモリ 〕 【 in-memory 】 - 意味/解説/説明/定義 : IT用語辞典

バッファプール

バッファープールとは,表や索引のデータ・ページをディスクから読み取る際や変更する際に,そのデータをキャッシュに入れておくためのメモリー

用語解説

バッファー・プールの目的は、データベース・システムのパフォーマンスを改善すること

DB2 バッファー・プール

ページ

特集:基礎から理解するデータベースのしくみ - 基礎から理解するデータベースのしくみ(6):ITpro

ページには,テーブルを構成するレコードや,インデックスのエントリを複数格納する

動き

  • 実際はディスク上の永続データのキャッシュであるという前提
  • メモリ内のページを固定し、他のプロセス、スレッドがそのページをバッファプールから削除できないようにする

Mpoolのファイル抽象化

ファイルシステム上で動かすことを前提としているため、ファイルを抽象化したAPIを公開

ダーティ

DBの書き込みはディスクからデータを取り出してメモリの中で計算して修正したデータをディスクに戻す。メモリにデータを書き込まれた状態をダーティと呼ぶ。

読み込み用として固定したページはダーティになったとしてもディスクに書き込める

データの読み込みでダーティ状態が発生するのか?

固定している最中に読み込みとか書き込みとかしてもいいのか?っていう話 書き込み中に書き戻しは当然だめだけど、読み込み中に書き込みはいいんじゃないのか

ログ先行書き込み(WAL)

処理に失敗した場合の復旧 データを更新する前に変更内容のログをディスクに書き込む すべてのデータページにログシーケンス番号(LSN)を書き込み、特定のページへの直近の変更に対応するログレコードを記録する

本体とは別のところにLSNを書き込むスペースがある。DBが壊れたときはそのスペースのLSNから該当するSQLを見つけ出して復旧する

LSNからSQLを呼び出す専用のメソッドが存在し、それを呼び出さないとLSNは使われない

設計

  • ログ先行書き込みはカプセル化とレイヤー化の一例でもある
  • こうした指針は有用で、ソフトウェアの保守やテスト、デバッグ、拡張をしやすくしてくれる

4.7 ロックマネージャー: Lock #read_aosa

Berkeley DBの続き

ファイル→ページ→レコードという階層構造が存在する

  • Mpoolと同様、ロックマネージャーも汎用コンポーネントとして作られた
  • Mpoolの場合と同様、他のアプリケーションからもまったく違う方法でロックマネージャーを使えるという点が重要

ロックマネージャを理解する上で重要な概念

  1. ロッカー(誰がロックを獲得したのか)
  2. ロックオブジェクト(ロックされている項目)
  3. 衝突マトリクス(ちょっと何言ってるかわからないけど後述)

ロッカー

  • ロッカーは32ビット符号なし整数
  • Lockers are 32-bit unsigned integers. Berkeley DB divides this 32-bit name space into transactional and non-transactional lockers (although that distinction is transparent to the lock manager).
  • この32ビットの名前空間(に)トランザクショナルなロッカーと非トランザクショナルなロッカーの二つに分割する
  • オレオレルール
  • 非トランザクショナル 長期間の読み込みロックの確保

ロックオブジェクト

  • 任意な長さの不透明(opaque)なバイト文字列(何を隠しているのかよくわからない)

二つの異なるロッカーが特定のオブジェクトをロックしようとしたときは、どちらも同じバイト文字列でそのオブジェクトを表す。つまり、オブジェクトを不透明なバイト文字列で表すときの規約についてはアプリケーション側で決めておく必要がある

  • ID ページ番号 型の構造体

ほとんどの場合、Berkeley DBで表す必要があるのはロックしたい特定のファイルやページだけ

opaque

インターフェース上で未定義のデータ型をopaque data type(不透明型)と呼び、そのような型を指すポインタをOpaqueポインタと呼びます Opaqueポインタについて - white wheelsのメモ

相互参照の問題を回避するために、Opaque型を利用する。中身がなんだかわからないけど定義されているもの

C言語とかC++とかでアレされているやつ

設計講座9

ページレベルのロックはアプリケーションの同時実行性を制限してしまう。ある制御スレッドがデータベースのレコードを変更すると、他のスレッドからは同じページにあるそれ以外のレコードを変更できなくなる。一方レコードレベルのロックなら、全く同じレコードを変更しようとしない限りはそんな制限を受けない。ページレベルのロックをすれば安定性が向上する

小規模な組み込みを想定している

衝突マトリクス

システム内に存在するさまざまな型のロックとその相互作用について定義 ロックを保持するエンティティを「ホルダー」、ロックを要求するエンティティを「リクエスター」

わかりづらいけど✓はNGを意味する

リクエスター/ホルダー No-Lock Read Write Wait iWrite iRead iRW uRead wasWrite
No-Lock
Read
Write
Wait
iWrite
iRead
iRW
uRead
iwasWrite

階層化ロックのサポート

階層化ロックとは、さまざまな項目を階層を保ったままロックすること

ページをロックしたらファイルもまるごとロックする

あるページをロックするということは、ファイルをロックするということでもあるからだ。あるページが変更されているとき、そのページを含むファイルを同時に変更することはできない

って書いてあるけど、ページをロックしたら自動的にファイルもロックするのか(インテンションロックも含んでいるのか)

iはインテンションの意

あるロッカーがコンテナに対するインテンションロックを獲得すると、そのコンテナの中身に対してもロックする意図があるということを表す iRead、iWrite、そしてiWRはすべてインテンションロックであり、それぞれ読み込み、書き込み、その両方に対する意図を指す

エンティティは要素、モノの意

ロックを結合するときには、ひとつのロックを保持するのは次のロックを獲得するまでの間となる。つまり、内部のBtreeページをロックするのは、次のレベルのページを選んでロックするための情報を読み込むまでの間である

設計講座10

汎用性重視の設計にしておいたことが、並列データ格納機能を追加するときに役立った

#

File←iwrite データ←write

より荒い粒度に対する要求なので許される

4.8 ログマネージャー: Log #read_aosa

Berkeley DB

ログマネージャ

ログマネージャーは、構造化された追記限定のファイルを抽象化したものである

ログの概念は極めてシンプルである。不透明なバイト文字列を受け取り、それをシーケンシャルにファイルへ書き出し、各レコードに一意な識別子を付与する。この識別子はログシーケンス番号(LSN)と呼ばれる

LSNを使ってレコードの取得などをやる。

  • 追記しかしないデータ構造
  • LSNの形式はファイル番号とファイル内オフセットのペア
  • ゆえに場所は簡単にわかる

注意点

  • 障害時でも一貫性の保証
  • パフォーマンス

設計講座11

ソフトウェアの構造を改良するため根本的にフレームワークを変更してしまうことを恐れてはいけない。また、一部だけ変更して後で徐々に整えていこうなんて考えではいけない。やるなら全体的にやってしまって、それから次に進もう。何度となく言われているように、「今それをする時間がないのなら、いつまでたってもそんな時間はひねり出せないだろう」ってことだ。また、フレームワークを変更するときにはテストもきちんと書いておこう

ログレコード書式

ログには必ずレコード単位のメタデータが含まなければならない。そうすれば、LSNさえわかれば返すレコードのサイズもわかるようになる

レコードヘッダがログレコードに含まれる

  • オフセット
  • チェックサム

トランザクションIDを見ればそのログレコードがどのトランザクションに属するのかがわかるので、リカバリー中にそのレコードを無視できるのか処理が必要なのかも判断できる

設計講座12

大量のソフトウェア部品を組み上げたり保守したりする能力はとてもはかないものだということである。大量のソフトウェア部品を組み上げたり保守したりするのは難しくて間違いを犯しやすい。そして、ソフトウェアアーキテクトとして、できる限りのことをしてできるだけ早くできるだけ頻繁にソフトウェアが運ぶ情報を最大化しなければならない

Building and maintaining significant pieces of software is difficult and error-prone, and as the software architect, you must do everything that you can, as early as you can, as often as you can, to maximize the information conveyed in the structure of your software.

積分値→整数値

ログ先行書き込みとなんの関係があるのだろう…

抽象化違反

特別なログレコード チェックポイントレコード

チェックポイントとは、ある特定時点のデータベースの状態をディスク上に書き出す処理のことである

一般に、ログマネージャー自身はレコードタイプを気にしないものだ。

ログマネージャーとトランザクションマネージャーがそれぞれログをまるごと読み込んで位置を調べるのではなく、トランザクションマネージャーがその処理をログマネージャーに委譲するということだ。抽象化による処理の切り分けに違反してでもパフォーマンスを求めたという、ありがちな話だ

有害なレイヤー化違反あるいは抜け目のないパフォーマンス最適化

Berkeley DBのログレコードの大半はデータベースを特定できなければならない。各ログレコードにデータベースファイルのフルパスを含めてもよいが、そんなことをすればログの容量が肥大化するしあまり美しくない

→かわりに一意な整数値を記録

ファイル名とログファイルIDのマッピングを管理

設計講座13

バグを修正するときには、単におかしなところを見つけるだけではいけない。なぜそうなったのか、何か誤解があったのではないかということを探るようにしよう。そうすれば、そのプログラムのアーキテクチャをより深く理解できるようになるし、そもそもの設計自体に根本的な問題があればそれもはっきりするだろう。

びっくりするほどいいこと言ってる

4.9 トランザクションマネージャー: Txn #read_aosa

Berkeley DB

このモジュールは、独立したコンポーネントたちをとりまとめてトランザクショナルなACID特性を提供する

ACID

  • 原子性(Atomicity) 一つのトランザクションで実行するすべての操作をひとまとめにして扱う。データベースにすべて反映されるかひとつも反映されないか、そのいずれかしかない。
  • 整合性(Consistency) トランザクションの実行前後がそれぞれ論理的に整合性のある状態になっている。
  • 独立性(Isolation) トランザクションの観点からはすべてのトランザクションがシーケンシャルに実行される。他のトランザクションと並列で実行されることはない。
  • 永続性(Durability) トランザクションをいったんコミットしたらそれはコミットされたままになるという意味である。一度コミットした内容は、どんな障害があっても消えることはない

トランザクションサブシステムは begin commit abortの3つで構成

  • トランザクションの開始
  • トランザクションの確定
  • トランザクションの中止

チェックポイントの処理

  • チェックポイントLSNより前のログファイルは消してしまってもかまわない
  • リカバリー処理ではチェックポイントLSN移行のレコードだけを処理すればよい
  • チェックポイントLSNと実際のチェックポイントレコードの間には多くのログレコードがある可能性がある(それらもリカバリー対象になりうる)

データベース チェックポイント (SQL Server)

チェックポイントによって、予期しないシャットダウンやクラッシュの後の復旧中に、ログに格納されている変更をSQL Server データベース エンジンが適用するための最適なポイントが作成されます

リカバリー

リカバリーの目標は、ディスク上のデータベースを(おそらく整合性が崩れてしまっている状態から)整合性のある状態に戻す

  1. 直近のチェックポイントのチェックポイントLSNのひとつ前のチェックポイントを探す
  2. そこから順方向に読み進め、ログファイルIDのマッピングとコミット済みトランザクションのリストを構築する
  3. チェックポイントLSNに向かって逆方向に読み進め、コミットしていないトランザクション上の操作をすべてUNDO
  4. 順方向に読み進め、コミット済みトランザクション上の操作をすべてREDO
  5. チェックポイントを作る(やってもやんなくてもいい)

2パス方式

直近のチェックポイントLSNに対して、コミットされていないすべてのトランザクションを取り消し(UNDO)、コミットされていたトランザクションをすべて再現する(REDO) ログファイルのIDと実際のデータベースとのマッピングを再構築

設計講座14

データベースのリカバリーは複雑なトピックであり、実装するのも難しいしデバッグも困難だ。というのも、リカバリー処理はそんなに頻繁に発生するものではないからである アーキテクトとして、そしてプログラマーとしての私たちの目標は、さまざまな道具を自由に使いこなすことだ。設計・問題の分割・レビュー・テスト・命名規約やコーディングスタイルなどの道具を使いこなし、プログラミングに関するさまざまな問題を、自分たちで解決可能な問題に落とし込む

第13章NoSQLを取り巻く世界 13.1 その名の由来は #read_aosa

Not Only SQL(RDBMSじゃないSQL)

データベース側に隠蔽されていた操作をアプリケーションの設計側に押し出した システムアーキテクトの立場で考えると、これらのシステムの仕組みをより深く知っておく必要がある

RDBMSが役に立つ場面(SQLと関係モデル)

SQLを使えば、データのディスク上での配置や使うインデックスそしてデータを処理するアルゴリズムを知らなくても必要な情報をよしなに取り出すことができる

クエリオプティマイザ

論理的に等価であるいろいろな問い合わせプランの中から最も効率的な問い合わせができるものを見つける

クエリ最適化 - Wikipedia

クエリ (データに対する問い合わせ) を実行する最も効率的な方法を決定

RDBMSの問題点

  • 複雑さは予測不可能性につながる。SQLは表現力がありすぎる
  • 問題をモデル化する方法は一つではない
  • データの量が増加して一つのサーバーでは保持しきれなくなると、データベース内のテーブルをパーティションに区切って複数のコンピューターで管理する必要がある

これだけ問題点はあるが、やっぱり枯れきった技術であるので、使うことを検討する必要はあると思う。

NoSQLの始まり

NoSQLが選択肢にあがるのは、何か特有の問題がある場合だ。たとえば大量のデータを扱う必要があったり作業量が膨大になったり、SQLとリレーショナルデータベースではうまく最適化できないようなデータモデリングを採用した場合などである

NoSQLムーブメントの起源をたどれば、その大半は研究コミュニティの論文に行き着く

Google BigTable 複数列からなる履歴データを分類して格納 Amazon Dynamo キー指向の分散型データストア

特徴と検討事項

大掛かりなSQL標準規格と決別して、ストレージの設計に関してシンプルながらも段階的なソリューションを提供 データベースがデータを操作する方法を単純化すればするほど、アーキテクトは問い合わせのパフォーマンスを予測しやすくなる

NoSQLではRDBMSの大前提であるACID特性をゆるくした

NoSQLの検討点

  • データモデルとクエリモデル
  • 永続性
  • スケーラビリティ
  • パーティショニング
  • 整合性
  • トランザクションの特性
  • 単一サーバーでのパフォーマンス
  • 作業量の分析