helloworlds

not a noun, it's a verb

【SQL】SQLのループは?

前回の続きで第五章の主要点をまとめていきたいと思います。 (具体的なテーブル表など割愛させてもらうことが多いです) ※今回の五章はけっこう割愛しています。

o21o21.hatenablog.jp

今回は、SQLにおいてどうしてループがないのか? といったところを考察してみます。

いきなりですが、答えです。

SQLは意識的にループを言語設計から排除した」が答えです。

RDBを考案したCodd氏いわく

関係操作では、関係全体をまとめて操作の対象とする。 目的は繰り返し(ループ)をなくすことである。 いやしくも末端利用者の生産性を考えようというのであればこの条件欠くことはできないし、 応用プログラマの生産性向上に有益であることも明らかである。

関係操作というのは、SQLのことだそうです。 そんなこんなで、現在わたしたちプログラマビジネスロジックをホスト側で実装しているということです。

SQLでのループの特徴としては、

  • 一定のオーバーヘッドが必要になる
  • 並列処理による最適化が受けられない
  • ストレージのぶんさん効率が悪い

ではループのメリットは?

  • 実行計画に変動リスクがさほどない
  • 処理時間の見積もり精度が高い ( 処理時間 = 1回あたりの実行時間 × 実行回数 )
  • トランザクション制御が容易

では、SQLでのループはどう表現するのでしょう? それは、CACE式とウィンドウ関数によってループを代用できます。

まとめ

そもそもSQLはループがないということ。 前章で確認したようにここでもパフォーマンスのこと優先的に考えればいいのではないかと思いました。 この章ではけっこう割愛させて頂いた例などもパフォーマンスを考慮することで判断できるものだと思います。

  • 前回の記事

o21o21.hatenablog.jp

【SQL】集約とカット

前回の続きで第四章の主要点をまとめていきたいと思います。 (具体的なテーブル表など割愛させてもらうことが多いです)

o21o21.hatenablog.jp

今回は「集合指向」についておってみます。

SQLの考え方として、行単位でなく、行の集合単位でまとめて記述するというものがあります。

集約

集約関数は以下の5つが挙げられます。

  • COUNT
  • SUM
  • AVG
  • MAX
  • MIN

これらは主に複数行を1つ行にまとめる機能を持っています。

どういう場合に活用できるかというと、以下に例をあげてみます。

非集約テーブルという、ある対象になるデータに対して情報が複数行に分散されている場合があります。

この場合、その対象のデータをSELECTをすると当然ながら複数行出力されます。 ただ今回は、1件(1行)のみ出力させたいとします。

安易にUNIONで複数行のクエリをマージすることはアンチパターンになってしまいます。

この場合は、MAX関数を使用することをおすすめします。 SELECTで、MAX (CASE WHEN = 'A' THEN ELSE NULL END) AS こんな形で使用します。

さてこのSQLを実行すると実行計画として得られるのは、'ハッシュ'というアルゴリズムが使用されていることに気付きます。

この時(ソートでも同様だが)、メモリを多く使用する演算であるため、十分のハッシュ用のワーキングメモリが必要です。 スワップが発生するとストレージ上のファイルを使用するので遅延が想定されます。(= TEMP落ち )

カット

いきなりSQLを見てみましょう。

SELECT CASE WHEN age < 20 THEN '子供'
                         WHEN age BETWEEN 20 AND 69 THEN '成人'
                         WHEN age >= 70 THEN '老人'
                         ELSE NULL END AS age_class,
               COUNT(*)
FROM Persons
GROUP BY WHEN age < 20 THEN '子供'
                    WHEN age BETWEEN 20 AND 69 THEN '成人'
                    WHEN age >= 70 THEN '老人'
                    ELSE NULL END;

なんとなくどういうものを抽象したのかわかるでしょう。

このSQLの実行計画もハッシュが使われます。 カットの基準となるキーをGROUP BYとSELECT句の両方に記述するのがポイントです。

これはお互いに重複する要素を持たない部分集合のことでパーティションといいます。

PARTITION BY

PARTITION BY句にもGROUP BYと同様に単純な列名でなく、CASE式や計算式などを用いて複雑な記述ができます。

先程のSQLにPARTITION BYをこのようにいれてみると、 年齢階層内で年齢のランクを設けて出力ができます。

RANK() OVER ( PARTITION BY CASE WHEN age < 20 THEN '子供'
                                                              WHEN age BETWEEN 20 AND 69 THEN '成人'
                                                              WHEN age >= 70 THEN '老人'
                                                              ELSE NULL END
                         ORDER BY age) AS age_rank_in_class

条件として扱えば、入力に情報えお付け加えるだけでなく、オリジナルのテーブルとして情報を保存できています。 もとのPersonsテーブルの行が全て出力されました。

まとめ

PARTITION BY句は集合のカットをしていること。 GROUP BY句やウィンドウ関数は、内部的にハッシュやソートを行っていることを覚えておきましょう。 GROUP BY、CASE式、ウィンドウ関数をうまく利用できるようになれたらいいですね。

  • 前回の記事

o21o21.hatenablog.jp

Luaをサクッと

Luaという拡張プログラミング言語を知ったので、とりあえずどんな書き方するのかなと思い まずサクッとLuaを動く環境を作成。 ※本当にただ動かすだけですww

Luaとは

オブジェクト指向プログラミング、関数型プログラミング、データ駆動型プログラミングもサポートしている。 拡張言語であるため、Luaは「メイン」プログラムを持たない。 ホストクライアント (エンベッディングプログラム、あるいは単にホスト とも呼ぶ) に 組み込まれて 動くだけである。

ふむふむ。軽量で高速でC言語のホストプログラムに組み込まれる目的なわけか〜。 文法的で面白いのは、関数を変数として扱えるのか〜、ふむふむ。

まあとりあえずで、

環境: macOS

Luaをインストール&ヴァージョン確認

$ brew install lua
$ lua -v
Lua 5.3.4  Copyright (C) 1994-2017 Lua.org, PUC-Rio

LuaRocksをインストール

LuaRocksは、パッケージマネージャの1つ。 インストールした後、ホームディレクトリにモジュール等がインストールされるディレクトリを作成。

$ brew install luarocks
$ mkdir ~/.luarocks

ライブラリをインストール

CJSON: JSONを扱うライブラリである LuaFileSystem: ファイルシステムを扱う

$ luarocks install --local lua-cjson
$ luarocks install --local luafilesystem

test.luaを作成

local json = require 'cjson'
local lfs = require 'lfs'
local doc_path = "./"
local result
local file
local text
local val
for filename in lfs.dir(doc_path) do
    result =  string.find(filename,"^.*json$")
    if result ~= nill then
        file = io.open(filename,"r");
        io.input(file);
        text = io.read()
        val = json.decode(text)
        io.close(file)
    end
end

print("from JSON File...." ..val.sample)

helloworld.jsonを作成

{ "sample":"Hello World!!!!"}

実行

$ lua ./test.lua

構成はこんなかんじになった(当たり前だけどw)

├── bin
│   ├── json2lua
│   └── lua2json
├── helloworld.json
├── lib
│   ├── lua
│   │   └── 5.3
│   └── luarocks
│       └── rocks-5.3
├── share
│   └── lua
│       └── 5.3
└── test.lua

出力

from JSON File....Hello World!!!!

参考: http://milkpot.sakura.ne.jp/lua/lua51_manual_ja.html#1 https://dev.classmethod.jp/server-side/language/installation_of_lua/

【SQL】SQLにおける条件分岐とは

前回の続きで第三章の主要点をまとめていきたいと思います。 (具体的なテーブル表など割愛させてもらうことが多いです)

o21o21.hatenablog.jp

今回は、「条件分岐について」です。

少しSQLが書けるようになるとパフォーマンスの考慮が気になってしまいます。 なにか条件があるとUNIONをすぐ使用して、「ほんとにUNIONでいいのかな?」など疑問が湧いてくることがあるかと思います。

思考が組み立てやすいので多用することは仕方のないことですが、パフォーマンスに欠点を抱えてしまうことはありがちです。

こうしたとき、実行計画を気にかけてみるようにしました。 実際には内部的に複数回のSELECT文を実行してしまっている!とか気づきがあるかもしれません。

例えばこのようなSQLがあったとします。 (ネーミングセンスがありません...w)

SELECT name, year
  FROM People
WHERE year <= 1990
UNION ALL
SELECT name, year
  FROM People
WHERE year >= 1991;

このSQLだとPeopleテーブルに対して2度のアクセスを実行しています。 フルスキャン実行され読み取りコストが大幅に上がります。 キャッシュに保持されることも想定されますが、大抵テーブルサイズが大きくなるほどキャッシュヒット率は悪くなります。

そしたらどうしたらいいのでしょうか?

SELECT句で条件分岐をする

SELECT name, year,
               CASE WHEN year <= 1990 THEN 'オールド'
                         WHEN year >= 1991 THEN 'ヤング'
FROM People;

このSQLは先程のSQLと同じ結果が出力されます。 が、こちらのほうがパフォーマンスは数段良いです。

UNIONによる分岐は、SELECTの単位で分岐させています。 CASEによる分岐は、の単位で分岐させています。(こちらがベター)

SQLを書く時、「IFを使えるのではないか?」と自答してみましょう。 そして使えるのであればCASE式を使えると覚えておきましょう。

それでもUNIONを使用するケースとは

シンプルに言うと、「マージされるSELECT文同士で使用するテーブルが異なる場合」です。

このようなSQLです。

SELECT co_1
  FROM Table_A
WHERE col_2 = 'a'
UNION ALL
SELECT co_3
  FROM Table_B
WHERE col_4 = 'b';

また、場合によっては「複数のインデックススキャン対1回のフルスキャン」が考えられます。

これに関しては、テーブルのサイズが大きくUNIONを使用してWHERE条件の選択率が十分に小さい場合はUNIONが良いでしょう。

まとめ

もともとUNIONは条件分岐を行うために導入されたものではないこと。 プログラミング言語を扱う際は、「文」に思考がまわります。SQLの場合は、「式」であることを頭に入れておきましょう。 できるだけストレージへのI/Oを減らせるかが重要です。

  • 前回の記事

o21o21.hatenablog.jp

  • 第一章

o21o21.hatenablog.jp

【SQL】SQLの基礎

前回の続きで第二章の主要点をまとめていきたいと思います。 (具体的なテーブル表など割愛させてもらうことが多いです)

o21o21.hatenablog.jp

今回は、「SQLの基礎」です。 IT系の業務をしていることがあれば1度は絶対見聞きしたことのあるものばかりでしょう。

ただ、なんとなく作業の中でないがしろ(仕組みを考えず作業したりノリでSQLを書いていたり...ww)にしていた部分が復習していると新たな気付きがありました。

SELECT文

SELECTといえば、「検索」です。 こんなSQLは実に簡単でよくみますね。

SELECT name, address
  FROM Address
WHERE address = '東京';
  • WHERE

WHEREという単語はSQLにおいて、「どこ?」ではなく、「〜という場所」という関係副詞の用法です。

  • AND/OR

ANDORをつければ複数条件で検索ができます。 ただ、ORを複数回使用するのであれば、 INをつかって、IN('value', 'value', 'value')のようにした方が効率的です。

また、NULLは、IS NULL, IS NOT NULLですね。

  • GROUP BY

GROUP BYは、カットと集約という機能から成り立っているということを覚えておきましょう。 GROUP BYを使用するときは、大きなホールケーキをイメージしてみるとわかりやすいです。 どんな目的でカットしよう?とか考えると何のカラムでGROUP BYすればいいかイメージがつきやすいかもしれません。

  • HAVING

HAVING句は、レコードの集合に対して条件指定を行うものだと考えます。

SELECT address, COUNT(*)
  FROM Address
GROUP BY address
HAVING COUNT(*) = 1;

これでレコード数が1人だけの住所列を選択しています。

  • ORDER BY

だいたいSELECT文を流して出力された結果の順序は特に決まったルールがあるわkではありません。 DBMS内での決まったルールみたいなものはあるかもしれませんが、これはとてもローカルなお話です。

そこで明示的にレコードの順序を保証するならば、ORDER BYを使えばいいわけです。 ORDER BY DESC みたいに。

ちなみに、昇順(ASC)/降順(DESC)を指定しましょう(デフォルトはASCなので昇順で並べたいなら指定する必要はありません)。

また、このORDER BYを使用したとき、同じ値(例: 年齢を検索して22歳が2人以上いた場合など)があったとき、 この同じ値内での順番は他に条件指定がなければランダムです。

  • VIEW

VIEW とは、SELECT文をDBに保存する機能です。 以下がVIEWの作成の仕方。

CREATE VIEW <view name> (row1, row2)
AS
SELECT address, COUNT(*)
  FROM Address
GROUP BY address;

このVIEWの使用方法としては、以下。

SELECT row1, row2
  FROM <view name>;
  • サブクエリ

理解するより見たほうが早いです。

SELECT name
  FROM Address
WHERE name IN (SELECT name FROM Address1);

よくWHERE句条件作成においてサブクエリを使用するケースが多いでしょうか。 もちろんFROM句に直接サブクエリを使うこともできます。 サブクエリを使用した場合、内部でサブクエリから優先的に実行されることを覚えておきましょう。

IN句などで定数を直接使用していた場合は、メンテが必要になるかもしれませんが、サブクエリで書いておけばメンテも最小限になるかもしれません。

条件分岐、集合演算、ウィンドウ関数、更新

  • CASE式

SQLはコードの中に手続きを一切記述しないため、必然的に条件分岐も「式」という単位で行われます。

SELECT name,
               address,
               CASE WHEN address = '東京' THEN '関東'
               CASE WHEN address = '福島' THEN '東北'
               CASE WHEN address = '三重' THEN '中部'
               CASE WHEN address = '大阪' THEN '関西'
               ELESE NULL END AS district
FROM Address;

CASE式はSELECT、FROM、WHERE、GROUP BY句などどこでも使用できます。

  • UNIONで和集合

WHERE句で例えるなら、ORが和集合でANDが積集合です。

Address1(9行レコード)テーブルとAddress2テーブル(6レコード)があるとして、

SELECT * 
  FROM Address1
UNION
SELECT * 
  FROM Address2;

この結果は13行です。本来であれば15行あるのかと思ってしまいますが、 2行少ない理由は、両方のテーブルに存在重複しているレコードがあるからです。

UNIONでは、重複するレコードは削除する動作をします。 もし削除したくない場合は、UNION ALLを使用しましょう。

  • INTERSECTで積集合
SELECT * 
  FROM Address1
INTERSECT
SELECT * 
  FROM Address2;

INTERSECTは、積集合です。「交差する」と覚えましょう。 両テーブルに共通するレコードしか出力されません。

  • EXCEPT

EXCEPTは、差集合です。こちらは、「除外する」と覚えましょう。

SELECT * 
  FROM Address1
EXCEPT
SELECT * 
  FROM Address2;

このSQLは、Address1 - Address2ということになります。 Address1からはAddress2にある共通しているレコードが削除されて出力されます。 (※削除と書いていますが、実際テーブルから削除されるわけでは勿論ありません。)

EXCEPTは、テーブルの書く順序で結果が変わります。 (例: Address2 - Address1)

  • ウィンドウ関数

先程、GROUP BYでは、「集約とカット機能を持っている」と説明しました。 この集約機能を省いたものが、PARTITION BYです。

なので、結果が異なります。 こちらがGROUP BY。

SELECT address, COUNT(*)
  FROM Address
GROUP BY address;


address     |   count     
-----------+---------
東京都          |        3
千葉県          |        2
福島県          |        2
三重県          |        1
和歌山県       |        1

こちらがPARTITION BY。

SELECT address,
               COUNT(*) OVER(PARTITION BY address)
FROM Address;


address     |   count     
-----------+---------
三重県          |        1
千葉県          |        2
千葉県          |        2
東京都          |        3
東京都          |        3
東京都          |        3
福島県          |        2
福島県          |        2
和歌山県       |        1

また、ウィンドウ関数として使用できるのはCOUNTやSUMの他に、 RANKやDENSE_RANK(抜け番なし)ROW_NUMBERなどが存在します。

例: RANK() OVER (ORDER BY age DESC) AS rnk

SQLで更新機能

  • INSERT

レコードを1行ずつ登録していく。

INSERT INTO <table name> (column1, column2, ....;) VALUES (value1, value2, .........);

valueには、文字列型ならシングルコーテーションを、NULLならNULLと指定します。

複数行追加することも可能です。VALUESのあとに続けて値を書いて行きましょう。

  • DELETE

テーブルのレコード全てを削除。

DELETE FROM <tabale name>;

全てのレコードを削除しないなら、WHERE句で条件を指定しましょう。

  • UPDATE

登録済みのレコードに対して、データを更新します。 こちらもWHERE句で条件を付け加えることが可能です。

UPDATE <table name>
  SET <column> = 式;

まとめ

以上、簡単な内容でしたが根本となる句や式が確認できました。 基礎となるSQLを知らなければ応用も活用も難しくなってしまいます。非手続き型で直観的に記述できるのがSQLの良いところです。 検索する目的や欲しい結果を求める際、素早く思考できるよになれるとより次回以降の章も理解度が増すのかと思いました。

【Go】Golang プロジェクト構成を考える

golangを使って色々と試したいなと思い調べていると、 どうもプロジェクト構成についてよく考えたほうが良さそうだった。

その時のメモとこれから随時、理解をしていきながら編集&更新していけたらと思う。 なので、ご指摘(できれば優しいアドバイスooo〜)ありましたらよろしくお願い致します。

まずは、

GOPATH

GOPATHは任意のディレクトリを指定してもおk。 そして、PATHを通す。

export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$PATH

こちらは公式brewでインストールした場合などに、 ファイルに書き込むことになるだろう。 ($ echo $GOPATHで、パスを確認。)

ただ、gvmという、バージョン管理ツールをインストールすると、 上記の設定はgvmがやってくれるらしいので、そちらでも良さそう。 (.bashrcに自動読み込み。即反映するには、$ source /home/ec2-user/.gvm/scripts/gvm実行。)

gvm

  • gvmをインストール

xcodeも必要になります。

$ brew install mercurial
$ bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
  • gvm確認
$ gvm version
Go Version Manager v1.0.22 installed at /<Path>/<To>/.gvm
  • gvm installできるgolangのヴァージョンを確認
$ gvm listall
  • gvm install
$ gvm install go1.9.2

-Bオプションでバイナリからインストール。

  • gvmでインストールした任意のヴァージョンを使う/切り替え
$ gvm use go1.9.2 --default

--defaulオプションで、terminalで再度ログインした後も指定したヴァージョンで使用できるようになる。

  • goコマンドでヴァージョン確認
$ go version
go version go1.9.2 linux/amd64
  • gvmでインストールしたヴァージョン一覧を表示
$ gvm list
gvm gos (installed)

   go1.8.4
   go1.9.2
   system
  • gvmをアンインストール
$ gvm implode

dep

依存関係管理(パッケージ管理)ツールです。 他のツールとしては、glideなどがありますが、goの公式開発チームがサポートしているツールみたいなのでdepを使用してみた。

depを使用するメリットとして、他のプログラマーと開発する際、外部パッケージのヴァージョン管理が容易になります。 また、depを使用することで、Gopkg.lockGopkg.tomlvendor/ができあがります。 こちらの出来あがるファイルやディレクトリについては、後ほど。

  • depをインストール
$ brew install dep
$ dep version
dep:
 version     : devel
 build date  :
 git hash    :
.
.
.

一旦ここまで。 次は、プロジェクト構成を確認してから、depコマンドを使ってみます。

プロジェクト構成

GOPATH配下にsrc、その配下にわかりやすいようにgithub.com/<ユーザー名>/としてディレクトリを構成してみた。 下の構成でいうとgotest, gotest2がプロジェクトになります。 ソースコードこちらの記事のものをサンプルプロジェクトとして使用させて頂きました。

$GOPATH
└── src
    └─ github.com
       │
       └── <UserName>
           ├── gotest
           └── gotest2

以下、gotestプロジェクトの構成

gotest/
|── Gopkg.lock
├── Gopkg.toml
├── Makefile
├── README.md
├── app
│   ├── app.yaml
│   └── main.go
├── gae
│   └── hello.go
└── vendor
    └── github.com
        ├── gorilla
        └── ryutah

ここで先ほどのdepで更新されるファイルがあるかと思います(Gopkg.lock、Gopkg.toml、vendor/)。 これらを作成していくので、プロジェクトディレクトリに移動して、以下を実行していく。 (glideからdepへの移行は、こちらの記事が非常に参考になりました!)

  • dep initを実行 Gopkg.lock、Gopkg.toml(編集可能)、vendor/を生成。
$ dep init

-vオプションで、ログを出力しながらinit。

  • depでパッケージを追加&追記

例えば、main.goで定義してあるパッケージでインストールされてなければインストールされる。

$ dep ensure
$ dep ensure -add <PACKAGES>
  • depでパッケージをアップデート
$ dep ensure -update <PACKAGES>
  • depパッケージの状態を確認
$ dep status

ローカルサーバーを立ち上げる

goapp serve -port <port num> app.yaml

エラーがでなければ完了です。

おわり

gvmとdepでシンプルにプロジェクトを管理できるのかなと思います。 また、ghq+pecoでさらに可動性アップできそうです。

トラブルシューティング

GOPATHがなんか変わってた〜


gvmを利用するとgoの環境変数が勝手に書き換わります。 なので、設定ファイルを編集する必要があります。

設定ファイル自体は、$HOME/.gvm/environments配下にありました。

  • 環境設定ファイルを作成
$ gvm pkgset create <name>

任意の名前を設定できる。私はわかりやすいように、go<version>@<project name>みたいにした。

  • 編集
$ gvm pkgenv <name>
  • 編集箇所

12行目と16行目。変更箇所のサンプルは以下。 プロジェクト名を追記していくかんじ。

# 12行目
export GOPATH; GOPATH="/Users/<mac user name>/.gvm/pkgsets/go1.9.2/gotest:$HOME/go:$GOPATH"

# 16行目
export PATH; PATH="/Users/<mac user name>/.gvm/pkgsets/go1.9.2/gotest/bin:${GVM_OVERLAY_PREFIX}/bin:$HOME/go/bin:${PATH}"
  • プロジェクト毎にpkgsetファイルを切替
$ gvm pkgset use <name>
  • pkgsetの一覧
$ gvm pkgset list

任意のプロジェクトディレクトリにて以下を実行。

$ gvm linkthis

【SQL】DBMS 内部アーキテクチャ

DBについて復習しようと思いました。なので、 「SQL 実践入門 (技術評論社)」という本を参考にメモがてら書きます。

SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)

SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)

今回は1章の内容です。

RDB(Relational Database)製品には、OracleMicrosoft SQL ServerDB2、PostreSQL、MySQLなどが存在する。 MySQLがよく日本では業務をする上で目にしたり、聞いたりするだろう。 内部のアーキテクチャはどれも同じというわけではない。しかし、RDBという枠組みでは基本は一緒、主な概要を抑えておけばよいだろう。

クエリ評価エンジン

ユーザから受け取ったSQLを解釈し、いかなる手順で記憶装置のデータへアクセスするべきか決定する。(実行プラン) この計画に基づいたデータへのアクセス方法がアクセスメソッド。

バッファマネージャ

メモリ領域の使い方を管理する。 ディスク容量マネージャと連携しながら動く。

ディスク容量マネージャ

どこに、どのようなデータを保存するかを管理する。 それに対する、読み込みと書き込みを制御する。

トランザクションマネージャとロックマネージャ

DBMS内部では、トランザクションという単位で個々の処理が管理される。 このトランザクション同士をデータの整合性を保ちながら実行させたり、データにロックをかけて待機させたりという機能です。

リカバリマネージャ

いざという時にデータの復旧(リカバリ)すること。

記憶装置

  • HDD

データを保存するストレージ。 ほとんどの場合は、容量、コスト、パフォーマンスなどという点からHDDあ選択されているでしょう。

  • メモリ

DBMSは常にディスク以外にもデータを持つようにしている。それがメモリです。 記憶コストが高く、1台のハードに搭載できる量は多くはない。 頻繁にアクセスされるデータをうまくメモリ上に保持しておくことができると、 SQL文を実行した際、パフォーマンスの向上が見込める。 パフォーマンス向上とする目的でメモリのことをバッファキャッシュと呼ぶ。

メモリ上の2つのバッファ

  • データキャッシュ

ディスクにあるデータの一部を保持するためのメモリ領域。

  • ログバッファ

更新処理(INSERT,DELETE,UPDATE,MERGE)の実行に関係する。 更新処理は、すぐにストレージ上のデータに変更をかけているわではなく、 更新情報をログバッファに一時溜めている。

トレードオフ

データキャッシュに比べてログバッファの設定値が非常に小さい。 これはDBが基本的に検索をメインとして想定しているからだ。 最近のDBMSは自動でメモリの割当をしているが、よりタスクが大規模化したり、細密化していけば どのような思想に基いてリソース配分が行われているか、行わなければならないのかは知っておく必要がありそうです。

ワーキングメモリ

ソートやハッシュなどの特定処理に利用される作業用の領域。 (ワークバッファやソートバッファなどと呼ばれる)

データへのアクセス方法はどのように決まるのか

さきほども出てきたクエリ評価エンジンでアクセスを決める。 流れとしては以下。(上から下へ)

SQL
|
parser
|
optimizer--> catalog manager
|
plan evaluation
  • パーサー

構文解析。 受け取ったSQLを要素毎に分解しDBMSが処理しやすいように変換する。

  • オプティマイザ パーサーからきたクエリをもとにデータアクセス方法を決定する。 インデックス有無やデータの分散や偏りの度合い、内部のパラメータなどの条件を考慮し計画を作成、 コストを計算して、最も低コストは1つに絞り込む。

  • 各テーブルのレコード数 2.各テーブルの列数と列のサイズ

  • 列値のカーディナリティ(値の個数)
  • 列値のヒストグラム
  • 列内にあるnullの数
  • インデックス情報

上記の情報をもとに実行計画を作成するわけだが、このカタログ情報が実際のテーブルやインデックスと一致しない場合。その場合は統計情報を更新する必要とある。(例:ANALYZE) 運用しているとANALYZEをするタイミングも重要ですが...w

  • カタログマネージャ

DBMS内の内部情報えお集めたテーブル群で、テーブルやインデックスの統計情報が格納される。(統計情報)

  • プラン評価

オプティマイザから受け取った実行計画で、最適な実行計画を選択する。

おわり

と、いった具合でDMNSのアーキテクチャをざくっとかいてみましたの巻。