せっかく、最強.NET開発PCにSSDを入れたので、SQL Serverがどれくらい早くなるか計測してみます。つーか、もともとその目的もあって、SSD にしています。大容量の SSD はそれなりにお高いので、本当なら 64GB ぐらいの安めのを買って試してみたいところなのですが、まだ買ってないので。
PLEXTRO PX-512M5P
http://www.goplextor.com/jp/index.php/m5-pro
WDC WD20EZRX-00DC0B0 1TB
という WD にあまりに不利な状況なのですが…まあ、ひと昔前の HDD と最新の SSD を比較するということでやってみます。状況としては、昔の HDD に SQL Server をがりがりやってチューニングして頑張って、という状況だけど、最近データアクセスが遅くて困る。となると、もっとチューニングをするのか、CPU を変えるのか、ソフトウェアをリプレースするのか?と色々考えるけども、HDD から SSD にデータベースを乗せ換えるだけでも十分じゃない?ってところです。
以下、100万件のWEBアクセスログを、BulkCopy でインサートして計測しています。100万件とは言いますが、実は大したことがなくて、実際に業務で扱うデータは1億件ぐらいと桁が2桁ぐらい違いますよね。以前は、この100万件扱うのにひいこら言っていたはずなんですが、CPU とメモリの関係なのか、結構スムースに動きます。なので、相対値としてみてください。
SSD にバルクインサート中
100万件インサートで 7.8秒
HDD にバルクインサート中
100万件インサートで 32.1秒
実測値で5倍ぐらいの違いが出ます。これは、大量のデータインサートなので、状況が特別なのですが、書き込みの場合でも SSD のほうが遥かに早い。
で、別途読み込み(SELECT)をやってみたのですが、こっちのほうはディスクキャッシュが効いていると、どちらも .3 秒とかで返ってくるので比較になりません。当然、インデックスがない状態でこれですから。SELECT のほうは、別途計測方法を考えてリトライしましょう。
■実験ソース
void GoBlukCopy(string CNSTR) { DataTable dt = _dt; SqlConnection cn = new SqlConnection(CNSTR); SqlBulkCopy bc = new SqlBulkCopy(cn); DateTime start = DateTime.Now; bc.BulkCopyTimeout = 0; bc.DestinationTableName = "logs"; cn.Open(); bc.WriteToServer(dt); cn.Close(); DateTime end = DateTime.Now; label2.Text = string.Format("BulkCopy完了 {0:#.0} sec", ((TimeSpan)(end - start)).TotalSeconds); }
ついでにアクセスログから DataTable を作るところも。非同期を使っているので、別の記事にでも解説を。
DataTable _dt; int _count; bool _completed; /// <summary> /// ファイルから読み込み /// </summary> /// <returns></returns> public async Task<DataTable> MakeDataTable() { DataTable dt = new DataTable(); dt.Columns.Add(new DataColumn("dt", typeof(string))); dt.Columns.Add(new DataColumn("code", typeof(string))); dt.Columns.Add(new DataColumn("ip", typeof(string))); dt.Columns.Add(new DataColumn("req", typeof(string))); dt.Columns.Add(new DataColumn("url", typeof(string))); _completed = false; await Task.Factory.StartNew( () => { var sr = System.IO.File.OpenText(@"D:sitelogsout3.csv"); _count = 0; while (sr.EndOfStream == false) { string line = sr.ReadLine(); string[] v = line.Split(new string[] { "t" }, StringSplitOptions.None); DataRow row = dt.NewRow(); dt.Rows.Add(row); row["dt"] = v[0]; row["code"] = v[1]; row["ip"] = v[2]; row["req"] = v[3]; row["url"] = v[4]; _count++; } sr.Close(); _completed = true; _dt = dt; }); return dt; } private void button2_Click(object sender, EventArgs e) { Task t1 = MakeDataTable(); Task t2 = Task.Factory.StartNew(async () => { while (_completed == false) { await Task.Delay(500); label2.Invoke((MethodInvoker)(() => label2.Text = string.Format("{0} 件読み込み中", _count))); } label2.Invoke((MethodInvoker)(() => label2.Text = string.Format("{0} 件読み込み完了", _count))); }); }