忍者ブログ
IT関係の小作人労働の日々の日記です。 最近データベースが好きです。 インフラ構築、DB構築、アプリケーション開発・・・何でも屋です。 何でもできそうで、何にもできない。

【C言語入門】Macターミナルで始めるC言語:Xcodeを使わずにClangで「Hello World」を動かす最短ルート


プログラミングの原点とも言えるC言語。Mac(Apple Silicon)環境には、標準で高性能なコンパイラ「Clang」が備わっています。今回は、重厚なGUIを持つXcodeはあえて使わず、ターミナルだけでコードを書き、ビルドして実行するまでの「一連の儀式」を、実際の動作環境に基づいて解説します。

1. 開発環境の確認:Apple Clang (arm64)

【 手を動かして確認 】 まずは、自分のMacに武器が備わっているか確認します。現在のMac(M1/M2/M3/M4チップ)では、アーキテクチャが「arm64」となっているのが特徴です。ターミナルで以下の結果が返ってくれば、準備は万端です。

% clang --version
Apple clang version 17.0.0 (clang-1700.0.13.5)
Target: arm64-apple-darwin24.6.0
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

※ `Target: arm64` は、Apple Siliconに最適化されたバイナリを生成することを示しています。

2. 作業ディレクトリの作成

【 実践のポイント 】 学習用のコードが散らからないよう、専用のディレクトリ(フォルダ)を用意します。こうした小さな整理整頓が、後のデバッグ効率を左右します。

# デスクトップに学習用フォルダを作成して移動
mkdir -p ~/Desktop/c-study && cd ~/Desktop/c-study

# 現在のディレクトリを確認
pwd

3. ソースコードの記述:hello.c

【 ここがエンジニアの視点 】 テキストエディタを開き、以下のコードを入力して `hello.c` という名前で保存します。C言語の最も基本的な構成要素(標準入出力のインクルードと、main関数)のみを記述します。

#include <stdio.h>

int main(void) {
    printf("Hello, World!\n");
    return 0;
}

4. コンパイルの実行(ビルド)

【 ターミナルの動き 】 書いたばかりのテキスト(ソースコード)を、arm64アーキテクチャが理解できる実行ファイルへ変換します。`-o` オプションで、出力ファイル名を指定するのがMac流のスマートなやり方です。

# hello.c を元に、実行ファイル hello を生成
clang hello.c -o hello

# 実行ファイルができたか確認(lsコマンド)
ls -F
→ hello* と表示されればビルド成功です。

5. プログラムの実行

【 実行時の注意点 】 いよいよ実行です。カレントディレクトリにあるファイルを明示的に指定するため、頭に `./` を付けて実行ファイルを叩きます。

# 実行
./hello

[ 出力結果 ]
Hello, World!

6. 【重要解説】なぜ `InstalledDir` がそこにあるのか?

【 ここが仕組みのキモ 】 先ほどのバージョン確認で表示された `InstalledDir: /Library/Developer/CommandLineTools/...` というパス。これは、Macが「Xcode本体をインストールしなくても、開発に必要な最小限のツール(Command Line Tools)を個別に管理している場所」を指しています。

Clangが選ばれる理由
・Apple Silicon(arm64)への最適化が極めて優秀。
・エラーメッセージが親切で、初心者でも「どこでミスしたか」が分かりやすい。

arm64アーキテクチャの意識
今のMacで作った実行ファイルは、そのままでは古いIntel Mac(x86_64)では動きません。このように「どのCPU向けの実行ファイルを作るか」を意識することが、低レイヤを学ぶ醍醐味の一つです。

7. 理解度チェック!練習問題

【 チャレンジ 】 今回の手順を復習しましょう。Macのターミナルで、カレントディレクトリにある実行ファイル「hello」を起動するための正しいコマンドはどれでしょうか?

選択肢:
A. run hello
B. hello.exe
C. ./hello
D. clang hello

正解と解説を見る

正解:C. ./hello

セキュリティ上の理由から、ターミナルでは現在のディレクトリ(./)を明示的に指定して実行するルールになっています。clang hello と打つと、実行ではなく再コンパイルを試みようとしてエラーになるので注意しましょう。

8. まとめ:やってみて分かったこと

「コンパイル」という一見手間のかかるステップを踏むことで、テキストが「動くプログラム」に変わる瞬間を肌で感じることができました。IDE(統合開発環境)のボタン一つで実行するのも便利ですが、ターミナルで `clang` コマンドを直接叩く経験は、パスの概念やコンパイルプロセスの理解を深め、エンジニアとしての基礎体力を確実に底上げしてくれます。


PR

【DBテクニック】SQLの実行速度を劇的に変える「チューニングの定石」


同じ結果を得るSQLでも、書き方ひとつでデータベース内部の「仕事量」は天と地ほど変わります。今回は、実行計画を意識した「現場で即効性のある」チューニングの一般事項を整理しました。

1. 検索アルゴリズムを最適化する

【 現場の感触 】 データベースに「無駄な探索」をさせないのが基本です。特に存在確認などは、最後まで数えるか、見つかった瞬間に止めるかで雲泥の差が出ます。

COUNTよりEXISTS:1件でも条件に合う行が見つかれば探索を終了するため、全件スキャンするCOUNTより圧倒的に高速です。
ORよりIN:ORを多用するとインデックスが効かなくなる場合がありますが、IN演算子(定数リスト)はオプティマイザが最適化しやすく、実行計画が安定します。
「<」「>」よりBETWEEN:範囲指定が明確になり、インデックスレンジスキャンの効率が上がります。

2. 余計な「ソート」と「スキャン」を削る

【 現場の感触 】 データベースにとって最も重い処理の一つが「重複排除(ソート)」と「全表スキャン」です。これらを回避する選択がパフォーマンスの鍵です。

UNIONよりUNION ALL:UNIONは重複を消すために内部で「ソート」を強制します。重複がないと分かっているなら、ソート不要のUNION ALLが鉄則です。
COUNT(*)よりCOUNT(主キー):製品によりますが、主キーを指定することで「インデックスだけを見れば済む(Index Only Scan)」状態になり、データ本体へのアクセスを減らせる場合があります。
インデックスの作成:言うまでもなく基本中の基本。WHERE句やJOINキーへの適切なインデックス配置が全ての土台です。

3. 解析器(オプティマイザ)のクセを掴む

【 現場の感触 】 SQLは「書いた順序」が評価に影響することがあります。データベースの解析エンジンがどう動くかを意識して記述しましょう。

IN演算子の評価順序:一般にINの中身は「左から順に」評価されます。ヒットする確率が高い値を左に置くことで、判定コストを下げられます。
WHERE句の記述順序:多くのDBではWHERE句に書かれた順にフィルターをかけます。データ件数をより大きく絞り込める条件を「先に」書くことで、後続の判定対象を減らすのがセオリーです。

4. まとめ:チューニングは「DBとの対話」

今回紹介した項目は、いずれも「DBの内部リソース(CPU・メモリ・I/O)をいかに節約するか」に直結しています。

改善のチェックリスト
1. 重複排除(UNION)を無意識に使っていないか?
2. 全件カウント(COUNT)で存在確認していないか?
3. WHERE句の条件順序は最適か?


一つ一つは小さな工夫ですが、大量データを扱う本番環境ではこの積み重ねが「100倍の速度差」となって現れます。実行計画(EXPLAIN)を確認しながら、最適な一文を追求していきましょう。


【db.c】MacとC言語でデータベースをゼロからビルドしてみた完全実録


データベースの内部構造を理解するために、SQLiteの構造をモデルにした「db.c」をMac(Apple Silicon)環境で動かしてみました。環境構築から、誰もが抱く「あの疑問」の解決まで、省略なしの全行程を記録します。

1. 開発環境の準備:Xcode Command Line Tools

【 現場の感触 】 MacでC言語をコンパイルするには、Apple純正のコンパイラ(Clang)が必要です。まずはターミナルから、開発の「儀式」とも言えるツールのインストールから始めます。

# ターミナル(Terminal.app)を起動し、以下を入力
xcode-select --install

※すでにインストール済みの場合は「already installed」と表示されます。未導入ならポップアップに従ってインストールを完了させてください。

2. ソースコードの取得とディレクトリ作成

【 現場の感触 】 作業用フォルダを作成し、GitHubから最新の db.c をダウンロードします。curlコマンドを使って、GitHubのマスターリポジトリから直接保存します。

# 作業フォルダの作成と移動
mkdir ~/db-study && cd ~/db-study

# ソースコードのダウンロード
curl -O https://raw.githubusercontent.com/cstack/db_tutorial/master/db.c

3. コンパイルの実行

【 現場の感触 】 Mac標準のコンパイラ(Clang)を使用して、ソースコードを実行可能なバイナリファイルに変換します。エラーが出なければ、同じフォルダ内に「db」という実行ファイルが作成されます。

# clangでビルド。-o は出力ファイル名の指定。
clang db.c -o db

# 実行ファイルができたか確認(lsコマンド)
ls -F
→ db* と表示されれば準備完了です。

4. データベースの起動と操作(実践)

【 現場の感触 】 実際にプログラムを動かし、データを入力してみます。ここで驚くのが、「CREATE TABLE」を一度も叩かずに insert ができてしまうことです。

# 起動。引数にデータベースファイル名(mydb.db)を指定。
./db mydb.db

db > insert 1 user1 person1@example.com
Executed.
db > insert 2 user2 person2@example.com
Executed.

db > select
(1, user1, person1@example.com)
(2, user2, person2@example.com)

db > .exit

5. 【重要解説】なぜ「表」を作らなくても入るのか?

【 仕組み 】 通常のDBではありえないこの挙動の理由は、ソースコードの中にあります。この `db.c` は、「ID、Username、Email」という3つの列を持つ1つのテーブルしか扱えないように、最初から「構造」がプログラムに直接書き込まれている(ハードコードされている)からです。

スキーマとプログラムの合体
・通常の汎用DB:何が来るか不明なため、まず「表の定義(メタデータ)」を作る必要がある。
・自作 db.c:最初から「ID(4byte)、名前(32byte)、メルアド(255byte)」という1行291byteの固定データが来ると知っている。

メリット
1行のサイズが常に一定(固定長)なので、n行目を探すには「291 × n」バイト目を見に行くだけで済みます。この割り切りが、自作DBの学習をシンプルにしています。

6. 本質的な「検証」:データの永続性確認

【 現場の感触 】 最後に、プロセスを終了してもデータが消えないことを確認します。これこそが「データベースを極める」ための第一歩です。

[ 再起動テスト ]
① 再度実行: ./db mydb.db
② データの確認: select と入力

[ 結果 ]
先ほど入れたデータがそのまま表示されれば、「ファイルへのページ書き出し(Persistence)」の仕組みが正常に動いている証拠です。

7. まとめ:やってみて分かったこと

「CREATE TABLEがいらない」のではなく、「最初から1つだけCREATE済みの表がある状態で起動している」という設計。この仕組みを理解することで、DBの心臓部である「ファイルI/O」や「ページ管理」がいかに動いているか、その一端を鮮明に体感することができました。


巨大なRDBMSも、突き詰めればこうした「ファイルの読み書き」の積み重ね。自作してみることで、トラブル時にも「今はディスクと対話中かな?」といった具体的な想像力が働くようになります。


【データベースの知識】SQLパフォーマンスチューニングの即効ヒント


同じ結果を得るSQLでも、書き方ひとつでデータベース内部の「仕事量」は劇的に変わります。今回は、明日からの開発でそのまま使える、実行速度を劇的に改善するための6つのTipsを紹介します。

1. 実行効率を高める書き方の基本

【 基本 】 データベースエンジンにとって、無駄なスキャンや重複排除(ソート)は最大の敵です。まずは、より効率的な演算子や関数を選ぶことから始めましょう。

COUNTよりEXISTS:1件見つかった瞬間に検索を終了するため、全件カウントするより圧倒的に高速です。
UNIONよりUNION ALL:UNIONは重複を消すために内部で「ソート」が発生します。重複がないと分かっているならALL一択です。
ORよりIN:ORを多用するとインデックスが効かなくなる場合があります。複数の値を指定するならIN演算子の方が最適化されやすいです。

2. インデックスとデータアクセスの最適化

【 ポイント 】 インデックスはただ作るだけでなく、「使われるように書く」のがコツです。また、読み込むデータの範囲をいかに絞り込むかが勝負を分けます。

インデックスの作成:WHERE句やJOINの結合キーには必ずインデックスを検討しましょう。
COUNT(*)より主キー参照:製品によっては、主キーを指定することでインデックススキャンのみで処理が完結し、高速化する場合があります。
範囲指定はBETWEEN:`col > 10 AND col < 20` と書くより、`BETWEEN` を使う方がオプティマイザが範囲を正しく認識しやすく、読みやすいSQLになります。

3. まとめ:チューニングの優先順位

闇雲に修正するのではなく、まずは以下の表を参考に、コストの高い部分から手を入れていきましょう。

改善項目修正前推奨(修正後)効果の理由
存在確認 COUNT(*) > 0 EXISTS 見つかったら即終了
集合演算 UNION UNION ALL ソート処理をスキップ
複数指定 OR ... OR ... IN ( ... ) 可読性と解析効率の向上


パフォーマンスチューニングに「銀の弾丸」はありませんが、これらの定石を組み合わせることで、システムのレスポンスは確実に見違えるはずです。

【PostgreSQL】論理レプリケーションを自前で動かしてみた実録


実際に手を動かすのが一番。ということで、PostgreSQLの論理レプリケーションをローカル環境で構築してみました。パブリッシャー(出す側)とサブスクライバー(受ける側)のやり取りを、実際にコマンドを叩きながら追いかけます。

1. 設定の落とし穴:wal_levelの変更

【 現場の感触 】 最初のハードルは設定変更です。デフォルトでは論理レプリケーション用のログが出ないので、`postgresql.conf` を書き換えます。再起動が必要なので、本番環境なら「ちょっと待って」となるところですね。

# wal_level を logical にして再起動!
show wal_level;

[ 結果 ]
wal_level
-----------
logical ← これで準備完了。

2. パブリケーションの作成と権限の「儀式」

【 現場の感触 】 テーブルを作ってデータを放り込みます。ここで大事なのは「主キー(PK)」があること。論理レプリケーションでは「どの行を更新するか」を特定するためにPKが必須です。あとは接続用の専用ユーザーを作って、権限を付与します。この「運び屋」を作る作業がレプリケーションっぽさを感じさせます。

-- 全テーブルを対象にパブリケーション作成
CREATE PUBLICATION pub FOR ALL TABLES;

-- レプリケーション専用の「運び屋」ユーザーを作る
CREATE ROLE repluser LOGIN REPLICATION PASSWORD 'repluser';
GRANT pg_read_all_data TO repluser;

3. 同期開始!サブスクリプションの威力

【 現場の感触 】 サブスクライバー側で接続情報を指定してサブスクリプションを作成。実行した瞬間に、既存のデータが「バッ」と流れてくるのは見ていて気持ちいいものです。試しにデータを1行追加すると、即座に反映されるのが確認できました。まさにリアルタイム!

-- サブスクライバー側で実行(ここで同期が始まる)
CREATE SUBSCRIPTION sub CONNECTION 'host=... user=repluser...' PUBLICATION pub;

-- パブリッシャー側で insert
insert into sample_table values(3, 'ccc');

-- サブスクライバー側で確認
select * from sample_table; → ちゃんと 3 | ccc が反映されている!

4. 応用:同期を止めても「裏で溜まっている」

【 現場の感触 】 運用でよくある「一時停止(DISABLE)」も試しました。停止中にパブリッシャー側でガシガシ更新('ddd'を追加)しても、サブスクライバー側は静かなまま。でも、再度「ENABLE」にした瞬間、溜まっていた更新が追い付いてくる。この確実性が論理レプリの頼もしいところです。

検証のまとめ
1. DISABLEにすると、サブスクライバー側はピタッと止まる。
2. パブリッシャー側の変更は破棄されず、裏(スロット)に保持される。
3. ENABLEに戻すと、未反映分が高速に流し込まれる。


実際にやってみると、コマンド一発で同期が制御できる手軽さと、内部でWALがしっかり管理されている安心感がよく分かりました。バージョン間のデータ移行や、特定のデータ集約には最高に便利そうです。