非 Railsアプリのマルチデータベース 対 応 と 高 速 化 の 取 り 組 み ( 株 ) 富 士 通 システムズ イースト 冨 田 昌 宏 2015-11-12 RubyWorld Conference
自 己 紹 介 冨 田 昌 宏 ( 株 ) 富 士 通 システムズ イースト 長 野 勤 務 OSS 推 進 フォーラム アプリケーション 部 会 に 参 加 1998 年 から 個 人 的 にRubyを 使 用 2003 年 からRubyで 製 品 開 発
SYNCDOT メールソリューション 製 品 群 Webメール / SMTP /POP / IMAPサーバー メールフィルター / メールアーカイブ 実 行 時 使 用 OSS Ruby / Apache / Postfix / MySQL 開 発 時 使 用 OSS Git / Redmine / Jenkins / Docker / RSpec / Cucumber
非 Railsアプリのマルチデータベース 対 応 と 高 速 化 の 取 り 組 み
非 Rails 2003 年 から 開 発 開 始 Rails 以 前 主 な 機 能 はメール 処 理 ウェブは 補 助 的 製 品 のライフサイクルがRailsとあわない Railsのスピードは 速 すぎる
Railsでなければ 何? 開 発 当 初 Ruby 1.8.x CGI (Apache + mod_ruby) 生 JavaScript 独 自 O/R Mapper
ちなみに 現 在 は Ruby 2.1 Apache Passenger Rack Padrino( 一 部 ) jquery( 一 部 ) Sequel
ある 日
MySQLだけでなく Symfowareにも 対 応 せよ
Symfoware 富 士 通 RDBMS 製 品 2013 年 PostgreSQL 互 換 エディション 追 加 Ruby や Postfix 等 多 くの OSS からも 使 える MySQLに 特 化 した 独 自 O/R Mapper では 厳 し い
Sequel
Sequel MySQL/PostgreSQL/その 他 RDBMSに 対 応 した O/R Mapper SQLをRubyスクリプトで 記 述 conn = Sequel.connect('mysql://user:pass@host/db') conn[:users].insert(name: 'abc', age: 32) conn[:users].where(name: 'abc').select(:age).first # => { age: 32 } Hash で 条 件 の 指 定 値 の 設 定 値 の 取 得 複 雑 なクエリも 記 述 可 能
Sequel Active Record パターンにも 対 応 class User < Sequel::Model # usersテーブルの 構 造 を 動 的 に 取 得 end user = User.new user.name = 'abc' user.age = 32 user.save
Sequel マイグレーション 機 能 コネクションプール トランザクション/SAVEPONIT サブクエリ Join
Sequel 対 応 API, 構 文 の 違 いは 吸 収 してくれる API 以 外 は 地 道 にコツコツと MySQLはゆるいがPostgreSQLは 厳 しい 数 値 カラムと 文 字 列 との 比 較 カラムサイズ 以 上 に 文 字 列 格 納 DATEカラムに 不 正 な 日 付 を 登 録 等 々 その 他 : 大 文 字 小 文 字 を 区 別 するかどうか / カラ ム 型 の 違 い / AUTOINCREMENT
性 能 測 定 Jenkins sar 開 始 SMTP サービス 停 止 IMAP サービス 起 動 POP 測 定 sar 停 止 gnuplot
結 果 遅 い スループット OLD NEW 0 5 10 15 20 25 30 多 重 度
原 因 Sequel 層 が 増 えたためCPU 使 用 率 増 Active Record でDB 接 続 時 にテーブル 構 造 を 取 得
デーモンプロセス 古 き 良 きUNIXデーモンモデル クライアントからの 接 続 を 受 けると 子 プロセスをfork クライアントから 切 断 されると 子 プロセスが 終 了 Client Client Parent Child Child RDBMS
改 善 案 マルチスレッド 化 プロセス 生 成 コスト 減 コネクションプールを 使 用 Client Client Process Thead Thread RDBMS
スレッドセーフ 化 既 存 コードをマルチスレッド 対 応 地 道 にコツコツと クラスメソッド クラス 変 数 グローバル 変 数 シングルトンクラス プロセスIDを 一 意 性 のために 使 用 していないか
結 果 遅 い スループット OLD NEW 0 5 10 15 20 25 30 多 重 度
原 因 Rubyは1プロセスが 同 時 に1CPUしか 使 えない 複 数 スレッドを 作 成 しても 同 時 に 動 けるのは1 個 だけ クライアントから 大 量 の 接 続 があると サーバー のすべての CPU を 使 いきってないのに 頭 打 ち マルチプロセス&マルチスレッドにしたい
ParallelServer マルチプロセス&マルチスレッドサーバーライブラリ Client Client Parent Child Child Thread Thread Thread Thread RDBMS
ParallelServer 最 小 / 最 大 プロセス 数 プロセスあたりのスレッド 数 を 指 定 して プロセスが 増 減 して 動 く # TCP/IP 12345 ポートで 待 ち 受 ける ParallelServer::Prefork.new(12345, max_threads: 5, max_processes: 10).start do sock, addr # ここは 子 プロセス sock.puts 'Who are you?' name = sock.gets sock.puts "Hello, #{name}" end
結 果 スループット OLD NEW 0 10 20 30 40 50 60 70 80 90 100 多 重 度
GC メモリ 使 用 量 を 抑 えるためGC.startしてた マルチスレッドプロセスだと 全 スレッドが 停 止 して しまう GC.start 廃 止
結 果 スループット OLD NEW 0 10 20 30 40 50 60 70 80 90 100 多 重 度
メモリ 使 用 量
GC.startあり メモリ 使 用 量 OLD NEW 0 10 20 30 40 50 60 70 80 90 100 多 重 度
GC.startなし メモリ 使 用 量 OLD NEW 0 10 20 30 40 50 60 70 80 90 100 多 重 度
メモリ 使 用 量 削 減 Sequel のスキーマキャッシュを 使 用 できるだけオブジェクトを 作 らない 無 駄 な 処 理 を 見 直 し Timeout 削 減 jemalloc
Timeout 削 減 データを1 行 出 力 する 毎 にタイムアウト 処 理 大 量 にスレッド 生 成 & 破 棄 msgs.each do msg 処 理 Timeout.timeout(999) do socket.write data end end 仕 組 みを 変 更 して 性 能 向 上 &メモリ 使 用 量 削 減
jemalloc 逆 効 果 Rubyで1000スレッド 作 成 したときのメモリ 使 用 量 の 増 分 (MB) 1600 1400 1451 1200 1000 MB 800 600 400 200 0 41 標 準 jemalloc
最 新 状 況 (スループット) スループット OLD NEW 0 5 10 15 20 25 30 多 重 度
最 新 状 況 (メモリ) メモリ 使 用 量 OLD NEW 0 10 20 30 40 50 60 70 80 90 100 多 重 度
まとめ Sequel Rubyの 構 文 でSQLクエリを 組 み 立 てるのがかなり 読 みやすい マルチデータベース 対 応 でなくてもおすすめ マルチプロセス&マルチスレッド 推 測 するな 計 測 せよ 計 測 グラフ 生 成 まで 自 動 しておくと 捗 る