まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2016/11

MySQL の主キーとかに auto_increment 属性を付けておくと連番の ID を自動生成してくれるので便利です。

例えばテーブル定義時にこんな感じで指定しておくと、
> create table names( id int primary key auto_increment, name varchar(100) );

データ挿入時に id を指定せず(気にせずに)他の値だけを挿入すればよくなって、
> insert into names( name ) values( '鈴木' );
> insert into names( name ) values( '佐藤' );
> insert into names( name ) values( '田中' );
> insert into names( name ) values( '山田' );
  :

ユニークな ID が自動で割り当てられる、ということが実現できます:
> select * from names;
+----+------+
| id | name |
+----+------+
|  1 | 鈴木 |
|  2 | 佐藤 |
|  3 | 田中 |
|  4 | 山田 |
     :
+----+------+

このように便利な auto_increment 属性ですが、便利な反面でコントロールする必要が出た場合に少しコツが必要になります。例えば 10 レコード入った時点で最後のデータに間違いが判明して、削除してしまったとします:
> select * from names;
+----+------+
| id | name |
+----+------+
|  1 | 鈴木 |
|  2 | 佐藤 |
|  3 | 田中 |
|  4 | 山田 |
     :
|  9 | 山本 |
| 10 | 木村 | ←ミスしたので削除↓
+----+------+

> delete from names where id = 10;

> select * from names;
+----+------+
| id | name |
+----+------+
|  1 | 鈴木 |
|  2 | 佐藤 |
|  3 | 田中 |
|  4 | 山田 |
     :
|  9 | 山本 |
+----+------+

この時点でテーブルには 9 件のレコードが入っています。ここで次のデータをインサートすると auto_increment はリセットされずに id = 11 として 10 件目のデータが挿入されます:
> insert into names( name ) values( '武田' );

> select * from names;
+----+------+
| id | name |
+----+------+
|  1 | 鈴木 |
|  2 | 佐藤 |
|  3 | 田中 |
|  4 | 山田 |
     :
|  9 | 山本 |
| 11 | 武田 |
+----+------+

ID をどう考えるかにもよりますが、この挙動を(ユニークである、という点は確保できているので)これはこれで OK と考えるのであれば特に気にする必要はありません。ただ連番ではなくなってしまうので、auto_increment を自分の意図で(上記の例であれば 10 から再スタートするように)リセットしたくなることもあります。

その場合は、以下のコマンドを実行します:
> alter table names auto_increment = 10;

names テーブルの auto_increment 属性値を 10 に(強制的に)変更しました。この状態からインサートすると、次に入るレコードの ID は 10 になります:
> insert into names( name ) values( '武田' );

> select * from names;
+----+------+
| id | name |
+----+------+
|  1 | 鈴木 |
|  2 | 佐藤 |
|  3 | 田中 |
|  4 | 山田 |
     :
|  9 | 山本 |
| 10 | 武田 |
+----+------+

ID は本来内部的に利用するべきものなので抜け番号があったからまずいわけでもなく、また連番でないといけないような制約が入るのは本来あまりいいことではありません。でもまあ気持ち的にスッキリはしますかね。

リレーショナルデータベースにデータを格納する場合、まずテーブルの定義を行って、その後にテーブル定義に沿ったデータを挿入(insert)していくことになります。

例えば、以下のようなデータをリレーショナルデータベースに格納することを考えてみます:
2016112101


(主キーとか制約とか)深く考えずにこのデータ格納用テーブルを定義すると、その SQL はこんな感じになると思います:
create table users( id long, name varchar(100), height float, weight float );

いわゆる「普通の」リレーショナルデータベースの場合、テーブルは行指向になります。1つのレコードを1行のデータと見なして格納します(直感的に理解しやすいと思います):
2016112102


これに対して、列指向と呼ばれるテーブル定義の場合、データは列ごとにまとまった形で持つことになります。そのため1つのレコードを格納する場合も、複数の列のデータに各値を追加することになります(こちらは直感的には理解しにくいと思います):
2016112103


なぜこんな直感的にわかりにくいテーブル定義が存在しているのか、というと、この方が便利になるケースがあるからです。例えば「身長の平均値」を取り出そうとすると、SQL では AVG 関数を使って以下のような処理を実行することになります:
select avg(height) from users;

SQL ではシンプルに見えますが、行指向データベースでこの時に内部で実行される処理は比較的重いものになります。各レコードを取り出し、各レコードの身長(height)の値を取り出しては合計して平均値を取り出し・・・という処理を行います。レコード数が増えるほど「各レコードを取り出す」回数が増え、全体負荷が大きなものになります。Oracle や SQL Server だと一発で標準偏差を求める STDDEV 関数とかもありますが、これも処理はかなり重いものです。

一方これが列指向データベースの場合、身長列のデータをひとまとめに取り出せるので、そこから平均値を計算するのが比較的楽になります。

では列指向データベースの方がパフォーマンスに優れているのか、というとそうとも限りません。データを1つ(1レコード)挿入する場合の内部処理を考えると、行指向では1つのレコードを追加するだけですが、列指向では各列に値を1つずつ追加することになります。データの追加や更新といった処理におけるパフォーマンスは、一般的には行指向データベースの方が優れていることになります。

要するに利用用途に応じて行指向データベースと列指向データベースを使い分けるのが理想ということになります。ただ「普段は行指向データベースを使って、統計処理を行う時のために列指向データベースに定期的に同期する」といったように、行指向データベースと列指向データベースを連携させる必要が出てきた場合など、データや形式の互換性などをあらかじめ考慮した上で製品やサービスを選ぶ必要がでてきます。


さて、そこで dashDB です。IBM Bluemix 上のサービスの1つとして提供されている DBaaS のデータベースですが、その特徴の1つに「テーブル定義の際に行指向か列指向かを指定できる」ことがあります。

例えば上記のテーブルを dashDB 上に行指向テーブルとして作成する場合は以下のような SQL コマンドを実行します:
create table users( id long, name varchar(100), height float, weight float ) organized by row;

一方、同じテーブルを列指向テーブルで作成する場合はこちらの SQL コマンドを実行します(ちなみに dashDB ではこちらがデフォルト):
create table users( id long, name varchar(100), height float, weight float ) organized by column;

つまり dashDB を使えばデータベース間の互換性を意識する必要もなく、1つのデータベースサービスインスタンスの中でテーブル毎に行指向か列指向かを指定して定義することができるのでした。トランザクション用途であれば行指向、分析用途であれば列指向といった具合にテーブルを定義することで、データベースサーバーの使い分けを意識することなく両方の用途で利用できるクラウドの DBaaS であることが最大の特徴だと思っています。

ちなみにこれが IBM Bluemix での dashDB のアイコンです。左が(普通の)dashDB で、右がトランザクションモードの dashDB Transaction 。パッと見て列指向か行指向かがわかるようになっている(らしい)です:
2016112201


加えて dashDB は Bluemix 上のフルマネージドな RDB クラウドサービスなので、データベースサーバーとしての管理は不要です。しかもデータ量が無料枠である1GB 未満であれば課金されることもないため、データベースとして本格的に利用する前の検証用途として安心して使ってくださいませ。


ElasticSearch で英語以外のテキストを使った検索エンジンを実装しようとすると、多くの場合 ICU(International Components for Unicode) というプラグインを使うことになります。特に日本語を扱う場合は kumoroji プラグインとセットで使うことが多いようですが、日本語文字列を正規化してフィルタリングをかける場合のデファクトスタンダードになっている組み合わせだと思います。

この ICU プラグインを ElasticSearch に導入する方法は ICU プラグインのホームページに記載されています:
https://github.com/elastic/elasticsearch-analysis-icu


具体的な導入方法は上記ページ内に記述されているように、コマンドラインから
# bin/plugin -install elasticsearch/elasticsearch-analysis-icu/(ICU のバージョン番号)

と入力・・・ だと思っていました。ところがこの方法は ElasticSearch のバージョンが 1.x までの頃の話でした:
2016111801


現在、多くの人が使っている ElasticSearch のバージョンは 2.x 以上だと思っています(2016/Nov/18 時点の最新バージョンは 5.0)。では ElasticSearch 2.x で ICU を導入するにはどのようにすればいいのでしょうか?

答はこれでした。かなりシンプルになりました:
# bin/plugin -install analysis-icu

これ、公式のどこかに書いてないのかな・・・

以前からずっと悩んでいたのが、PHP と IBM DB2 の相性の悪さ(苦笑)でした。

知らない方が多いと思うので補足すると、PHP からデータベースにアクセスする場合には PDO(PHP Data Object) の拡張モジュールが必要になります。更に PDO に加えてデータベースの種類毎(例えば MySQL とか PostgreSQL とか)に接続するための拡張モジュールをシステムに導入しておく必要があります。

CentOS での例を書いておくと、例えば MySQL や PostgreSQL を使う場合はそれぞれ以下のようにして、これらのモジュールをインストールすることができます(PDO 拡張モジュールごとインストールできます):
(MySQL の場合)
# yum install php-mysql
(PostgreSQL の場合)
# yum install php-postgresql

さて IBM DB2 のケースです。普通に "yum install php-db2" などと入力しても「何それ?」みたいな扱いになります(苦笑)。
# yum install php-db2
  :
  :
パッケージ php-db2 は利用できません。
エラー: 何もしません

ではどこから拡張モジュールをインストールするのか・・・ と思って調べてみたのですが、かなりハードル高そうです:
http://php.net/manual/ja/ibm-db2.installation.php


↑リンク先を読んでいただくとわかりますが、自分でヘッダファイルやライブラリファイルを用意して、ビルドしろ、ということになっています。ということは DB2 のインストールモジュールが必要になるわけで・・・ 普通に PHP から使いたいだけなのですが、クラウドの DBaaS として使うにはかなりハードルが高めに設定されているように見えますね。。


一方で IBM Bluemix 環境であれば、このハードルはぐっと下がります。なにしろ「用意されたビルドパックを使うだけ」です。

前提として、IBM Bluemix 上に dashDB のインスタンスを1つ作成し、以下のようなテーブル(names)を定義しておきます:
2016111401
↑ID(int) と NAME(varchar(20)) だけのテーブル定義

2016111402
↑3つのレコードを入力済み


この dashDB インスタンスを、IBM Bluemix 上に作成した PHP ランタイムからバインドしてアクセスする、というアプリケーションを作ります:
2016111401



ビルドパックはこちらにあるものをそのまま利用します:
https://github.com/ibmdb/db2heroku-buildpack-php

ソースコード(index.php)はこんな感じで用意します(ホスト名やユーザー名、パスワードなどは Bluemix の接続情報を参照して取得します):
<html>
<head>
<title>DB2</title>
</head>
<body>
<table border="1">
<tr><th>ID</th><th>NAME</th></tr>
<?php
$host = "xxxxx.services.dal.bluemix.net"; # DB2(dashDB) のホスト名
$port = 50000; # ポート番号
$db = "BLUDB"; # データベース名
$username = "(username)"; # ユーザー名
$password = "(password)"; # パスワード

$conn_str = "DRIVER={IBM DB2 ODBC DRIVER};DATABASE=" . $db . ";HOSTNAME=" . $host . ";PORT=" . $port . ";PROTOCOL=TCPIP;UID=" . $username . ";PWD=" . $password . ";";
$conn = db2_connect( $conn_str, $username, $password );
if( $conn ){
  $sql = "SELECT * FROM \"names\"";
  $stmt = db2_prepare( $conn, $sql );
  $result = db2_execute( $stmt );
  if( $result ){
    while( $row = db2_fetch_array( $stmt )){
?>
<tr><td><?php print_r($row[0]); ?></td><td><?php print_r($row[1]); ?></td></tr>
<?php
    }
  }else{
    $error = db2_stmt_error( $stmt );
    $errormsg = db2_stmt_errormsg( $stmt );
?>
<tr><td><?php print_r($error); ?></td><td><?php print_r($errormsg); ?></td></tr>
<?php } } ?> </table> </body> </html>

PHP 内で dashDB のインスタンスに接続して SQL を実行し、全レコードを取り出して表示する、という内容になっています。PHP としては非常にシンプルですが、"db2_" で始まる拡張関数が数か所使われており、DB2 用のコードになっていることがわかると思います。

こいつを cf コマンドでプッシュします。その際に -b オプションで上記のビルドパックを指定します:
# cf push (アプリ名) -b https://github.com/ibmdb/db2heroku-buildpack-php

この方法で作成したアプリケーションにアクセスすると、PHP の IBM DB2 拡張が読み込まれて実行され、期待通りの結果が表示されます:
2016111403


PHPer の皆さん、IBM Bluemix と dashDB であれば面倒な手続きなく PHP からも使えます。是非アプリを作ってみましょう!


なお、PHP の IBM DB2 拡張機能については公式ドキュメントが存在しているので、関数リファレンスも含めて詳しくはこちらを参照ください:
http://php.net/manual/ja/book.ibm-db2.php


この記事の最新版です:
ノーツでワトソンを体験!

上記記事は「IBM ノーツのデータベースコンテンツを IBM Watson の NLC(Natural Language Classifier)に学習させた上で問い合わせを行う」という内容でした。アクセスログを見る限り、そこそこの反響があったブログエントリでした。

が、ある時を境にして、この記事で提供していたサンプルが動かなくなってしまいました。直接の原因は別のブログエントリでも紹介した IBM Watson 側のセキュリティ仕様変更でした:
"javax.net.ssl.SSLException: java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 64" エラー


上記記事でも紹介していますが、この回避策の1つがプロクシー的なサービスを作る、というものでした。今回、そのプロクシーサービスを IBM Bluemix 上に作った上でサンプルを改良しました。
2016111101

新しいサンプルはこちらです。使い方は変わっていません:
http://dotnsf.blog.jp/nlcdemo.zip

参考程度に、以前のサンプルはこちらです(注 現在は正しく動きません ノーツ9の FP8 を適用することで動くようになりました)
http://dotnsf.blog.jp/nlcdemo.old.zip



新しい nlcdemo.nsf では、リクエストを全て http://watsonapiproxy.mybluemix.net/nlcproxy 経由で Watson に送信するようにしています。この方法で JDK 1.6 からの HTTPS リクエストをエラーなく実行できるようにしています。




このページのトップヘ