astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

Spark 2.0 で Spark SQLを試す

こんにちは!Spark大好きな朴です。

本日はSpark 2.0で大幅の改善が行われてたSpark SQLについて書こうと思います。
弊社ではCDHのバージョンアップをこまめに行うことでSpark,HBaseなどのプロダクトの最新バージョンを常に試せる環境を作っております。

Spark 2.0についても先日弊社福田のもう待てない、Spark2.0の導入と実践にも書いてたとおり もう使えるようになりました。

ということで少し乗り遅れた感もありますが、本日はSpark 2.0でSpark SQLの実力を試したいと思います。

Spark 2.0でSpark SQLの主な変更点は以下の3つ

  • SparkSession
  • 性能改善
  • サポートするSQLが増えた

本日は上記3つの改善について触れてみたいと思います。

【変更その1】 SparkSQLのニューエントリポイントSparkSession

Spark 1.6まではユーザは以下のようにSqlContext(SparkSQL)とHiveContext(HiveQL)を使い分ける必要がありました。

// Spark SQL
val sc = new SparkContext(spConf)
val sqlContext = new SQLContext(sc)
sqlContext.sql("select * from hoge").show()

// HiveQL
val hiveContext = new org.apache.spark.sql.hive.HiveContext(sc)
hiveContext.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
hiveContext.sql("LOAD DATA LOCAL INPATH 'temp/kv1.txt' INTO TABLE src")
hiveContext.sql("FROM src SELECT key, value").collect().foreach(println)


Spark 2.0では以下のようSparkSessionでSparkSQL,HiveQL両方書けるようになりました。

val spark = SparkSession.builder()
      .master("yarn")
      .appName("Test")
      .getOrCreate()

// Spark SQL
spark.sql("select * from hoge").show()

// HiveQL
spark.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
spark.sql("LOAD DATA LOCAL INPATH 'temp/kv1.txt' INTO TABLE src")
spark.sql("FROM src SELECT key, value").collect().foreach(println)

【変更その2】 性能面での改善

SQLおよびDataFrame関連の処理にてwhole stage code generationの新技術を使うことでおよそ2-10倍の性能改善を実現したそうです。
リリースノートの原文はこちら

それでは実際どうなのかちょっと試してみたいと思います。

以下同じ条件でSpark 1.6 と Spark 2.0でいくつか簡単なSQLを試してどれぐらい実行スピードが違うかを実験してみます。

データソースはwikipediaのデータを使います。 こちらのリンクよりenwiki-20170201-abstract.xmlをダウンロードします。 https://dumps.wikimedia.org/enwiki/20170201/

上記データは英語wikiの概要とタイトル、urlを含めたxmlになります。

<doc>
<title>Wikipedia: Anarchism</title>
<url>https://en.wikipedia.org/wiki/Anarchism</url>
<abstract>Anarchism is a political philosophy that advocates self-governed societies based on voluntary institutions.</abstract>
<links>
 <sublink linktype="nav"><anchor>External links</anchor><link>https://en.wikipedia.org/wiki/Anarchism#External_links</link></sublink>
</links>
</doc>
<doc>
 ..
 ..
</doc>


  • 前準備として上記のデータをhdfsにアップします。
hadoop fs -put enwiki-20170201-abstract.xml temp/


  • 以下のコマンドでspark2-shellを起動します。
spark2-shell --master yarn \
 --driver-memory 2G --executor-memory 4G  \
 --packages com.databricks:spark-xml_2.10:0.4.1 \
 --conf spark.serializer="org.apache.spark.serializer.KryoSerializer"

※注 spark2.0ではspark-xmlのバージョンを0.4.1以上に指定する必要があります。

  • XMLをDataFrameに変換します。
scala> val enwiki=spark.read.format("com.databricks.spark.xml").option("rowTag", "doc").option("inferSchema", "true").load("temp/enwiki-20170201-abstract.xml")
scala> enwiki.printSchema
root
 |-- abstract: string (nullable = true)
 |-- links: string (nullable = true)
 |-- title: string (nullable = true)
 |-- url: string (nullable = true)

scala> enwiki.createOrReplaceTempView("enwiki")


  • 実行時間計測用の関数を定義します。
scala > def showTiming[T](proc: => T): T = {
    val start=System.nanoTime()
    val res = proc
    val end = System.nanoTime()
    val esplatedSec = java.util.concurrent.TimeUnit.SECONDS.convert((end-start), java.util.concurrent.TimeUnit.NANOSECONDS)
    println("Time elapsed: " + esplatedSec + " secs")
    res
}


性能検証1 件数count

Spark 2.0

scala> showTiming{ enwiki.count() }
Time elapsed: 116 secs
res4: Long = 5328417

結果でました。実行時間は116秒、件数は532万件ちょっとありました。

同じ処理をSpark 1.6で実行してみた結果


scala> showTiming{enwiki.count()}
Time elapsed: 146 secs
res4: Long = 5328417

こちらは実行時間が30秒増えてますね。 劇的ではありませんが、DataFrameの件数カウントではSpark2.0のほうが早いことが分かります。

性能検証2 like検索

Spark 2.0

scala> showTiming{ spark.sql("select count(*) from enwiki where abstract like '%Robot%'").show() }
+--------+
|count(1)|
+--------+
|     769|
+--------+

Time elapsed: 100 secs

Spark 2.0でlike検索の実行時間は116秒、件数は769件ありました

同じ処理をSpark 1.6で実行してみた結果


scala> showTiming{ sqlContext.sql("select count(*) from enwiki where abstract like '%Robot%'").show() }
+---+
|_c0|
+---+
|769|
+---+

Time elapsed: 143 secs

こちらは実行時間が43秒増えてますね。countと同じくSpark2.0のほうが性能的には上ですね

性能検証3 join処理

join系の処理を試してみたいと思います。 enwikiとjoinするためのデータを準備するためにsimplewiki-20170201-abstract.xmlを 以下のリンクよりダウンロードします。 https://dumps.wikimedia.org/enwiki/20170201/

データの構造はenwikiと同じでurlが違うだけです。

<doc>
<title>Wikipedia: April</title>
<url>https://simple.wikipedia.org/wiki/April</url>
<abstract>April is the fourth month of the year, and comes between March and May. It is one of four months to have 30 days.</abstract>
<links>
<sublink linktype="nav"><anchor>The Month</anchor><link>https://simple.wikipedia.org/wiki/April#The_Month</link></sublink>
</links>
</doc>
<doc>
..
..
</doc>


  • データをhdfsに上げます
hadoop fs -put simplewiki-20170201-abstract.xml temp/


  • simplewikiのデータをロードしてDataFrameに変換します。

scala> val simplewiki=spark.read.format("com.databricks.spark.xml").option("rowTag", "doc").option("inferSchema", "true").load("temp/simplewiki-20170201-abstract.xml")

scala> simplewiki.printSchema
root
 |-- abstract: string (nullable = true)
 |-- links: string (nullable = true)
 |-- title: string (nullable = true)
 |-- url: string (nullable = true)

scala> simplewiki.createOrReplaceTempView("simplewiki")


  • 下記のSQLでenwikiとsimplewikiで同じタイトルのデータ同士でinner joinをします。

Spark 2.0
scala> showTiming{spark.sql("select count(*) from enwiki en inner join simplewiki simple on (en.title = simple.title)").show()}
+--------+
|count(1)|
+--------+
|  107550|
+--------+

Time elapsed: 128 secs

Spark 2.0では128秒かかりました。

Spark 1.6

scala> showTiming{sqlContext.sql("select count(*) from enwiki en inner join simplewiki simple on (en.title = simple.title)").show()}
+------+
|   _c0|
+------+
|107550|
+------+

Time elapsed: 152 secs

Spark 1.6では152秒となり、Spark2.0より遅いことが分かります。

とりあえず、以上で簡単にcount,like検索,joinでSpark 2.0とSpark 1.6でSQLの性能検証を実施してみました。 どの処理もSpark 2.0で性能が改善されてることが分かりました。

【変更その3】 SQL2003をサポート、99個のTPC-DS クエリサポート

主には以下可能になりました。

  • ANSI-SQLとHive QLの両方をサポートするネイティブSQLパーサー
  • DDLが書けるようになった
  • Subqueryが書けるようになった
    • in,exists,not in, not existsなどなど(これは嬉しいー)
  • View canonicalization support (viewが使えるようになった?)

それではサブクエリ関連ちょっと触ってみたいと思います。

  • 給料を管理するテーブル(DataFrame)を定義
scala> val df1 = spark.createDataFrame(Array(("1001",1000),("1002",2000),("1003",1300),("1004",1500),("1005",1600),("1006",1700),("1007",1600),("1008",1700),("1009",1000),("1010",1900)))
df1: org.apache.spark.sql.DataFrame = [_1: string, _2: int]

scala> val salary=df1.withColumnRenamed("_1", "staffId").withColumnRenamed("_2", "salary")
salary: org.apache.spark.sql.DataFrame = [staffId: string, salary: int]

scala> salary.show()
+-------+------+
|staffId|salary|
+-------+------+
|   1001|  1000|
|   1002|  2000|
|   1003|  1300|
|   1004|  1500|
|   1005|  1600|
|   1006|  1700|
|   1007|  1600|
|   1008|  1700|
|   1009|  1000|
|   1010|  1900|
+-------+------+


  • 社員情報を管理するテーブル(DataFrame)を定義

scala> val df2 = spark.createDataFrame(Array(("1001","Staff1","Marketing"),("1002","Staff2","Marketing"),("1003","Staff3","Marketing"),("1004","Staff4","Sales"),("1005","Staff5","Sales"),("1006","Staff6","Sales"),("1007","Staff7","Sales"),("1008","Staff8","Development"),("1009","Staff9","Development"),("1010","Staff10","Development")))
df2: org.apache.spark.sql.DataFrame = [_1: string, _2: string ... 1 more field]

scala> val depart=df2.withColumnRenamed("_1", "staffId").withColumnRenamed("_2", "staffName").withColumnRenamed("_3", "depart")
depart: org.apache.spark.sql.DataFrame = [staffId: string, staffName: string ... 1 more field]

scala> depart.show
+-------+---------+-----------+
|staffId|staffName|     depart|
+-------+---------+-----------+
|   1001|   Staff1|  Marketing|
|   1002|   Staff2|  Marketing|
|   1003|   Staff3|  Marketing|
|   1004|   Staff4|      Sales|
|   1005|   Staff5|      Sales|
|   1006|   Staff6|      Sales|
|   1007|   Staff7|      Sales|
|   1008|   Staff8|Development|
|   1009|   Staff9|Development|
|   1010|  Staff10|Development|
+-------+---------+-----------+


  • (not) existsを試してみる
scala> salary.createOrReplaceTempView("salary")

scala> depart.createOrReplaceTempView("depart")

scala> spark.sql("select * from salary s where exists (select 1 from depart d where s.staffId = d.staffId and d.depart = 'Marketing')").show()

+-------+------+
|staffId|salary|
+-------+------+
|   1001|  1000|
|   1002|  2000|
|   1003|  1300|
+-------+------+

scala> spark.sql("select * from salary s where not exists (select 1 from depart d where s.staffId = d.staffId and d.depart = 'Marketing')").show()
+-------+------+
|staffId|salary|
+-------+------+
|   1004|  1500|
|   1005|  1600|
|   1006|  1700|
|   1007|  1600|
|   1008|  1700|
|   1009|  1000|
|   1010|  1900|
+-------+------+


  • (not) inを試してみる

scala> spark.sql("select * from salary s where staffId in (select staffId from depart d where d.depart = 'Marketing')").show()
+-------+------+
|staffId|salary|
+-------+------+
|   1001|  1000|
|   1002|  2000|
|   1003|  1300|
+-------+------+

scala> spark.sql("select * from salary s where staffId not in (select staffId from depart d where d.depart = 'Marketing')").show()
+-------+------+
|staffId|salary|
+-------+------+
|   1004|  1500|
|   1005|  1600|
|   1006|  1700|
|   1007|  1600|
|   1008|  1700|
|   1009|  1000|
|   1010|  1900|
+-------+------+

エラーなく実行できました!

終わりに

去年(2016年)の12月にSpark2.1がリリースされて、Spark2系も開発が活発に進められてる印象があります。 本日はSpark2系のSQL面での主な改善点を書きましたが、上記以外にもたくさんの改善が行われてますので、 これから既存のSparkジョブをSpark2系向けに移行しながらその良さを実感したいと思います。

ではまた!

星屑のおじさま、野生のトラを撃つ

f:id:astamuse:20170220174018j:plain

こんにちは。石橋を叩き過ぎて割るタイプの福田です。

春の足音を聴きながら、雲方面への移住を進めています。

さて、寒さもピークの昼下がり、ソイラテを片手にラップトップ整理。

2年物・書き捨てのPythonスクリプトに、ふと足を止めました。

すれっからしですが、意外に役立つので取り上げておきます。

Mongo Oscillator

今から2年前の2015年3月、MongoDBのバージョン3.0がリリースされ、新しいストレージエンジンWiredTigerが選択できるようになりました。その後、同3.2で従来のMMAPv1に代わりデフォルトのエンジンとなります。

当時のホワイトペーパーには、大幅なパフォーマンス向上、透過的圧縮、フラグメンテーションからの解放とあり、食指が動きます。

しかし、直ぐにアップグレードするのは気が引けるので、諸々のテストを実施しました。

はじめに、実運用のケースに即したテストとして、一定量のデータに対して継続的な更新操作を行った際にデータの破損や肥大化、レプリケーションの破綻が起こらないかを確認するための簡単なコードを書きました。

当初、バックアップのダンプデータを入力としてシミュレーションを行っていましたが、以下の点で不満が出てきます。

  • I/Oの面での不利
  • BSONのデコードやドメイン特有の処理のオーバーヘッド
  • 複数環境でのテストツール一式の可搬性の欠如
  • 更新のシミュレーションの煩雑さと再現性の欠如

これらの問題点を解消すべく書いたのが、こちらのPythonスクリプトになります。

唐突すぎて何をやっているのか分からないと思いますので、簡単に解説します。

  • generate_object関数

seq_noで与えられた番号にkey_shift値を加えた値をシードとして生成した乱数を元に、所与の平均値と標準偏正規分布に従ったサイズのデータフィールドを持つオブジェクトを生成します。

オブジェクトの_idはseq_noのハッシュ値とし、幾つかの属性値を含む辞書を返します。

  • shoot関数

split_tasks関数で分割されたタスクを受け取り、担当分のデータの挿入/更新を行います。

並列処理の単位となります。

  • split_tasks関数

n件のデータ生成タスクを、parallel数のタスクに分割し、開始と終了番号のタプルを返します。

使い方

テストドライバのホストで、パラメータを指定して実行するだけです。

$ python mos.py -h
usage: mos.py [-h] [-n N] [-w W] [-p P] [-s S] [-z Z] [-c C] [-d D] --host
             HOST [--db DB] [--collection COLLECTION]

Mongo Oscillator

optional arguments:
 -h, --help            show this help message and exit
 -n N                  number of objects to generate
 -w W                  mongodb replicaset write concern
 -p P                  number of processes to run in parallel (split n into p
                       tasks)
 -s S                  key shift offset
 -z Z                  average object size
 -c C                  cheat ratio
 -d D                  stddev
 --host HOST           comma separated mongodb replicaset hosts
 --db DB               mongodb database name
 --collection COLLECTION

実行例

単発の実行

$ python mos.py -p 4 --host yin,yang -n 10000 -z 4096 -d 1024
Namespace(c=0.9, collection='star', d=1024, db='shooting', host='yin,yang', n=10000, p=4, s=1, w=2, z=4096)
30659   n=0 (2500)      10.1889410019 sec       245.36406674 tps
30657   n=0 (2500)      10.6970849037 sec       233.708531109 tps
30656   n=0 (2500)      11.1619198322 sec       223.975806812 tps
30658   n=0 (2500)      11.3631420135 sec       220.009571034 tps
30651   n=10000 11.3728001118 sec       879.290931145 tps

実際には、以下のように1ずつキーシフトしながら複数回のデータ上書き更新をシミュレートします。

$ for x in `seq 1 4`; do python mos.py -p 4 --host yin,yang -n 10000 -z 4096 -d 1024 -s $x; done

10000件、4分割(4プロセス)で、キーを1つずつシフトして4回書込み(初回の挿入と3回の更新)

パラメータの説明

  • -n 生成するオブジェクト数 (データの総件数)
  • -p タスク分割数(1つのタスクにつき1プロセス割り当て)、デフォルト値:2
  • -s キーシフトのオフセット(後述します)、デフォルト値:1 ※1
  • -z 平均のオブジェクトサイズ(byte)、デフォルト値:50 ※2
  • -d 標準偏差、デフォルト値:-1 (-zで指定した平均のオブジェクトサイズの10%)
  • -c ランダム生成文字列使い回し比率(生成の負荷を調整)、デフォルト値:0.9 ※3
  • –host MongoDB ホスト名(レプリカセットの場合はカンマ区切りで複数指定可)、必須
  • –db  MongoDB データベース名、デフォルト値:shooting
  • –collection MongoDB コレクション名、デフォルト値:star

※1 シーケンスで採番したキーを指定数分ずらすことで(オブジェクトからキーを切り離してシフト)、サイズの変動を伴うオブジェクトの更新をシミュレートしています。

更新時に全体の総サイズを変えずにオブジェクトサイズの変動をシミュレートすることで、フラグメンテーションとそれに伴うデータの肥大化傾向の有無や度合いを観察できることを期待しました。

以下、小さなデータを生成し、挙動を簡単に説明します。

データ件数3、平均データサイズ40、標準偏差10でデータ生成します。

$ python mos.py -p 1 --host yin,yang -n 3 -z 40 -d 10 -s 1

mongo shellでの確認

r0:PRIMARY> use shooting
r0:PRIMARY> db.star.find()
{ "_id" : "b6589fc6ab0dc82cf12099d1c2d40ab994e8410c", "length" : 52, "data" : "NnzxHPXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJjUPMVNPsZYi", "s
eq" : 0, "last_modified" : 1486707174.010212 }
{ "_id" : "356a192b7913b04c54574d18c28d46e6395428ab", "length" : 57, "data" : "ceRMIqFXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJjUPMVNPsZYiNLxB
", "seq" : 1, "last_modified" : 1486707174.016221 }
{ "_id" : "da4b9237bacccdf19c0760cab7aec4a8359010b0", "length" : 40, "data" : "tFGdXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJj", "seq" : 2, "la
st_modified" : 1486707174.01845 }

何度やっても生成結果が同じになります。

r0:PRIMARY> db.star.find()
{ "_id" : "b6589fc6ab0dc82cf12099d1c2d40ab994e8410c", "length" : 52, "data" : "NnzxHPXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJjUPMVNPsZYi", "s
eq" : 0, "last_modified" : 1486707200.27103 }
{ "_id" : "356a192b7913b04c54574d18c28d46e6395428ab", "length" : 57, "data" : "ceRMIqFXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJjUPMVNPsZYiNLxB
", "seq" : 1, "last_modified" : 1486707200.276858 }
{ "_id" : "da4b9237bacccdf19c0760cab7aec4a8359010b0", "length" : 40, "data" : "tFGdXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJj", "seq" : 2, "la
st_modified" : 1486707200.279132 }

1回目のシフト

$ python mos.py -p --host yin,yang -n 3 -z 40 -d 10 -s 2

シフト後の状態

r0:PRIMARY> db.star.find()
{ "_id" : "b6589fc6ab0dc82cf12099d1c2d40ab994e8410c", "length" : 57, "data" : "ceRMIqFXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJjUPMVNPsZYiNLxB
", "seq" : 0, "last_modified" : 1486707251.232851 }
{ "_id" : "356a192b7913b04c54574d18c28d46e6395428ab", "length" : 40, "data" : "tFGdXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJj", "seq" : 1, "la
st_modified" : 1486707251.232893 }
{ "_id" : "da4b9237bacccdf19c0760cab7aec4a8359010b0", "length" : 40, "data" : "uiduXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJj", "seq" : 2, "la
st_modified" : 1486707251.234418 }

_idの集合は据え置かれて、dataおよびlengthがシフトしているのが分かると思います。

2回目のシフト

$ python mos.py -p --host yin,yang -n 3 -z 40 -d 10 -s 3

シフト後の状態

r0:PRIMARY> db.star.find()
{ "_id" : "b6589fc6ab0dc82cf12099d1c2d40ab994e8410c", "length" : 40, "data" : "tFGdXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJj", "seq" : 0, "la
st_modified" : 1486707260.945545 }
{ "_id" : "356a192b7913b04c54574d18c28d46e6395428ab", "length" : 40, "data" : "uiduXXceRMIqFFEiwuLZXCxnbbyqtUBDmbqhAZJj", "seq" : 1, "la
st_modified" : 1486707260.942702 }
{ "_id" : "da4b9237bacccdf19c0760cab7aec4a8359010b0", "length" : 28, "data" : "PXMXXceRMIqFFEiwuLZXCxnbbyqt", "seq" : 2, "last_modified"
: 1486707260.943574 }

※2 dataフィールドのサイズ(バイト)を指定。data以外のフィールドがあるため、db.stats()のavgObjSizeとの差異が生じ、差異の影響はこの値が大きいほど小さくなります。

※3 生成のオーバーヘッドを減らすため、予めランダム文字列のテンプレートを用意しておいて使いまわし、パラメータで指定した一定の比率部分のみオブジェクト毎にランダム生成を行うようにしました(0.9という設定は各オブジェクト毎に決定されたdataフィールドのサイズのうち9割をテンプレートから使いまわすことを意味します)。

折しも

バージョン3.4を検証中のGCP環境での実行結果を上げておきます。

f:id:astamuse:20170220125143p:plain

データディレクトリのある/dev/sdb1の使用量は、既存のデータがあるため0.64Tがベースになります。

無事更新操作が完了し、ストレージサイズの肥大化がないことが確認できました。

  • MongoDBのバージョン: 3.4.1
  • 構成: 2ノード(PRI, SEC)+ ARBのレプリカセット
  • インスタンスタイプ
    • PRI,SEC: n1-highmem-4 (CPU:4, Mem:26G, SSD)
    • ARB: g1-small (CPU:1, Mem:1.7G)
    • ドライバホスト:n1-highmem-4(CPU:4, Mem:26G)

テストパラメータ

  • 件数: 10,000,000
  • 平均オブジェクトサイズ: 40960
  • σ: 10240
  • キーシフト: 1-8
  • 上書き回数: 7
  • write concern: 2
  • cheat_ratio: 0.95

まとめ

MongoDBのデータ更新をシミュレートする、Pythonスクリプトを取り上げました。

実際、バージョン3.0の初めの頃にこのテスト実行した際に良好な結果が得られず、移行を見合わせる判断材料となりました。

その後、バージョン3.2にて無事アップグレードと同時にWiredTigerへの移行を完了し、今日も正常稼働を続けています。

世の中にはY

おや、誰かきたようなのでこの辺で。

バナーデザインに最適なイメージ・ブレストとは!?(バナーレース竹葉亭うな重カップ)

f:id:astamuse:20161216160732p:plain ――――都内某所アスタミューゼ会議室。

マーケtng:「突然ですが、みなさん『イメージ・ブレスト』というのをご存じですか?」

部長srk&デザイナーkrt:「イメージ・ブレスト!?」
デザイナーkrt:(なにそれ。めんどそう。。)
部長srk:「なんすか、それ?」
マーケtng:「ご存じないですか(まあ、さっき俺が思いついたやつだし)。では説明しましょう」

イメージ・ブレストとは?

みなさん、ブレストは知ってますよね?そう、みんなで意見出し合って他人の意見に乗っかって新しいアイデアどんどん出してくアレです。平たく言うとアレを「言葉」ではなく、「クリエイティブ」でやろうってのが『イメージ・ブレスト』です。てことで、さっそくルールを説明します。

  1. まず、テーマ・目的を説明して一旦解散!
  2. 共有フォルダに作ったクリエイティブをどんどん入れてく(途中でもいいから。ローカルで作業しない!)
  3. 他人のクリエイティブをパクってもいい!(てゆーかむしろパクろう。訴求をパクって別のクリエイティブとか、クリエイティブをパクって別の訴求とか)
  4. 定期的に集まって自分のクリエイティブを説明。言葉でも軽くブレスト。で、また解散!
  5. これを繰り返してとにかくクリエイティブをいっぱい出す!

マーケtng:「つまり、デザイナーなら言葉じゃなく、クリエイティブでブレストしようってことです!」(`^´) ドヤッ!
部長srk:(お前デザイナーじゃねーじゃん)
デザイナーkrt:(やっぱめんどそう。。)
マーケtng:「今回はマンガを使ったバナー制作なんで、バナーレースも開催します!」
部長srk&デザイナーkrt:「……(なにそれ?)」

バナーレースとは?

  1. みなさんが作ったバナーから、配信したいバナー(無記名)を広告ディレクターhtnが選びます。

  2. 実際に配信してみて、最も成績のよかったバナーを制作した人に部長が銀座竹葉亭のうな重をご馳走します!

部長srk:「はぁ!?(聞いてねーし)」
デザイナーkrt:(うな重…!?)
マーケtng:「てことで、みなさんよろしくです!」
部長srk:「俺が勝ったらどーすんだよ!?」
マーケtng&デザイナーkrt:(独りで食えばいーじゃん)
マーケtng:「では一旦解散!」

こんな感じでただの思い付きから始まったイメージ・ブレストのバナーレース(竹葉亭うな重カップ)。果たしてどうなることやら……

エントリーはこちら!

★ぜひ、誰が勝つか予想しながら読み進めてください!

コンテンツ理解度100% × デザインセンス0% マーケtng
予想:〇(手堅い)オッズ:1.4倍
本企画の立案者でランディング先の設計もしてることから、理解度は間違いなく断トツ!絶望的なデザインセンスを除けばおそらく順当に勝ち上がってくることでしょう。
低血圧系無気力ガール デザイナーkrt
予想:△(やる気次第)オッズ:5.5倍
朝から晩までテンション寝起き状態で間違いなく今回もやる気はない。ただし、未確認情報でマンガ大好き腐女子との噂も…
俺には他にやるべきことがある! 部長srk
予想:▲(流れ次第で大穴)オッズ:11.0倍
そもそもバナーなんて作ってていいのか?もっと大事な仕事があるだろうに。時間がなくて作ってこないと予想されるが、いいクリエイティブが集まらない場合は部門長の責任感で仕上げてくる可能性もある大穴。

ところが、このレースで誰も予想しなかったことが起きる――――



第一回イメージ・ブレスト(第1コーナー)

――――早くも迎えた第一回ミーティング。で、集まったのがこれ。 f:id:astamuse:20161216160742p:plain ※大きな画像を見たい方はこちらよりご応募ください。

さあ迎えた第1コーナー!
マーケtng、大きくリード!
デザイナーkrt、建前程度に1コ。
部長srk、責任感で作ろうとしたけど直前で断念!

やっぱこうなるか。まぁいいや、やろう。てことで各々軽く自分のクリエイティブを説明。 ところが、デザイナーkrtが作ったクリエイティブの視点が意外とよかったんで、「こうすればいいんじゃない?」とか、「こうゆうのもアリじゃない?」など、第一回ミーティングは思ったより盛り上がりました。最後にもう一度、「他人のクリエイティブをもっとパクってとにかくアイデアを出しましょう」と念押しして会議を閉じました。

これがウォーミングアップになったのか、第二回ミーティングでは予想外の展開に――――



第二回イメージ・ブレスト(第2コーナー)

第二回ミーティングで集まったクリエイティブがまさかのこれ。 f:id:astamuse:20161216160748p:plain ※大きな画像を見たい方はこちらよりご応募ください。

第2コーナーを回って、
マーケtng、第1コーナーの優位にあぐらをかいたか伸び悩み。
デザイナーkrt、マーケtngをおさえてまさかのトップ!
部長srk、やはり脱落コースか!?

パクり合って新しいクリエイティブも生まれています。例えばこんな感じ。 f:id:astamuse:20161216160807p:plain f:id:astamuse:20161216160814p:plain ※もっと見たい方はこちらよりご応募ください。

おお~なんかほんとにブレストっぽくなってる(自分でもびっくり)。
て、喜んでる場合じゃない。
正直、相手は時間ない部長とやる気ないデザイナーなんで、うな重余裕だと思ってたのに、ちょっとピンチです。
(デザイナーkrt、まさかうな重で釣れた!?)

こうして迎えた選考会で、再び衝撃が走る――――



配信バナー選考会(最終コーナー)

――――選考会当日。何も知らない広告ディレクターhtnが選んだのはこちら。 f:id:astamuse:20161216160756p:plain ※大きな画像を見たい方はこちらよりご応募ください。

さあ、いよいよ迎えた最終コーナー!
マーケtng、再び大きくリード!
デザイナーkrt、ここでスタミナ切れか!?
部長srk、やっぱダメなのか!?

最終コーナーでなんとかリードは守れたけど、まさか全員生き残るなんて……
(選考会でみんな締め出そうと思ってた)
ここで、カンのいい読者はエントリーにあったオッズの意味に気づいたかもしれません。
そう、バナーレースの本番はここからです!

この中で最も効果のあったバナーはどれか!?
果たして部長srkはプライドにかけて追い上げることができるのか!?
このあと、配信直後にあるバナーがまさかの快進撃をみせる。
銀座竹葉亭うな重は誰の手に――――!?




配信結果発表

――――選考会で選ばれたバナーを配信して1ヵ月が経ちました。

ある程度結果が予想できたバナーも、やってみるまで誰にも予想できなかったバナーも、様々なクリエイティブを試すことができました。
そしてその中でCPAやCV数などから、優位性のある1枚のクリエイティブが決まりました。

みなさんは誰を予想しましたか?
バナーレースは、アスタミューゼ社内でも予想外の劇的な結末を迎えました。

結果を知りたい方は、ぜひ、
アスタミューゼの募集要項からご応募いただき、
「バナーレースの結果を教えてください」とお伝えください。

みなさまのご応募、心よりお待ちしております m(_ _)m

Copyright © astamuse company, ltd. all rights reserved.