【Perl講座 第5回】データはデータベースへ!MariaDB(MySQL)連携とDBI/DBDモジュール完全攻略

「ファイル」で管理できるのは、日記帳までだ。

こんにちは!「LINUX工房」管理人の「リナックス先生」です。
前回までは、テキストファイルを使ってデータを保存・読み込みする掲示板を作りました。

しかし、データが1万件、10万件と増えたらどうなるでしょうか?
「特定の人の投稿だけ探したい」「日付順に並べ替えたい」と思った時、テキストファイルを全部読み込んで処理するのは時間がかかりすぎます。
また、ファイルが壊れるリスクや、同時アクセスの制御も大変です。

コウ君

先生、まさにそれで困ってました!
ログファイルが巨大になりすぎて、開くだけでサーバーが重くなるんです。
それに、間違えてファイルを消しちゃったら終わりですよね…。
もっとこう、スマートにデータを管理できる「専用の倉庫」みたいなものはないんですか?

リナックス先生

そのための道具が「データベース(RDBMS)」よ。
今回は、Linuxで最も使われているデータベース「MariaDB(MySQL互換)」を使って、大量のデータも一瞬で検索・保存できる仕組みを作りましょう。
そして、Perlからデータベースを操作するための最強の武器「DBIモジュール」の使い方を伝授するわ!

本記事では、AlmaLinux 9 にMariaDBを構築し、Perl標準のデータベースインターフェースである DBI (Database Interface) を使って、安全かつ高速にデータを操作する方法を完全解説します。

🐪 Perlサーバサイドプログラミング講座(バックナンバー)

現在地:【第5回】データはデータベースへ!MariaDB(MySQL)連携とDBI/DBDモジュール


第1章:MariaDB (MySQL) の環境構築

まずはデータを保存する「倉庫」であるデータベースサーバーを準備します。
AlmaLinux 9 では、MySQLから派生した完全互換のオープンソースDBである「MariaDB」が標準採用されています。

1. インストールと起動

# インストール
sudo dnf install mariadb-server mariadb -y

# 起動と自動起動設定
sudo systemctl start mariadb
sudo systemctl enable mariadb

# ステータス確認
sudo systemctl status mariadb

2. 初期セキュリティ設定

インストール直後はパスワード無しの危険な状態なので、初期設定ツールを実行します。

sudo mysql_secure_installation

対話形式で進みます。基本的には「Y」を選び、rootパスワードを設定してください。

3. 掲示板用のDBとユーザー作成

Perlから接続するためのデータベースとユーザーを作ります。
ここでは以下のように設定します。

  • データベース名: bbs_db
  • ユーザー名: bbs_user
  • パスワード: password123(※本番では複雑にしてください)
# rootでログイン
mysql -u root -p

# SQL実行(DB作成)
CREATE DATABASE bbs_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

# ユーザー作成と権限付与
CREATE USER 'bbs_user'@'localhost' IDENTIFIED BY 'password123';
GRANT ALL PRIVILEGES ON bbs_db.* TO 'bbs_user'@'localhost';
FLUSH PRIVILEGES;

# テーブル作成(投稿データ用)
USE bbs_db;
CREATE TABLE posts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100),
    message TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

# 確認して終了
SHOW TABLES;
EXIT;

第2章:Perlの標準規格「DBI」と「DBD」

Perlでデータベースを扱う際、必ず登場するのが「DBI」「DBD」です。
この2つの関係を理解しておくと、トラブル対応力が上がります。

DBI (Database Interface)

「データベースと話すための共通の窓口」です。
MySQLだろうが、PostgreSQLだろうが、SQLiteだろうが、Perlのコード側では「DBIの命令(connect, prepare, executeなど)」だけを使います。
これにより、データベースの種類が変わってもPerlのコードを書き換える必要がほとんどありません。

DBD (Database Driver)

「特定のデータベースと話すための通訳」です。
MySQL用なら DBD::mysql、PostgreSQL用なら DBD::Pg が必要になります。
DBIからの命令を受け取り、各データベース固有の処理に変換します。


第3章:モジュールのインストールと接続テスト

AlmaLinux 9 でDBIとMySQL用ドライバをインストールします。

1. パッケージのインストール

CPANコマンドではなく、OSのパッケージマネージャー(dnf)を使うのが安定運用のコツです。

# DBI本体と、MySQL(MariaDB)用ドライバを入れる
sudo dnf install perl-DBI perl-DBD-MySQL -y

2. 接続テストスクリプトの作成

ちゃんと繋がるか確認するために、小さなスクリプト db_test.pl を作ってみましょう。

#!/usr/bin/perl
use strict;
use warnings;
use DBI;

# 接続情報(DSN: Data Source Name)
# 書式: "DBI:ドライバ名:database=DB名;host=ホスト名"
my $dsn = "DBI:mysql:database=bbs_db;host=localhost";
my $user = "bbs_user";
my $pass = "password123";

# 接続オプション(エラー時に自動でdieするなど)
my %attr = (
    PrintError => 0,
    RaiseError => 1,
    AutoCommit => 1,
    mysql_enable_utf8 => 1  # 文字化け対策(超重要!)
);

# 接続実行
# eval { ... } は try-catch のようなもの
eval {
    my $dbh = DBI->connect($dsn, $user, $pass, \%attr);
    print "データベース接続成功!\n";
    
    # 切断
    $dbh->disconnect;
};

if ($@) {
    print "接続エラー: $@\n";
}

実行して「データベース接続成功!」と出れば準備完了です。

💡 文字化けの最大の原因
mysql_enable_utf8 => 1 オプションを忘れると、DBに保存される日本語が文字化けしたり、取り出した時に化けたりします。
現代のWeb開発では必須のオプションです。


第4章:SQLインジェクションの脅威と「プレースホルダ」

データベース操作に入る前に、セキュリティの最重要事項「SQLインジェクション」について警告しておきます。
これを守らないコードは、セキュリティホールそのものです。

危険な書き方(絶対NG)

# 変数をSQL文字列に直接埋め込む
my $sql = "SELECT * FROM users WHERE name = '$input_name'";
$dbh->do($sql);

もし悪意あるユーザーが $input_name' OR '1'='1 という文字を入力したらどうなるでしょう?
SQLは SELECT * FROM users WHERE name = '' OR '1'='1' となり、全ユーザーのデータが盗まれてしまいます。

安全な書き方(プレースホルダ)

変数の代わりに ?(ハテナ)を置きます。
DBIが自動的に変数を安全な形にエスケープして、? の部分に当てはめてくれます。

# 安全な書き方
my $sql = "SELECT * FROM users WHERE name = ?";
my $sth = $dbh->prepare($sql);
$sth->execute($input_name); # ここで値を渡す

本講座では、徹底してこの「プレースホルダ」を使った書き方を行います。


第5章:【実践】DB版掲示板の作成(書き込み編)

それでは、第3回で作った掲示板をデータベース対応版に改造しましょう。
まずはデータを保存する bbs_db_write.cgi です。

コードの記述

#!/usr/bin/perl

use strict;
use warnings;
use CGI;
use DBI;
use Encode;

binmode STDOUT, ":utf8";

my $q = CGI->new;
my $name = decode('UTF-8', $q->param('name'));
my $message = decode('UTF-8', $q->param('message'));

# DB接続設定
my $dsn = "DBI:mysql:database=bbs_db;host=localhost";
my $user = "bbs_user";
my $pass = "password123";
my %attr = ( RaiseError => 1, mysql_enable_utf8 => 1 );

print $q->header(-charset => 'UTF-8');
print "<html><body>";

if ($name ne "" && $message ne "") {
    eval {
        # DB接続
        my $dbh = DBI->connect($dsn, $user, $pass, \%attr);
        
        # SQL準備(プレースホルダを使用)
        my $sql = "INSERT INTO posts (name, message) VALUES (?, ?)";
        my $sth = $dbh->prepare($sql);
        
        # 実行(ここで変数を渡す)
        $sth->execute($name, $message);
        
        # ステートメントハンドルとDB接続を閉じる
        $sth->finish;
        $dbh->disconnect;
        
        print "<h2>書き込みました</h2>";
    };
    if ($@) {
        print "<h2>エラーが発生しました: $@</h2>";
    }
} else {
    print "<p>名前とメッセージを入力してください。</p>";
}

print "<a href='bbs_db_read.cgi'>掲示板を見る</a>";
print "</body></html>";

ファイル操作版に比べて、ロック制御(flock)やファイルオープンの記述がなくなり、コードがスッキリしましたね。
DBIが面倒な部分を肩代わりしてくれているからです。


第6章:【実践】DB版掲示板の作成(読み込み編)

次に、保存されたデータを表示する bbs_db_read.cgi を作ります。
SELECT 文を使ってデータを取り出します。

コードの記述

#!/usr/bin/perl

use strict;
use warnings;
use CGI;
use DBI;
use Encode;

binmode STDOUT, ":utf8";
my $q = CGI->new;

# DB接続設定(書き込み時と同じ)
my $dsn = "DBI:mysql:database=bbs_db;host=localhost";
my $user = "bbs_user";
my $pass = "password123";
my %attr = ( RaiseError => 1, mysql_enable_utf8 => 1 );

print $q->header(-charset => 'UTF-8');
print "<html><body>";
print "<h1>DB版 掲示板</h1>";

# 入力フォーム
print <<HTML;
<form action="bbs_db_write.cgi" method="POST">
    名前: <input type="text" name="name"><br>
    内容: <textarea name="message"></textarea><br>
    <input type="submit" value="書き込む">
</form>
<hr>
<table border="1">
<tr><th>ID</th><th>名前</th><th>メッセージ</th><th>日時</th></tr>
HTML

eval {
    my $dbh = DBI->connect($dsn, $user, $pass, \%attr);
    
    # データを新しい順(降順)に取得
    my $sql = "SELECT id, name, message, created_at FROM posts ORDER BY id DESC";
    my $sth = $dbh->prepare($sql);
    $sth->execute();
    
    # 1行ずつ取り出すループ
    # fetchrow_hashref は、結果をハッシュリファレンスとして返します
    while (my $row = $sth->fetchrow_hashref) {
        # $row->{カラム名} でデータにアクセスできる
        # mysql_enable_utf8 のおかげでデコード済み
        
        # HTMLエスケープ(簡易版)
        # 本来はエスケープ関数を通すべきですが、今回は省略
        
        print "<tr>";
        print "<td>" . $row->{id} . "</td>";
        print "<td>" . $row->{name} . "</td>";
        print "<td>" . $row->{message} . "</td>";
        print "<td>" . $row->{created_at} . "</td>";
        print "</tr>\n";
    }
    
    $sth->finish;
    $dbh->disconnect;
};
if ($@) {
    print "<p>エラー: $@</p>";
}

print "</table>";
print "</body></html>";

fetchrow_hashref の便利さ

fetchrow_hashref を使うと、データベースのカラム名(name, messageなど)をキーにしたハッシュリファレンスとしてデータを受け取れます。
配列で受け取る(fetchrow_array)よりも、コードが読みやすくなるのでおすすめです。


第7章:データベース運用の注意点

データベースを使うことで機能は飛躍的に向上しましたが、運用上の注意点も増えます。

1. 接続情報の管理

今回のコードでは、DBのパスワードをソースコードに直書きしていますが、これはセキュリティ上好ましくありません。
実際の運用では、Web公開領域外の設定ファイルにパスワードを記述し、Perlからそれを読み込んで使用するのが一般的です。

2. エラーハンドリング

DBサーバーがダウンしている場合など、接続エラーが発生するとスクリプトはそこで停止(die)します。
eval { ... } ブロックで囲むことで、エラーが発生しても画面が真っ白にならず、ユーザーに「現在メンテナンス中です」などのメッセージを表示することができます。


まとめ:DB連携で「本物」のアプリ開発へ

お疲れ様でした!
ファイル管理の掲示板から、データベース駆動の掲示板へと進化しました。
これで、数万件のデータがあってもサクサク動くシステムが作れるようになりました。

今回の重要ポイント:

  • PerlのDB操作は「DBI」と「DBD」モジュールを使う。
  • SQLインジェクションを防ぐため、必ず「プレースホルダ(?)」を使う。
  • 接続オプション mysql_enable_utf8 で文字化けを防ぐ。
  • fetchrow_hashref でデータをスマートに取り出す。

ここまでで、CGIを使ったWeb開発の基礎はほぼ網羅しました。
しかし、毎回 print "<html>..." とHTMLを手書きするのは大変ですし、デザインの変更も困難です。

次回、第6回は「モダンPerlの世界へ!Webフレームワーク「Mojolicious」入門」です。
古き良きCGIスタイルを卒業し、現代的なPerl開発の標準である「Webフレームワーク」を使って、より効率的でメンテナンスしやすい開発手法を学びます。
Perlの進化に驚くはずです。お楽しみに!

▼ エンジニアとしてのキャリアを加速させる ▼

DB連携を試す
「VPS」で自分専用環境

おすすめVPSを見る

バックエンド技術を年収に
「ITエンジニア転職」

転職エージェントを見る

コメント