2014-10-22

僕の Redshift の圧縮エンコード使い分けメモ

最近 Redshift を触っていて、圧縮エンコードについて調べることがあったのでメモメモしておきます。なお、2014 年 10 月時点での情報であることと、わりとざっくりとした確認だったので不正確な情報が混じっているかもしれないのでご承知おきください。

文字列データ (VARCHAR など)

  • 選択肢としては、 text255, text32k, bytedict, lzo あたり
    • 空間効率的には text255 text32k はあんまりよろしくなさげ
    • また、text255 はカラムサイズが 255 を超えるカラムに適用することができない (厳密には、255 バイトを超える文字列が入っている場合に適用できない、となる)
  • 値の種類数が少なく (目安として、256 個以下ぐらい)、かつ種類が増える可能性が低い場合
    • bytedict もしくは lzo を選ぶのがよい
    • 種類数が 256 を超える場合であっても、出現頻度に偏りがある場合は bytedict を選択するのもありっぽい
    • ただし、文字列の平均長が長い場合は bytedict はあんまり性能よろしくない?
  • 最初は値の種類数が少ないが、時間経過とともに使われる値の種類が徐々に増える場合
    • lzo を選ぶのがよい
  • 時間経過とともに使われる値の種類が増えるが、ソートキーを考慮すると使われる値の種類が少数に限定されると想定できる場合
    • lzo を選ぶのがよい
  • 値の種類数が多い場合
    • lzo を選ぶのがよい

というわけで、迷ったら lzo を選べばだいたいいい感じだと思う。

数値データ

  • 選択肢としては、 bytedict, delta/delta32k, mostly8/16/32 あたり
    • lzo はあんまり向いてなさげ
  • 値の種類数が少なく、かつ種類が増える可能性が低い場合
    • 0 付近の値が頻出する場合は mostly8 を選ぶのがよい
    • それ以外では bytedict を選ぶのがよい
  • 時間経過とともに使われる値の種類が増えるが、ソートキーを考慮すると使われる値の種類が少数に限定されると想定できる場合
    • bytedict でよさそう
  • 値の種類数は多いが、出現する値に偏りがある場合
    • 0 近辺に偏っているなら、 mostly8/16/32 などを選ぶのがよい
    • それ以外なら bytedict がよさそう
  • ソートキーを考慮したときに値が昇順に並ぶ場合
    • delta/delta32k を選ぶのがよい
  • それ以外の場合
    • 諦めて、圧縮エンコードは設定しない (raw にする)

正直言って、数値データは実際のデータで性能を確認してみないとわからないと思う…

その他

  • runlength の使いどころがよくわからない…
  • 時間計算量
    • bytedict は時間計算量ちょっと高めな傾向がみられる
    • lzo はバランスがとれている感じ
9:30No comments

2014-09-21

第八回 #渋谷java にて、認証系を Java 8 で自作する話をしてきました

はじめに

毎回、他の発表者の方々が有益情報を発表してくれる中で僕一人が誰得情報をひたすら発信しているわけですが、今回もご多分に漏れず Java 8 で認証系を自作する、というニッチな話をしてきました。

昨今のパスワード流出系のセキュリティインシデントが業界内で話題になるたびに「認証系なんてものは自作するようなもんじゃない」という認識が醸成されつつあると思います。しかし、マイナー or オレオレ Web アプリケーションフレームワークなどを利用しているとそうもいってはおられず、認証系が用意されていないときはやむなく認証系の自前実装が要求されることもあるでしょう。自分自身も周りを見渡していても、過去何度かそういうことがありました。

ただ、実装者の認証セキュリティに対する認識や知識によって脆弱な認証系を作り込むことも少なくなく、そういうのをなるべく減らしたいなー、という思いを込めて今回の発表に至りました。

どうしても認証系を自前実装しなければならないときに気をつけたいこと

前述した発表資料に書いてあることにはなりますが、今一度文章に起こしておきます。

アカウント別 salt の生成と暗号論的擬似乱数生成器の利用

セキュリティを少しかじったことがある人であれば「salt ってやつとパスワードを組み合わせたものをハッシュする」ということぐらいは知っているものと思います。ただ、どうも salt だけがひとり歩きしていることがあったりして、アプリケーションで共通の事前に用意された salt を使いまわす、みたいなアレな実装を見たことがある人はそこそこいるんじゃないでしょうか?

本来は、認証単位であるアカウント別に salt を生成すべきであり、かつ salt の生成には暗号論的擬似乱数生成器を用いるべきです。この salt の生成を Java で実現するならば、

    static byte[] newSalt(int length) {
        try {
            byte[] result = new byte[length];

            SecureRandom.getInstance(
                    // "NativePRNGNonBlocking"
                    // "NativePRNGBlocking"
                    "SHA1PRNG"
            ).nextBytes(result);

            return result;

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

のような実装になることでしょう。 SecureRandom.getInstance() の呼び出しで指定しているのは擬似乱数生成アルゴリズムで、Java 7 以前は SHA1PRNG だけだったのが、Java 8 では新たに

  • NativePRNG
  • NativePRNGBlocking
  • NativePRNGNonBlocking

が (Windows を除く) *nix-like なプラットフォーム向け JRE にて導入されたようです。

暗号学的ハッシュ関数の利用とストレッチング

salt を生成したら、あとはパスワードと組み合わせてハッシュ化、なのですが、これも単に SHA-* を使えばいい、というほど簡単ではありません。

結論から言ってしまえば、PBKDF2 などのキー派生 (導出?) 関数を使ってストレッチングすべき、となります。

Java 7 では PBKDF2 の実装は PBKDF2WithHmacSHA1 しか用意されていませんでしたが、Java 8 にて

  • PBKDF2WithHmacSHA224
  • PBKDF2WithHmacSHA256
  • PBKDF2WithHmacSHA384
  • PBKDF2WithHmacSHA512

の 4 つが導入されました。この PBKDF2 を用いたパスワードのハッシュ化コードは以下になります。

    static byte[] hash(String password, byte[] salt, int numIterations, int numHashLength) {
        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, numIterations, numHashLength);

        SecretKeyFactory factory;
        try {
            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }

        try {
            return factory.generateSecret(keySpec)
                    .getEncoded();

        } catch (InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }
    }

まとめ

なんだかんだ書いてはいますが、やっぱり僕含めて素人は認証系の実装をするのはやめたほうがいいですね (過去何度もダメな認証系を作ってしまったし…)。

既存ライブラリの Apache Shiro とか Spring Security とか、僕は使ったことないですが、多分このあたりの面倒なお仕事をこなしえくれるのではないでしょうか。

なお、

こういうライブラリもあるようですよ!

参考文献

2:52No comments

2014-09-17

"Can't connect to MySQL server on 'HOST_NAME' (111)" のエラーが出たときの原因の一つと対処法

VM 上の Ubuntu に apt-get で mysql-server-5.6 をインストールし、VM の外から VM 内部の mysqld に接続しようとしたときにこのエラーが発生した。

原因は my.cnf に

bind-address = 127.0.0.1
が設定されていたためであった。

この行を my.cnf から削りとって、ようやくVM 外部から mysqld に接続できるようになったけど、毎回こんな感じのトラブルで時間を喰われてしまっているわけで、MySQL 力が低いと何かと生きるのが辛い…

23:59No comments

2014-09-15

Java のプロパティファイルは UTF-8 エンコーディングで記述したい

「えーマジ native2ascii!?」「プロパティエディタプラグインが許されるのは J2SE 5.0 までだよねー」

ジャバエンジニアはそろそろ、プロパティファイルを扱う場合に native2ascii やら各種プロパティエディタプラグインやらの呪縛から解放されてもいいと思うのです…

概要

Java でアプリケーションの各種設定を記述したプロパティファイル (.properties) を取り扱う場合、 Properties クラスもしくは ResourceBundle クラスのお世話になるかと思います。

一昔前までは、プロパティファイル内で日本語などいわゆるマルチバイト文字を含む場合に、 native2ascii コマンドを利用して ASCII コードの文字のみで表現できる ユニコードエスケープ (!= UTF-xx エンコーディング) 表現に変換することが必要とされていました。

しかし、Java は 7 or 8 がメインストリームになり、また UTF-8 エンコーディングされたテキストを取り扱うことがわりと当たり前になりつつある昨今、いまだに Eclispe にわざわざプロパティエディタプラグインを導入してプロパティファイルをしこしこ編集したり、プロダクション環境でユニコードエスケープされたプロパティファイルを四苦八苦しながらパラメータ調整したりする現場があったりなかったりするようなので、ここで native2ascii とかプロパティエディタプラグインを必要と せずに UTF-8 エンコーディングされたプロパティファイルを扱う方法についてメモしておきます。

UTF-8 エンコーディングされたプロパティファイルを取り扱う方法

Properties クラスと ResourceBundle クラスそれぞれについて説明します。

その 1 : Properties クラスを使う場合

Properties#load(Reader) メソッドを呼び出す際の Reader オブジェクトを、UTF-8 エンコーディング指定した InputStreamReader オブジェクトにすれば OK です。

すなわち、以下のような実装になるでしょう。

その 2 : ResourceBundle クラスを使う場合

こちらは若干複雑になりますが、 ResourceBundle.Control クラスのサブクラスを用意して対処する方法をとります。

同クラスの newBundle() メソッドのオーバーライド実装にて、 Properties のときと同様に UTF-8 エンコーディング指定した InputStreamReader オブジェクトを用意し、 PropertyResourceBundle クラスのコンストラクタ引数にそのオブジェクトを指定します。

そして実際に ResourceBundle クラスのオブジェクトを取得する際に、その ResourceBundle.Control サブクラスのオブジェクトを ResourceBundle.getBundle(String, ResourceBundle.Control) メソッドの二つ目の引数に指定してやることで、UTF-8 エンコーディングなプロパティファイルを取り扱うことができるようになります。

実装は以下のとおり。

まとめ

  • PropertiesResourceBundle もどちらも、文字エンコーディング指定した InputStreamReader オブジェクトを扱うようにすれば、 native2ascii 的な呪縛から逃れることができるよ
  • UTF-8 エンコーディングなどにしておけば、プロダクション環境上でプロパティファイルをどうしても確認しなきゃいけなくなった場合でも、プロパティファイルに記述した日本語コメントを頼りにスムーズな確認ができるようになるよね
  • プロパティエディタプラグインはもうオワコン扱いでいいよね
23:173 comments

2014-09-11

Java 8 で Base64 エンコーディング機能が導入されたらしいので使ってみた

「何をいまさら」な感じの機能追加だけど、Java 8 から 標準のクラスライブラリで Base64 エンコーディング できるようになった。これはこれで嬉しいことなので、使い方とそのパフォーマンスをメモっておく。

使い方

まずはデモコードから。

エンコーダは以下のメソッド呼び出しで得られる 3 つの種類がある。

  • Base64.getEncoder()
  • Base64.getUrlEncoder()
  • Base64.getMimeEncoder()

対応するデコーダがそれぞれ用意されている。

  • Base64.getDecoder()
  • Base64.getUrlDecoder()
  • Base64.getMimeDecoder()

Base64.getUrlEncoder() で得られるエンコード文字列は、普通の Base64 エンコーディングではファイルパスや URI での利用時に問題となりうる +/ の文字の代わりに -_ を使ってエンコードしてくれる。「Base64 エンコーディングした文字列をファイル名に利用したい!」みたいなケースだととっても有用だと思う。

パフォーマンス

Java 7 以前は Apache Commons CodecBase64 クラス を使うことが一般的だったぽいので、それとの性能比較をしてみた。

今回は、メソッド呼び出しにおけるオーバーヘッドを見るスイート (上段) と、Base64 の変換処理の性能を見るスイート (下段) の二種類を用意した。

この結果から、

  • 全般的に、Java 8 で導入された Base64 クラスの方が処理性能的に優れている
  • 特に、Java 8 側の変換メソッドを呼び出す際のオーバーヘッドが Commons Codec のそれより明らかに小さいことがわかる
  • 変換処理の性能は、エンコーディングが 6 倍程度、デコーディングが 2 倍ちょっと、Java 8 の Base64 クラスの方が速い

ということがわかるかと思う。

なお、性能測定に用いたプログラムは以下のとおり。

まとめ

  • Base64 だけについて言えば、Commons Codec はその役目を終えた感がある
  • これからは Java 8 の Base64 を積極的に使うのがよさそうだね!
1:381 comment

2014-08-21

Docker の Official らしい MySQL のイメージを利用してみる

気づいたらいつの間にか Docker Official な MySQL イメージ が公開されていたので、ちょうどとある製品の検証目的で使い捨てられる MySQL の環境が欲しかったついでに試してみたところ、ちょっとハマって数時間を無駄にしてしまったことにカッとして書き殴ったメモ。

(photo by Mark Interrante)

達成したいこと

達成したかったことは以下のとおり。

  • お手軽にポイ捨てできる MySQL 環境を構築・用意したい
  • Dockerfile をシコシコつくるのは面倒だったので、既存の Docker イメージを利用したい
  • boot2docker はあんまりいい思い出がなかったので、Vagrant で構築した VM 上に Docker 環境を構築したい

やろうとしたこと

やろうとしたことの具体的な手順は以下のとおり。

  1. Ubuntu 14.04 の VM 環境を Vagrant で用意する
  2. 上記の VM 環境上に Docker をインストールする
  3. MySQL な Docker イメージを pull する
  4. pull した Docker イメージを run する

実際はどうだったのか?

1. Ubuntu 14.04 な VM 環境を Vagrant で用意する

Ubuntu 14.04 の daily Cloud Image を利用して vagrant init && vagrant up してみた。

Docker をインストール & docker pull するあたりまではうまくいったものの、MySQL イメージを docker run するところで、mysqld が起動しないという問題に遭遇してしまった。

以前、別件で orchardup/mysql の Docker イメージを使ったときはこんな問題は起こらなかったのになあ、と思いつつ docker logs container-id としてコンテナのログを確認してみると、

2014-08-20 16:31:09 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2014-08-20 16:31:10 1 [Warning] Buffered warning: Performance schema disabled (reason: init failed).

2014-08-20 16:31:10 1 [Note] Plugin 'FEDERATED' is disabled.
2014-08-20 16:31:11 1 [Note] InnoDB: Using atomics to ref count buffer pool pages
2014-08-20 16:31:11 1 [Note] InnoDB: The InnoDB memory heap is disabled
2014-08-20 16:31:11 1 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2014-08-20 16:31:11 1 [Note] InnoDB: Memory barrier is not used
2014-08-20 16:31:11 1 [Note] InnoDB: Compressed tables use zlib 1.2.3
2014-08-20 16:31:11 1 [Note] InnoDB: Using Linux native AIO
2014-08-20 16:31:11 1 [Note] InnoDB: Not using CPU crc32 instructions

…ログはここで途切れている。

2014-08-20 16:31:10 1 [Warning] Buffered warning: Performance schema disabled (reason: init failed).

の行が怪しいなあ、ということでこのエラーメッセージでぐぐってみると、Oracle のページ が引っかかった。

当該ページにある

you may have specified other Performance Schema variables with values too large for memory allocation to succeed.

を読む限り、「これメモリが足りないんじゃね?」疑惑が湧いてきたので、Vagrantfile を以下のようにいじいじして、メモリサイズを 1GB にしてみたら、ちゃんと起動するようになった。めでたしめでたし。

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # (省略)

  config.vm.provider "virtualbox" do |vb|
    vb.customize [ "modifyvm", :id, "--memory", 1024 ]
  end

  # (省略)
end

2. 上記の VM 環境上に Docker をインストールする

apt-get install docker.io でインストールできる Docker は 0.9 だった。

せっかくなので新しいバージョンを使ってみたいので、

curl -s https://get.docker.io/ubuntu/ | sh

として Ubuntu 向け最新のバージョンをインストールするのであった。

3. MySQL な Docker イメージを pull する

以前使っていた orchardup/mysql は GitHub の README を見るとオワコン宣言されていたので、Docker official らしい Docker イメージに乗り換えるのであった。

4. pull した Docker イメージを run する

とりあえず

docker run \
  --name hogehoge \
  -e MYSQL_ROOT_PASSWORD=hogehoge_password \
  -d \
  mysql

で mysql イメージのコンテナを立ち上げて、Docker コンテナをホストしている Ubuntu 上の MySQL CLI で

mysql -u root -phogehoge_password -h 127.0.0.1

としてみたんだけど、

Can't connect to MySQL server on '127.0.0.1' (111)

などと悲しいことを仰る MySQL CLI さんに、僕は涙を浮かべずにはいられなかった。

ps コマンドでコンテナ上の mysqld が稼動していることは確認できたものの、それ以上のことはわからず試行錯誤したりいろいろぐぐったりしているうちに、「これコンテナのポートが開いてないんじゃね?」疑惑が湧いてきて Difference between “expose” and “publish” in docker のページにたどり着いた。

当該 Stackoverflow のページの回答をかいつまんで説明すると、

  • Dockerfile にて EXPOSE でポート番号を設定すると、コンテナ間でポートを通じた通信ができるようになる
    • コンテナ間の通信をしたい場合は、これを設定すべき
  • Dockerfile での EXPOSE のポート番号指定に加えて、 docker run する際の -p hostPort:containerPort を指定すると、 コンテナ側のポート containerPort をホストのポート hostPort でパブリッシュする
    • コンテナ間だけでなく、Docker の外の世界と通信したい場合は、 -p も設定すべき

ということになる。そんなわけで、

docker run \
  -p 3306:3306 \
  --name hogehoge \
  -e MYSQL_ROOT_PASSWORD=hogehoge_password \
  -d \
  mysql

としてコンテナを立ち上げてみたところ、ようやく MySQL CLI からコンテナ上の mysqld にご挨拶をするハッピーエンディングを迎えることができたのであった。

まとめ

Docker ネタのブログエントリって、たいてい Dockerfile の手組みから始まる系のやつが多い印象なんだけど、既存の Docker イメージを活用するカジュアル利用的なブログエントリももうちょっと増えてもいいと思う。

おまけ

できあがった Vagrantfile はこちら。

2:21No comments