JMX metrics exposed in Presto version 302

Summary

Presto provides JMX metrics to monitor its system but there is few description in the document what metrics we can use, so I checked it.

Monitoring · prestosql/presto Wiki · GitHub

I used CLI tool jmxterm to get the information.

Commands

$ echo domains | java -jar jmxterm-1.0.0-uber.jar -l localhost:8086 -n > domains.txt
$ sed -i "s/^/beans -d /g" domains.txt
$ cat domains.txt | java -jar jmxterm-1.0.0-uber.jar -l localhost:8086 -n > beans.txt
$ cat beans.txt | awk -F: '{print "info -d " $1 " -b " $2}' | java -jar jmxterm-1.0.0-uber.jar -l localhost:8086 -n > info.txt 2>&1

Results

domains

[ec2-user@ip-10-0-101-241 ~]$ cat domains.txt
JMImplementation
com.amazonaws.management
com.sun.management
io.airlift.discovery.client
io.airlift.discovery.store
io.airlift.event.client
io.airlift.http.client
io.airlift.http.server
io.airlift.jmx
io.airlift.log
io.airlift.node
io.airlift.stats
java.lang
java.nio
java.util.logging
org.eclipse.jetty.http2.server
org.eclipse.jetty.io
org.eclipse.jetty.jmx
org.eclipse.jetty.server
org.eclipse.jetty.server.handler
org.eclipse.jetty.server.handler.gzip
org.eclipse.jetty.servlet
org.eclipse.jetty.util.thread
org.eclipse.jetty.util.thread.strategy
presto.execution
presto.execution.executor
presto.execution.resourceGroups
presto.execution.scheduler
presto.failureDetector
presto.memory
presto.metadata
presto.operator.index
presto.plugin.hive
presto.plugin.hive.metastore
presto.plugin.hive.metastore.thrift
presto.plugin.hive.s3
presto.security
presto.server
presto.server.remotetask
presto.spiller
presto.sql.gen
presto.sql.planner.iterative
presto.sql.planner.optimizations
sun.nio.ch

beans

[ec2-user@ip-10-0-101-241 ~]$ cat beans.txt
JMImplementation:type=MBeanServerDelegate
com.amazonaws.management:type=AwsSdkMetrics
com.amazonaws.management:type=AwsSdkMetrics/1
com.sun.management:type=DiagnosticCommand
com.sun.management:type=HotSpotDiagnostic
io.airlift.discovery.client:name=Announcer
io.airlift.discovery.client:name=ServiceInventory
io.airlift.discovery.store:name=dynamic,type=DistributedStore
io.airlift.discovery.store:name=dynamic,type=HttpRemoteStore
io.airlift.discovery.store:name=dynamic,type=Replicator
io.airlift.event.client:name=EventClient
io.airlift.http.client:name=ForDiscoveryClient,type=HttpClient
io.airlift.http.client:name=ForDynamicStore,type=HttpClient
io.airlift.http.client:name=ForEventClient,type=HttpClient
io.airlift.http.client:name=ForExchange,type=HttpClient
io.airlift.http.client:name=ForFailureDetector,type=HttpClient
io.airlift.http.client:name=ForMemoryManager,type=HttpClient
io.airlift.http.client:name=ForNodeManager,type=HttpClient
io.airlift.http.client:name=ForScheduler,type=HttpClient
io.airlift.http.client:name=ForWorkerInfo,type=HttpClient
io.airlift.http.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=httpserverchannellistener
io.airlift.http.server:id=0,type=classpathresourcehandler
io.airlift.http.server:id=0,type=statsrecordinghandler
io.airlift.http.server:id=1,type=classpathresourcehandler
io.airlift.http.server:name=HttpServer
io.airlift.http.server:name=RequestStats
io.airlift.jmx:name=StackTraceMBean
io.airlift.log:name=Logging
io.airlift.node:name=NodeInfo
io.airlift.stats:name=PauseMeter
java.lang:name=Code Cache,type=MemoryPool
java.lang:name=CodeCacheManager,type=MemoryManager
java.lang:name=Compressed Class Space,type=MemoryPool
java.lang:name=G1 Eden Space,type=MemoryPool
java.lang:name=G1 Old Gen,type=MemoryPool
java.lang:name=G1 Old Generation,type=GarbageCollector
java.lang:name=G1 Survivor Space,type=MemoryPool
java.lang:name=G1 Young Generation,type=GarbageCollector
java.lang:name=Metaspace Manager,type=MemoryManager
java.lang:name=Metaspace,type=MemoryPool
java.lang:type=ClassLoading
java.lang:type=Compilation
java.lang:type=Memory
java.lang:type=OperatingSystem
java.lang:type=Runtime
java.lang:type=Threading
java.nio:name=direct,type=BufferPool
java.nio:name=mapped,type=BufferPool
java.util.logging:type=Logging
org.eclipse.jetty.http2.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=abstracthttp2serverconnectionfactory$http2sessioncontainer
org.eclipse.jetty.http2.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=http2cserverconnectionfactory
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=0,type=arraybytebufferpool
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=0,type=connectionstatistics
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=0,type=managedselector
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=0,type=managedselector$selectorproducer
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=1,type=managedselector
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=1,type=managedselector$selectorproducer
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=2,type=managedselector
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=2,type=managedselector$selectorproducer
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=3,type=managedselector
org.eclipse.jetty.io:context=HTTP/1.1|h2c@183fc2fa,id=3,type=managedselector$selectorproducer
org.eclipse.jetty.jmx:id=0,type=mbeancontainer
org.eclipse.jetty.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=abstractconnector$acceptor
org.eclipse.jetty.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=httpconfiguration
org.eclipse.jetty.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=httpconnectionfactory
org.eclipse.jetty.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=serverconnector
org.eclipse.jetty.server:context=HTTP/1.1|h2c@183fc2fa,id=0,type=serverconnector$serverconnectormanager
org.eclipse.jetty.server:id=0,type=server
org.eclipse.jetty.server.handler:id=0,type=errorhandler
org.eclipse.jetty.server.handler:id=0,type=handlercollection
org.eclipse.jetty.server.handler:id=0,type=handlerlist
org.eclipse.jetty.server.handler:id=0,type=requestloghandler
org.eclipse.jetty.server.handler:id=0,type=statisticshandler
org.eclipse.jetty.server.handler.gzip:context="ROOT@@http",id=0,type=gziphandler
org.eclipse.jetty.server.handler.gzip:id=0,type=gziphandler
org.eclipse.jetty.server.handler.gzip:id=1,type=gziphandler
org.eclipse.jetty.servlet:context="ROOT@@http",id=0,type=servletcontexthandler
org.eclipse.jetty.servlet:context="ROOT@@http",id=0,type=servlethandler
org.eclipse.jetty.util.thread:context=HTTP/1.1|h2c@183fc2fa,id=0,type=scheduledexecutorscheduler
org.eclipse.jetty.util.thread:id=0,type=queuedthreadpool
org.eclipse.jetty.util.thread:id=0,type=reservedthreadexecutor
org.eclipse.jetty.util.thread.strategy:context=HTTP/1.1|h2c@183fc2fa,id=0,type=eatwhatyoukill
org.eclipse.jetty.util.thread.strategy:context=HTTP/1.1|h2c@183fc2fa,id=1,type=eatwhatyoukill
org.eclipse.jetty.util.thread.strategy:context=HTTP/1.1|h2c@183fc2fa,id=2,type=eatwhatyoukill
org.eclipse.jetty.util.thread.strategy:context=HTTP/1.1|h2c@183fc2fa,id=3,type=eatwhatyoukill
presto.execution:name=QueryExecution
presto.execution:name=QueryManager
presto.execution:name=RemoteTaskFactory
presto.execution:name=TaskManager
presto.execution.executor:name=MultilevelSplitQueue
presto.execution.executor:name=TaskExecutor
presto.execution.resourceGroups:name=InternalResourceGroupManager
presto.execution.resourceGroups:name=chartio,type=InternalResourceGroup
presto.execution.resourceGroups:name=user,type=InternalResourceGroup
presto.execution.scheduler:name=NodeScheduler
presto.execution.scheduler:name=SplitSchedulerStats
presto.execution.scheduler:segment=all
presto.execution.scheduler:segment=machine
presto.failureDetector:name=HeartbeatFailureDetector
presto.memory:name=ClusterMemoryManager
presto.memory:name=general,type=ClusterMemoryPool
presto.memory:name=general,type=MemoryPool
presto.memory:name=reserved,type=ClusterMemoryPool
presto.memory:name=reserved,type=MemoryPool
presto.metadata:name=DiscoveryNodeManager
presto.operator.index:name=IndexJoinLookupStats
presto.plugin.hive:name=hive,type=FileFormatDataSourceStats
presto.plugin.hive:name=hive,type=HiveSplitManager
presto.plugin.hive:name=hive,type=HiveWriterStats
presto.plugin.hive:name=hive,type=NamenodeStats
presto.plugin.hive:name=hive,type=OrcFileWriterFactory
presto.plugin.hive:name=hive_ad,type=FileFormatDataSourceStats
presto.plugin.hive:name=hive_ad,type=HiveSplitManager
presto.plugin.hive:name=hive_ad,type=HiveWriterStats
presto.plugin.hive:name=hive_ad,type=NamenodeStats
presto.plugin.hive:name=hive_ad,type=OrcFileWriterFactory
presto.plugin.hive.metastore:name=hive,type=CachingHiveMetastore
presto.plugin.hive.metastore:name=hive_ad,type=CachingHiveMetastore
presto.plugin.hive.metastore.thrift:name=hive,type=ThriftHiveMetastore
presto.plugin.hive.metastore.thrift:name=hive_ad,type=ThriftHiveMetastore
presto.plugin.hive.s3:name=hive,type=PrestoS3FileSystem
presto.plugin.hive.s3:name=hive_ad,type=PrestoS3FileSystem
presto.security:name=AccessControlManager
presto.server:name=AsyncHttpExecutionMBean
presto.server:name=ExchangeExecutionMBean
presto.server:name=StatementHttpExecutionMBean
presto.server:name=TaskExecutorResource
presto.server:name=TaskResource
presto.server.remotetask:name=RemoteTaskStats
presto.spiller:name=SpillerFactory
presto.sql.gen:name=ExpressionCompiler
presto.sql.gen:name=JoinCompiler
presto.sql.gen:name=JoinFilterFunctionCompiler
presto.sql.gen:name=OrderingCompiler
presto.sql.gen:name=PageFunctionCompiler
presto.sql.planner.iterative:name=IterativeOptimizer,rule=AddExchangesBelowExchangePartialAggregationGroupId
presto.sql.planner.iterative:name=IterativeOptimizer,rule=AddExchangesBelowProjectionPartialAggregationGroupId
presto.sql.planner.iterative:name=IterativeOptimizer,rule=AddIntermediateAggregations
presto.sql.planner.iterative:name=IterativeOptimizer,rule=AggregationExpressionRewrite
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ApplyExpressionRewrite
presto.sql.planner.iterative:name=IterativeOptimizer,rule=CheckNoPlanNodeMatchesRule
presto.sql.planner.iterative:name=IterativeOptimizer,rule=CreatePartialTopN
presto.sql.planner.iterative:name=IterativeOptimizer,rule=DetermineJoinDistributionType
presto.sql.planner.iterative:name=IterativeOptimizer,rule=EliminateCrossJoins
presto.sql.planner.iterative:name=IterativeOptimizer,rule=EvaluateZeroLimit
presto.sql.planner.iterative:name=IterativeOptimizer,rule=EvaluateZeroSample
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ExtractSpatialInnerJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ExtractSpatialLeftJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=FilterExpressionRewrite
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ImplementBernoulliSampleAsFilter
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ImplementFilteredAggregations
presto.sql.planner.iterative:name=IterativeOptimizer,rule=InlineProjections
presto.sql.planner.iterative:name=IterativeOptimizer,rule=JoinExpressionRewrite
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeAdjacentWindowsOverProjects
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeFilters
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeLimitWithDistinct
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeLimitWithSort
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeLimitWithTopN
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MergeLimits
presto.sql.planner.iterative:name=IterativeOptimizer,rule=MultipleDistinctAggregationToMarkDistinct
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PickTableLayoutForPredicate
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PickTableLayoutWithoutPredicate
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ProjectExpressionRewrite
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneAggregationColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneAggregationSourceColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneCountAggregationOverScalar
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneCrossJoinColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneFilterColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneIndexSourceColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneJoinChildrenColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneJoinColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneLimitColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneMarkDistinctColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneOrderByInAggregation
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneOutputColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneProjectColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneSemiJoinColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneSemiJoinFilteringSourceColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneTableScanColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneTopNColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneValuesColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PruneWindowColumns
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushAggregationThroughOuterJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushLimitThroughMarkDistinct
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushLimitThroughProject
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushLimitThroughSemiJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushPartialAggregationThroughExchange
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushPartialAggregationThroughJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushProjectionThroughExchange
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushProjectionThroughUnion
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushRemoteExchangeThroughAssignUniqueId
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushTableWriteThroughUnion
presto.sql.planner.iterative:name=IterativeOptimizer,rule=PushTopNThroughUnion
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveEmptyDelete
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveFullSample
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveRedundantIdentityProjections
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveTrivialFilters
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveUnreferencedScalarApplyNodes
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RemoveUnreferencedScalarLateralNodes
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ReorderJoins
presto.sql.planner.iterative:name=IterativeOptimizer,rule=RewriteSpatialPartitioningAggregation
presto.sql.planner.iterative:name=IterativeOptimizer,rule=SimplifyCountOverConstant
presto.sql.planner.iterative:name=IterativeOptimizer,rule=SingleDistinctAggregationToGroupBy
presto.sql.planner.iterative:name=IterativeOptimizer,rule=SwapAdjacentWindowsBySpecifications
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformCorrelatedInPredicateToJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformCorrelatedLateralJoinToJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformCorrelatedScalarAggregationToJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformCorrelatedScalarSubquery
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformCorrelatedSingleRowSubqueryToProject
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformExistsApplyToLateralNode
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformUncorrelatedInPredicateSubqueryToSemiJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=TransformUncorrelatedLateralToJoin
presto.sql.planner.iterative:name=IterativeOptimizer,rule=ValuesExpressionRewrite
presto.sql.planner.optimizations:name=PlanOptimizer,optimizer=AddExchanges
presto.sql.planner.optimizations:name=PlanOptimizer,optimizer=PredicatePushDown
sun.nio.ch:context=HTTP/1.1|h2c@183fc2fa,id=0,type=serversocketchannelimpl

bean info

[ec2-user@ip-10-0-101-241 ~]$ cat info.txt
Welcome to JMX terminal. Type "help" for available commands.
#mbean = JMImplementation:type=MBeanServerDelegate
#class name = javax.management.MBeanServerDelegate
# attributes
  %0   - ImplementationName (java.lang.String, r)
  %1   - ImplementationVendor (java.lang.String, r)
  %2   - ImplementationVersion (java.lang.String, r)
  %3   - MBeanServerId (java.lang.String, r)
  %4   - SpecificationName (java.lang.String, r)
  %5   - SpecificationVendor (java.lang.String, r)
  %6   - SpecificationVersion (java.lang.String, r)
#there's no operations
# notifications
  %0   - javax.management.MBeanServerNotification(JMX.mbean.unregistered,JMX.mbean.registered)
#mbean = com.amazonaws.management:type=AwsSdkMetrics
#class name = com.amazonaws.metrics.MetricAdmin
# attributes
  %0   - CredentialFile (java.lang.String, rw)
  %1   - HostMetricName (java.lang.String, rw)
  %2   - JvmMetricName (java.lang.String, rw)
  %3   - MachineMetricsExcluded (boolean, rw)
  %4   - MetricNameSpace (java.lang.String, rw)
  %5   - MetricQueueSize (java.lang.Integer, rw)
  %6   - MetricsEnabled (boolean, r)
  %7   - PerHostMetricsIncluded (boolean, rw)
  %8   - QueuePollTimeoutMilli (java.lang.Integer, rw)
  %9   - Region (java.lang.String, rw)
  %10  - RequestMetricCollector (java.lang.String, r)
  %11  - ServiceMetricCollector (java.lang.String, r)
  %12  - SingleMetricNamespace (boolean, rw)
# operations
  %0   - void disableMetrics()
  %1   - boolean enableDefaultMetrics()
(以下省略)

ベン・ホロウィッツの「HARD THINGS」を読んだ

最近、組織的な動きによって問題の解決が可能かを考える場面がたまにあるので、何かヒントになることでもないかと思い、前から読んでみたかった本書を読んだ。

HARD THINGS

HARD THINGS

良かったところを以下に述べる。

CEOの苦闘の話

前半の苦難が詰まったエピソードを読んだ後に、CEOの苦難が語られ始める。 特に下記のフレーズ。他の文書と違ってやたら詩的でぐっとくる。

苦闘とは、そもそもなぜ会社を始めたのだろうと思うこと。苦闘とは、あなたはなぜ辞めないのかと聞かれ、その答えを自分もわからないこと。苦闘とは、社員があなたはウソをついていると思い、あなたも彼らがたぶん正しいと思うこと。

苦闘とは、料理の味がわからなくなること。苦闘とは、自分自身がCEOであるべきだと思えないこと。苦闘とは、自分の能力を超えた状況だとわかっていながら、代わりが誰もいないこと。苦闘とは、全員があなたをろくでなしだと思っているのに、誰もあなたをクビにしないこと。苦闘とは、自信喪失が自己嫌悪に変わること。苦闘とは、苦しい話ばかり聞こえて、会話していても相手の声が聞こえないこと。苦闘とは、痛みが消えてほしいと思う時。苦闘とは、不幸である。苦闘とは、気晴らしのために休暇をとって、前より落ち込んでしまうこと。苦闘とは、多くの人たちに囲まれていながら孤独なこと。苦闘は無慈悲である。

苦闘とは、破られた約束と壊れた夢がいっぱいの地。苦闘とは冷汗である。苦闘とは、はらわたが煮えくり返りすぎて血を吐きそうになること。苦闘は失敗ではないが、失敗を起こさせる。特にあなたが弱っているときにはそうだ。弱っているときは必ず。

採用周りの話

今の勤めている会社は100-200人くらいの規模の会社で、そんなに小さくはないが大きくもない会社で、日々採用面接を担当している。 候補者の職歴はスタートアップから大企業から様々で、面接をするたびにこの人は果たしてこの会社でやっていけるだろうかと考えている。 本書は基本的には「幹部」の人事について言及していたが、一般社員にもある程度当てはまるように感じ、とても参考になった。

「大企業の幹部が小さな会社で成功できない理由」のセクション

大企業から小さな会社の転職の場合、

  • ペースのミスマッチ: 「大企業の有能な幹部の多くは、四半期に新しいプロジェクトが3つあったら、多すぎると言うだろう」
  • スキルのミスマッチ: 「大企業を動かしていくには、新たに組織をつくり上げるのとはまったく異なるスキルが必要だ」

のようなミスマッチがあり、そして、そのような採用の失敗を防ぐには、

  • 面接段階でどうしようもないミスマッチを察知する
  • 面接と同じくらい融和を大切にする

が大切だと書かれている。

ミスマッチの検知

下記のような質問をすると良い、と言っており参考になる。

  • 質問: 仕事に就いて最初の一ヶ月に何をしますか?
    • 勉強という答えを強調しすぎる人には要注意だ
    • 候補者が、割り込み駆動型である兆候を見逃してはならない。小さな会社では、割り込みはいつまでたってもやってこないからだ。CEOが無理だと思うくらい新規プロジェクトを持ってくる候補者を探そう。
  • 質問: 新しい仕事は、あなたの今の仕事とどう違いますか?
    • 仕事の違いを自覚しているrかどうかに注目すること
    • 過去の体験の大部分を今すぐ応用できると思い込んでいる候補者は要注意だ
  • 質問: なぜ小さな会社に入ろうと思いましたか?
    • ストックオプションが主要な動機でないかに注意せよ。ゼロの1パーセントはゼロだ。
    • 大きな会社と小さな会社でもっとも大きく違うのは、経営している時間と、創造している時間の長さだ。

入社したら積極的に溶け込ませる

  • 創造を強いる
    • 毎月、毎週、あるいは毎日でも目標を与えて、すぐに結果が出せることを確認する。
  • 「理解」をしているか確認する
    • コンテキストを持たない幹部は、スタートアップでは価値を持たない。
    • できるだけ早く、必要な情報を与えること。何も質問がなければ解雇を検討すべきだ。30日経っても状況を把握できていないと感じたら、間違いなく解雇すべきだ
  • 社員と接触させる

その他の人事の話

下記のセクションも良かった。

  • 人を正しく解雇する方法
  • 幹部を解雇する準備
  • 親友を降格させるとき
  • 友達の会社から採用してもよいか

人間の感情を認めた上で、人事判断を行う際にどう振る舞うのが良いのかを経験を元に描かれていて良い。 このような人事がどのようなダイナミズムで発生するかが面白かった。

「幹部の採用 - 未経験の仕事でも適任者を見つける」のセクション

幹部の採用のためのステップは下記の通りと書かれている。

  1. 自分が欲しいものを知る
  2. 適性を見極めるプロセスを実行する
  3. 孤独な決断を下す

人の採用を行うに際して陥りやすい罠として、「典型的な人物を探してしまう」という項目が挙げられており、最近の自分の判断を省みながら読んだ。 結成したての少数チームのメンバーの採用にとっても、大事な考え方だなと感じた。

もし私が典型的な人物を探していたら、マーク・クラニーを雇うことも、今あなたがこれを読んでいることもなかっただろう。この誤ったアプローチは、セールス責任者にプラトンイデア(真実の姿)を求めることと同じだ。まず、理想的なセールス担当幹部のイメージを描き、次に現実世界の候補者を自分のモデルと一致させようと試みる。これは良くない方法だ。第一に、あなたが雇おうとしているのは、架空の会社で働く概念上の幹部ではない。自分の会社の今この瞬間にとって、正しい人物を雇わなくてはならない。2010年のオラクルのセールストップは、1989年なら失敗していた可能性が高い。アップルのエンジニアリング担当副社長は、フォースクエアにとっては完全に誤った人選かもしれない。重要なのは詳細さと明確さだ。第二に、CEOが描く空想モデルは、ほぼ確実に間違っている。CEOがこのモデルをつくった根拠は何だろうか。最後に、そんな曖昧な基準を面接チームに教え込むことはあり得ないほど困難である。その結果、全員が何か違うものを求めることになる。

目標管理の話

「社員がマネージャーを誤解するとき」のセクション

このセクションでは、数字を重視するあまり間違ったインセンティブを与えてしまい失敗していた事例が書かれていた。

促進する対象には、定量化できるものとできないものがある。定量的な目標についてばかり報告して、定性的な目標を無視していけば、定性的な目標は達成できない-たとえそれがもっと重要な目標であったとしても。純粋に数字だけによるマネジメントは、筋通りに色を塗るぬり絵キットのようなもの-あれはアマチュア専用だ。

と言っている。これは、最近のトレンドの一部の OKR による目標管理に反対になる意見のように見える。 (参考: https://satoshihirose.hateblo.jp/entry/2019/01/05/153343

文章中には見落とされがちな定性的な目標として

  • 対ライバルの勝率は、上がっていたか下がっていたか?
  • 顧客満足度は、上がっていたか下がっていたか?
  • わが社のエンジニアたちは、この製品をどう思っていたのか?

を例としてあげられていたが、社員や顧客に対するサーベイによりこれらの項目を定量化することはOKRの中では普通に行われているように思う。

カルチャーの話

罵倒語

著者のベン・ホロウィッツは罵倒語を良く使うらしく、社内でそれを使うことの是非についても書かれてあった。 部下からその点を問題提起され、ベンは下記のように整理したらしい

  • テクノロジー企業では罵倒語を不快に感じる社員もいるが、不快に感じない社員もいる。
  • もし罵り言葉を禁止したら、罵り言葉を使っている社員は「この会社は古臭い」と思い、辞めてしまうかもしれない。
  • 罵り言葉を続けると、それを不快に感じる社員の一部は辞めてしまうかもしれない。
  • なんといっても私が最大の違反者なのだから、私の判断に偏りが入り込むのは避けられない

この罵倒語を使っている会社はクールである、といった風潮を感じたことはないが、当時のアメリカではそのような風潮はあったのだろうか(今も?)(もしくは、エグゼクティブの界隈ではあったのかもしれない)。 その問題提起に対して、ベンは社員を集めて下記のように言い渡したらしい。印象的な場面なので長いけれど引用する。

「当社における罵倒語の頻繁な使用が一部の社員を不快にしているという問題があると知った。罵倒語の使用頻度のナンバーワンとして、私自身の振る舞いを反省したが、同時に当社全体の問題としても考えてみた。私の考えではふたつの選択肢がある。(a)罵倒語の使用を禁じる、(b)罵倒語の使用を受け入れる-だ。このふたつの中間の選択肢というのは、理論的にはともかく現実的ではない。『罵倒語の使用を最小限に留める』などという対策は実効性がない。前にもの述べたことだが、世界で最優秀の人材を惹きつけられなければ、われわれは勝利できない。テクノロジー業界ではほとんど全員が罵倒語の使用を受け入れている。

そこで、罵倒語の使用を禁じることで生じる人材の損失は、受け入れることによって生じる損失を上回ると予想される。以上の理由で当社は、罵倒語の使用を受け入れる。ただし、いかなる場合でも、セクシャル・ハラスメント、その他の不当な目的で罵倒語を使ってはならない。罵倒語を使うかどうかにかかわらず、かかる行為が許されないのはもちろんだ。(略)」

その日以後、罵倒語に対する苦情は後を断った。それにこの方針によって会社を辞めた人間も出なかったようだ。ここで強調したいのは、組織で問題が生じたときに、必ずしも解決策は必要なく、単に事柄を明確化するだけで良い場合もあるという点だ。脅迫やセクハラにならない限り罵倒語の使用はオーケーだということをはっきりさせただけで、問題は消滅した。ともかく、この方針の結果、職場環境の快適さは維持され、辞職率は低いままにとどまり、苦情はなくなった。つまり、この方針は正しかったということになる。

最近の心理的安全をケアする風潮からするとにわかには信じられない言説だ。 (参考: 「暴言を吐かれた人は処理能力が61%、創造性58%落ちる」暴言が#職場 にもたらす悪影響 https://togetter.com/li/1201526

もしくは、単に私がスタートアップもしくは「戦時の企業」に向いていないだけなのかもしれない。 少なくとも個人的には頻繁に罵倒語やリスペクトに欠ける発言を頻繁に目にするような状況は、退職を検討するひとつの理由になるのではと感じている。

「優秀な人材が最悪の社員になる場合」のセクション

企業が大きくなるにつれて、円滑なコミュニケーションはどんどん難しくなっていく。 それにつれて優秀な人材が悩みのタネとなるという話も興味深かった。

ある種の人々はコミュニケーションのスタイルがあまりに好戦的なので、誰も口を利きたがらなくなる。誰かがマーケティングの話題に触れるたびにマーケティング担当副社長が飛び出してきて怒鳴りまくるようだったら、誰もマーケティングの話をしなくなる。会議にこういう根性曲がりがいると誰も口を開こうとしなくなる。その結果、会社幹部の間での意思疎通に大きな障害が生じ、やがて会社全体にその悪影響が出る。念のために付け加えておくが、問題の社員が一方で極め付けに頭がいい場合のみ、こうした破壊的な影響が広がる。そうでなければ、その社員が誰を攻撃しようと気にする者はない。ケガが大きくなるのは大きな犬に噛まれたときだ。もし幹部スタッフに大きな犬がいる場合は、その犬をどこかに追い払うしかない。

偉大なフットボール・コーチも同様のことに言及していると引用し、その影響をコントロールすることを肝に銘じるよう締めている。

「ひとりがバスに遅れれば、チーム全員が待っていなければならない。うんと遅れれば、チームは試合に間に合わなくなってしまう。だからそんなヤツは許しておくわけにはいかない。バスには出発しなければならない時刻がある。そうは言っても、時にはあまり役に立つので、バスを遅らせるのもやむを得ないような選手もいる。しかしそれはよほどの選手に限る」 (略) 時として会社にはデニス・ロッドマンのようにあまりに貢献が巨大なので、何をしても許容せざるを得ないような社員が存在することがある。だがその場合、CEOはその社員の悪影響が会社のほかの社員にひろがらないように自ら措置を講じなければならない。「社会全体でもデニス・ロッドマンはめったにいない」ことを肝に銘じておくべきだ。

1on1の話

1on1とは書いていなかったが、社員との個人面談の大事さについても言及している。 社員の方が個人面談の主役だと前置きしつつ、意見を引き出すのに役に立つ質問の例として下記のものをあげている。

  • われわれがやり方を改善するとしたらどんな点をどうすればよいと思う?
  • われわれの組織で最大の問題は何だとおもう?またその理由は?
  • この職場で働く上で一番不愉快な点は?
  • この会社で一番頑張って貢献しているのは誰だと思う?誰を一番尊敬する?
  • きみが私だとしたら、どんな改革をしたい?
  • われわれの製品で一番気に入らない点は?
  • われわれがチャンスを逃しているとしたら、それはどんな点だろう?
  • われわれが本来やっていなければならないのに、やっていないのはどんなことだろう?
  • この会社で働くのは楽しい?

まとめ

紹介しなかったどの章も興味深く、組織で働く上で折に触れて読み返したいと思えるような本だった。

クリスティーナ・ウォドキー「OKR」とジョン・ドーア「Measure What Matters」を読んだ

What's this?

去年の8月に現職に就いた際に、組織目標をOKRで管理していることを知りました。 OKRについてのインターネット上の情報などを調べていくうちに、「シンプルかつ具体的で少数の重要な目標に絞る」「野心的な目標を挙げることで成果をストレッチさせる」などのコンセプトが気に入り、その詳細な思想や運用について興味が湧きました。 そこで、クリスティーナ・ウォドキー「OKR」とジョン・ドーアMeasure What Matters」の二冊を読んだので、学んだ点をまとめます。

OKRそのものの概要は以下の記事などを参照してください。

OKRと過去のMBOとの違い

1960年代にはドラッカーが「目標による管理(MBO)」を提唱し各社が導入をしていたそうですが、下記の理由などにより徐々に形骸化していってしまったそうです。

  • 目標の中央集権化による、伝達の遅延
  • 頻繁に更新しないため、状況の停滞
  • 給与や賞与と連動させたことによって、社員がリスクを取らなくなってしまった

これらの問題を受けてOKRは、

  • ボトムアップによる目標管理の推奨
  • 四半期ごとの更新、定期的な見直し
  • 給与や賞与と連動させない

などを基本姿勢としています。

品質保証について

数値目標と品質目標

野心的な目標は得てして品質を犠牲にするので、ジョン・ドーアは数値目標と品質目標を対にする運用も紹介しています。 例えば、新機能を3つリリースするという数値目標に対して、品質保証テストで各機能あたりのバグは5つ以下にする品質目標を対にするなどの例を挙げています。

健康・健全性指標

似たような問題意識があるからか、クリスティーナ・ウォドキーは健康・健全性指標を紹介しています。 ObjectivesとKey Resultsとは別に、失ってはいけないこと、守らなくてはいけないことを、状況に応じて赤色・黄色・青色など適宜評価し続ける方法です。 顧客との関係、コードの安定性、チームの健康などを例に挙げています。

勤務評定と目標管理の関係

OKRの達成度により勤務評定を下すことは、アンチパターンであることをジョン・ドーアは繰り返し述べています。 年に一度の勤務評定の代わりに、下記の継続的パフォーマンス管理(CFR)の導入を推奨しています。

  • 対話(Conversation): パフォーマンス向上を目的に、マネージャとコントリビュータのあいだで行われる真摯で深みのある意見交換。
  • フィードバック(Feedback): プロセスを評価し、将来の改善につなげるための、同僚との双方向あるいはネットワーク型のコミュニケーション
  • 承認(Recognition): 大小さまざまな貢献に対して、しかるべき個人に感謝を伝える。

一方で、勤務評定を目標管理から完全に切り離すべきとまでは主張しておらず、OKRで数字で進捗を測れることの意義についても述べています。

ただし、勤務評定と目標管理は完全に切り離せるとか、切り離すべきだと言っているわけではない。 個人が「何を達成したか」をデータでわかりやすく示すことには、勤務評定の歪みを防ぐ効果もある。 しかもOKRは個人が取り組んでいる最も価値のある仕事を示しているので、次の評価サイクルで信頼性のあるフィードバックをするための材料となる。 ただ、目標が報酬を決めるベースとなったり、悪用されたりすると、従業員は実力を隠すようになる。 守りに入り、驚異的成果を目指して限界に挑戦しなくなる。 そして仕事のやりがいのなさにうんざりする。最も割りを食うのは組織だ。

進捗と評点の違い

通常OKRは目標達成度合いに応じて0.0-1.0の評点が付きますが、ジョン・ドーアは、進捗と評点は厳密に一致する必要はない旨を記しています。

たとえばチームの目標が新規顧客の獲得で、あなた個人の「主要な結果」が50本の電話をかけることだったとする。 結局あなたが見込み客にかけた電話は35本で、そのまま採点すると70%になる。 これは成功だろうか、失敗だろうか。 データだけではよくわからない。 だがかけた電話のうち、10本以上がそれぞれ数時間続き、結局8件の新規顧客を獲得できたら、評価は最高の1.0としてもいいかもしれない。 反対に電話をかけるのをグズグズ先延ばしし、期限間際に35本の電話をかけ、新規顧客は1件しか獲得できなかったなら、パフォーマンスの自己評価は0.25となるかもしれない。 もっと努力できたはずなのだから。

その上で、行動の振り返りが大事であると主張しています。

OKRは本来、行動を重視する。しかしやみくもに行動するだけでは、回し車のハムスターと変わらず、単なる苦行になってしまう。 充足感を得るためにカギとなるのは、野心的な目標を立て、そのほとんどを達成し、足を止めてそれを振り返り、そのうえで新たなサイクルを繰り返すことだ。

コミットする目標と野心的な目標

「Measure What Matters」では巻末にGoogleでのOKRの具体的な運用方法についての記載があり、参考になります。 例えば、GoogleではOKR上で、コミットする目標と野心的な目標を分けて管理しているそうです。 内容としては、

  • コミットする目標は、プロダクトのリリース、経営指標などと結びついている期限内に100%達成する必要がある目標
  • 野心的な目標は、壮大なビジョン、高いリスク、未来志向の発想を反映した目標

という感じで分けて運用しているようです。 それらの区別をつけないことで、コミットすべき目標の未達率が高まる、チームが守りに入るなどの弊害が出ると述べています。

良いOKRのためのリトマステスト

こちらもGoogleの運用例中の記載があった内容ですが、良いOKRを判定するための自問すべき項目が挙げられています。 具体的には下記のような内容です。 全ての項目をクリアする必要はないとは思いますが、OKRを設定する際の参考になります。

  • そのOKRは合理的に考えて、1.0の評定を得ても、エンドユーザーへの直接的価値あるいは経済的恩恵をもたらさない可能性があるか
  • すべてのKRで1.0の評定を得ても、目標の意図が達成されない可能性があるか
  • そのOKRを書くのに5分もかからなかったら、おそらく良いものではない
  • 目標が一行に収まっていないなら、十分簡潔とは言えない
  • KRにチーム内でしか通じない用語が含まれていたら、おそらく良いものではない
  • 具体的日付を使う。すべてのKRの期日が四半期の最終日となっているのは、まともな計画がない証拠だろう
  • 「主要な結果」は必ず測定可能なものにする
  • 指標に曖昧さがないこと
  • OKRに含まれていないが、チームにとって重要な活動(あるいは活動の一部)があれば、OKRを追加する
  • 規模が大きい組織ではOKRを階層式にする

豊富な事例紹介

「OKR」の前半は、架空のスタートアップがOKRを活用したおかげで成功する物語、「Measure What Matters」はインタビュー形式でOKRを導入している各社の活用事例が載っていて、その運用が具体的でわかりやすく紹介されています。

特に、「Measure What Matters」の中で、OKRの父アンディ・グローブインテルにて、8086がモトローラの68000にいかにして対応したかの章が、具体的で臨場感があり面白かったです。 その他にも、

  • ビル&メリンダ・ゲイツ財団のケーススタディ(インタビュー中に具体的に挙げられたOKRは基本原則に即していないように見えますが...)
  • スンダー・ピチャイのインタービューによる、グーグルクロームチームのケーススタディ
  • ユーチューブCEOスーザン・ウォジスキのインタビューによる、ユーチューブのケーススタディ
  • U2のボノのインタービューによる、途上国援助活動ONEのケーススタディ

などが興味深かったです。

個人での運用するとしたら

OKRは組織的に導入することを目的として設計されていますが、個人で使用しても一定の効果はあるでしょう。 会社の評価と関係のない自分個人の四半期目標を用意すると下記のような感じになるでしょうか。

Objectives:

  • エンジニアとして国際的に活躍する、またはそのための下地を作る

Key Results:

  • 週5で30分のオンライン英会話レッスンを受ける
  • TOEFLで100点を取得する
  • international conference で業務で行った活動についての発表を1回する
  • 使用しているOSSにPull Requestを4回投げる

その他

気分を盛り上げる言葉たち

  • 「目的地がどこであるか知らなければ、そこにたどりつくことはできないだろう」- ヨギ・ベラ
  • 「アイデアを思いつくのは簡単。実行がすべてだ」- ジョン・ドーア
  • 「人の真価は、どのような能力があるかより、どのような選択をするかでわかる」 - J・K・ローリング
  • 「最善を善の敵にしてはならない」 - ヴォルテール

アンディ・グローブスタンフォードでの講義動画

(翻訳) データエンジニアリングビギナーズガイド 最終部

訳者まえがき

原著者の Robert Chang の許可を得て以下の記事を翻訳・公開しました。

medium.com

第一部と第二部の翻訳はこちら。

satoshihirose.hateblo.jp

satoshihirose.hateblo.jp

以下から翻訳内容です。

データエンジニアリングビギナーズガイド 最終部 ETLパイプラインからデータエンジニアリングフレームワーク

https://cdn-images-1.medium.com/max/2000/1*JsZuJ2aUFgNsZk9cTv_61w.png イメージクレジット:よく設計されたデータエンジニアリングフレームワークは、多くの扉や新しい可能性を開くことができます :)

ついにフィナーレ

このシリーズの第一部では、ビジネスインテリジェンス、実験、機械学習など、分析を進める上で、堅牢なデータ基盤が重要な前提条件であることを学びました。第二部では、Airflowの詳細を深く掘り下げ、データモデリング、スタースキーマ、正規化などの技術について説明しました。さらに、データパイプラインの例を取り上げ、ETLのベストプラクティスについて学びました。これらはすべて、際立ったデータサイエンティストになるために学ぶ重要なスキルです。

通常、企業がデータ分析の階層を上って小規模なチームを拡張するにつれ、複雑さと開発コストは増加する場合が多いです。 Airbnbでは、100人以上のコントリビューターがAirflowパイプラインの作成に関っています。これにより、ETLのベストプラクティスの実施、データ品質の維持、ワークフローの標準化がますます困難になっています。幸いなことに、複雑さに対する特効薬の一つには、抽象化の力があります。もちろん、この原則はデータエンジニアリングに関しても例外ではありません。

データエンジニアリングにおいては、抽象化はユーザーのワークフローでよく見られるETLパターンの見極めと自動化を意味する場合が多いです。この最後の記事では、データエンジニアリングフレームワークの概念を定義します。このようなフレームワークを構築するための典型的なデザインパターンを分析し、最後にAirbnbで頻繁に使用する具体例をいくつか紹介します。この最後の記事の終わったところで、読者が抽象化の力を活用して独自のフレームワークを構築できるようになることを願っています。

一般的なシナリオ

あなたの会社が今、製品のそれぞれの部分を担当する複数のチームを持つ十分な大きさであるとします。各チームには、独自のOKR、製品ロードマップ、KPIがあります。あなたはチーム専属のデータサイエンティストとして、ビジネスのしくみを追跡するための分析ダッシュボードを作成する責任があります。袖をまくって働きます。

データモデリングスキーマ設計から始めます。関連するファクトテーブルとディメンションテーブルを識別して、さまざまな有効な次元削減によって構成された意味のあるメトリックを計算します。テーブルをJOINして最終的な非正規化テーブルを作成し、最後にすべての履歴データを埋め戻します。この作業はインパクトがありますが、いくつかのダッシュボードで作業した後は、ETLワークフローは反復的になってきました。事実、あなたが会社で話す多くのデータサイエンティストは、それぞれのチームで似たワークフローを使用してダッシュボードを作成しています。

https://cdn-images-1.medium.com/max/1600/1*_iE8D0TtXzrb-8S1NMNv_A.png 出典:多くのETLに関する辛い仕事は、このような単純なダッシュボードに電力を供給するために必要なものです(Supersetからの参照)

驚くことではありませんが、製品の立ち上げの際には詳細に渡って実験をします。ユーザーレベルのメトリックを集計するために、ファクトテーブルを使用して実験に割り当てられたテーブルを慎重にJOINします。実験のp値と信頼区間を計算するために、異なる対象群からのテスト統計を計算します。いくつかの他のデータサイエンティストと話をして、これはデータサイエンティストに共通のもう一つのワークフローであることに即座に気付きました。実際、サイエンティストが日常的に行っていることの多くは、別個ではあるが一般的なワークフローに統一できるように感じられます。

これらのワークフローを(少なくとも部分的に)自動化することは可能か?と思われるでしょう。もちろん、大声で Yes と答えられます!

パイプラインからフレームワーク

https://cdn-images-1.medium.com/max/2000/1*6D46R9q8wc2gwa1K5rvCHQ.png イメージクレジット:ETLパイプラインからETLフレームワーク

第二部で既に学んだように、Airflow DAGは恣意的に複雑にすることができます。場合によっては、データの計算は制御フローのようなロジックに従うこともあります。たとえば、特定の条件付きチェックの後にデータフローを分岐する場合は、BranchPythonOperatorを使います。条件が満たされた場合にのみワークフローを続行したい場合は、ShortCircuitOpeartorを使用できます。これらの演算子は、Configuration as Codeの原則と組み合わされて、Airflow ETLを多目的かつ柔軟にするものです。しかし、Airflowはさらに多くのことをすることができます。

これまでの議論は、単一の独立したパイプラインの設計に限られていましたが、パイプライン生成にも同じ原則を適用することができます。プログラムで動的にDAGを生成する方法です。これは、基本的にデータエンジニアリングフレームワークが行うことです。それはデータワークフローを自動化するAirflow DAGに対する異なるインスタンス化の方法です。 Airflowの原著者であるMaximeがこの記事について説明しています

コードからワークフローを動的に構築する...ひとつの非常に簡単な例は、テーブル名のリストを持つYAML設定ファイルを読み込み、各テーブルの小さなワークフローを作成し、ターゲットデータベースにテーブルをロードしたり、サンプリング、データ保存などの設定ファイルのルールを適用したり、匿名化したりする Airflowスクリプトです。この抽象化により、多くの作業をせずに新しいワークフローを作成できます。この種のアプローチには、多くのユースケースがあることがわかります。

これらのツールは、重要です。なぜなら、データサイエンティストがデータバリューチェーンをはるかに迅速に進めることができるからです。

  • 実験報告フレームワークがユーザーレベルのメトリックを自動生成し、実験の統計を計算すると、データサイエンティストは、主要なメトリックの変化分析、ユーザーの行動解釈、製品変更の影響をハイライトするために多くの時間を費やすことができることを想像してください。

  • 同様に、メトリックフレームワークが自動的にOLAPテーブルを自動的に生成する場合、データサイエンティストは傾向の把握、ギャップの特定、およびビジネス変更にともなう製品変更の橋渡しに多くの時間を費やすことができます。

  • もう一つの例は、オフラインバッチMLモデルの本番化に必要なエンジニアリング作業を抽象化するフレームワークです。データサイエンティストは、パッケージの依存関係、仮想環境の設定、またはデプロイについて心配する必要がなくなりました。代わりにモデリングに時間を費やすことができます。

  • これらのフレームワークの意味は、データサイエンティストの働き方を大幅に改善するため、意義深いものです。これらは、データサイエンティストが大規模に価値を提供することを可能にする技術です。

データエンジニアリングフレームワークの設計パターン

有用ではあるにしろ、DEフレームワークは何もない所からは生まれません。私はAirbnbの最初のデータエンジニアに、どのようにして皆にとって非常に多くの有用なフレームワークを作り出すことができたかを尋ねました。彼の反応は次のようなものでした。「実際には魔法はありません。十分な時間を過ごすせば、自動化できるパターンが見え始めます」ワークフローとして自分の作業を見ると、新たな可能性が生じます。

どのワークフローを自動化するか考えているとき、フレームワーク設計者はエンドユーザーの体験について考える必要があります。よく設計されたデータエンジニアリングフレームワークには一般に、入力レイヤ、データ処理レイヤ、出力レイヤの3つの層があります。

https://cdn-images-1.medium.com/max/1600/1*iDfX-IBcwGu8R0PbiXw-Bg.png 出典:ミートアップでのMaxのトーク、「Apache Airflowを使用した高度なデータエンジニアリングパターン」

  • 入力:エンドユーザーがDAGの設定方法を指定する場所です。ここではユーザー体験は重要です。通常、入力は静的な設定ファイル(YAMLやHOCONなど)でも、Web UIのようなものでもかまいません。ここでの目標は、ユーザーのニーズを把握することです。

  • データ処理:これは、ETLパイプラインが動的にインスタンス化されるデータエンジニアリングフレームワークの中核です。これを実現するコードは一般にDAGファクトリと呼ばれ、工場のようにDAGが一度に一つずつ作成されるという概念を酔狂で取り込んでいます。

  • 出力:前のステップで生成されたDAGによって派生データが作成され、それは出力はしばしば下流のHiveテーブルに保存されて、適切に設計されたUI /ビジュアライゼーションレイヤーに表示されるか、下流のパイプラインまたはフレームワークで消費されます。

これはすべて抽象的に聞こえるかもしれません。したがって、今後のいくつかのセクションでは、Airbnbでこれをより具体的にするために活用する具体的な例をハイライトします。以下のセクションを読むときは、どのワークフローを各フレームワークが自動化しようとしているか、フレームワークの入出力レイヤに注意を払うことに注意してください。

1. 逐次計算フレームワーク

データサイエンティストが、最初のイベントまたは最後のイベント以降に累積した合計や時間などの計算集約型のメトリックを計算することは、とても一般的です。たとえば、新製品を使用したユーザーの総数を報告したり、ユーザーが最後に復帰してからの日数のヒストグラムを計算したいと思うでしょう。素朴なアプローチは、ファクトテーブルにクエリし、これらの望ましいメトリックを計算するために、すべての日付パーティションにわたって合計最大、または最小値をとることです。しかし、このクエリ方法は実は非効率的です。

なぜでしょう?この方法は、必要な計算がファクトテーブル全体をスキャンするため、逐次的データ読み込みのETL原則に違反します。理想的には、これらのメトリックを事前計算するためにサマリーテーブルを作成し、エンドユーザーはサマリーテーブルの単一または最新の日付パーティション内のメトリックを参照するだけです。このパターンは、データエンジニアが逐次計算フレームワークと呼ばれるフレームワークを構築するのに、非常に一般的です。

  • 入力:HOCON構成ファイル。ここでは、ユーザーが事前計算するメトリックまたはイベント、グループ化する主キー、サマリーテーブルを作成するためにクエリするファクトテーブルを指定します。

  • データ処理:サマリーテーブルを逐次的に作成するAirflowスクリプト。すなわち、当日のファクトテーブルと前の日付パーティションのサマリーテーブルを結合して、コストの大きな指標を更新します。cumsum_metric_today = f(cumsum_metric_yesterday、metric_today)(fは合計、最小/最大、または任意の他の集約関数を含む)

  • 出力:サマリーテーブルの唯一つの日付パーティションから累積合計、最初/最後のイベント以降の日数またはその他のコストの大きなメトリックをクエリできる最適化されたサマリーテーブル。

https://cdn-images-1.medium.com/max/1600/1*MrZeunAzDfm2eHsyZXk0EA.png 参考:インクリメンタル計算フレームワークの図

このフレームワークはどのようなワークフローを自動化するでしょう?これは、ユーザーが非効率的なクエリパターンを回避し、使用しなければならないわずらわしい集約を一度に一つの日付パーティションへと自動化します。

2. バックフィルフレームワーク

第二部ですでに説明したように、バックフィルは、重要ですがどんなデータエンジニアリングの作業のなかでも時間のかかるステップです。 ETLパイプラインが構築されたら、履歴を再構築するために、以前のデータを遡及的に確認する必要があります。私たちが議論したバックフィル戦略の中には、ダイナミックパーティションJinjaテンプレートを使用したSQLへのバックフィルロジックの焼き付けがあります。しかし、これらの技術を用いても、バックフィルは依然として面倒なことがあります。あるエンジニアはかつて私に「バックフィルとう単語はもう聞きたくない」と言いました。この言葉は、退屈なバックフィルがどれほど煩わしいかを教えてくれます。

たとえば、数年分のデータをバックフィルする必要がある場合、そのようなタスクを中断して小規模なバックフィルに並列化する方がはるかに効率的です。しかし、これらの長時間実行される並列プロセスを管理することは、かなり面倒なことになります。また、バックフィルされたデータを本番テーブルに挿入する前に、サニティチェックを実行する必要があることがよくあります。バックフィルが一般的だがあまりにも頻繁で不愉快な経験であることを思い、このワークフローを自動化するバックフィルフレームワークを構築しました。

  • 入力:ユーザーがジョブ名、バックフィルジョブの start_dateend_date 、バックフィルを並列化するプロセスの数、各プロセスがバックフィルする日数を指定できる簡単なUI。

  • データ処理:バックフィル作業の実行方法をユーザーが指定すると、フレームワークはAirflowパイプラインを作成し、バックフィルタスクを自動的に並列化し、サニティチェックを行い、ステージングテーブルを本番テーブルと交換します。

  • 出力:完全にバックフィルされ使用する準備が整ったテーブル。

https://cdn-images-1.medium.com/max/1600/1*eR0I__mi5VXU5_BkbFyL6g.png 参照:バックフィルフレームワークの図

このフレームワークはどのようなワークフローを自動化するでしょう?これは、人々が自身のマシンで実行しなければならなかったアドホックなバックフィルスクリプトの多くを自動化します。自動的な比較を用意しておくことで、品質保証を自動化します。最後に、QAテスト後に、ステージングテーブルを本番テーブルと交換します。

3. グローバルメトリクスフレームワーク

最近まで、Airbnbのデータサイエンティストは、分析用のETLとダッシュボードを構築するのにかなりの時間を費やしました。前に説明したように、正しいデータソースを特定し、メトリックとディメンションを定義し、最終的な非正規化テーブルを作成するには多くの作業が必要です。異なるチームは異なるパフォーマンス指標を持つことがあります。その結果、異なるファクトテーブルが存在することもありますが、ビジネスの重要なディメンションは通常とても一貫性があり、ゆっくりと変化しています。

例えば、市場のホスト側に関する仕事をするデータサイエンティストは、通常、一覧の需要、種類、または容量などの次元削減を気にします。同様に、ゲスト側の仕事をするデータサイエンティストは、ゲストの段階、起点需要、または目的地需要などの次元を気にします。この洞察を受けて、多くのETLパイプラインで実際に多数のファクトテーブルの結合がより小さいディメンションテーブルのセットに関係していたことが明らかになりました。これが、グローバルメトリクスフレームワークの作成を動機づけた理由です。

  • 入力:小さなファクトテーブルの一つ以上のメトリック、最終テーブルに含めることを望むディメンションの集合、結合に使用される主キーと外部キー、およびテーブルの作成を追跡するためのその他の有用な情報を指定するHOCON設定ファイル

  • データ処理フレームワークは、集計およびカットするために必要なメトリックとディメンション区分を識別し、ディメンションテーブルをファクトテーブルとJOINして自動的に非正規化表を作成します。

  • 出力:同じメトリックセットを持ち、おそらく異なるディメンションセットを持つ一つ以上のHiveテーブルが作成されます。これは、一つ以上の非正規化テーブルを即座に作成できることを意味し、これらのデータソースは、Supersetでの可視化でき、Druidでさらに活用できます。

出典AirBnbのメトリクスフレームワークトーク、DataEngConf18 でのLauren Chircus氏

このフレームワークはどのようなワークフローを自動化するでしょう?これは、後でダッシュボードや分析などで使用する非正規化テーブルの作成に必要なよくあるデータエンジニアリング作業を自動化します。このプロジェクトのプロダクトマネージャーはそれを「非正規化マシン」と呼びました。

4. 実験報告フレームワーク

データ駆動型テクノロジー企業の多くは独自の組織内実験プラットフォームを構築しており、Airbnbも例外ではありません。私がTwitterで同様のチームに取り組んでいたことを考えると、実験パイプラインの複雑なETLがどのくらい複雑なのかを理解することができます。多くの場合、それぞれ数千のタスクで構成されたいくつものモジュール式のDAGが伴います。

その複雑さにもかかわらず、私たちの実験報告フレームワークは、実際には上で述べたものと同じデザインパターンに従います。唯一の違いは、各レイヤーがこれまで述べたすべての例で説明したものよりはるかに複雑であることです。しかし、このような投資は、企業内の製品チームが何百人何千人ものデータサイエンティストを雇うことなく、数百または数千の実験を並行して実行できるため、しばしば価値があり必要となります。

  • 入力:単純な設定ファイルや単純なUIの代わりに、成熟したUIが組み込まれているため、ユーザーは実行するテストのタイプ、追跡するターゲットまたは二次的指標、実験バケットとその相対サイズなどを指定できます。このステップでは、実験データの開始と計算に関係するものがすべて取得されます。

  • データ処理:各テストごとに、サブジェクトレベルのメトリックとそれに対応するディメンションを計算するメトリクスパイプライン。これらのメトリックとディメンションの完全な組み合わせは、計算を非常に複雑にするものです。実際、実験パイプラインは企業で最も複雑なETLジョブであることがよくあります。

  • 出力:単純な出力テーブルの代わりに、このステップに関連する多くの下流処理があります。例えば、p値、信頼区間、有意性、最小検出可能な効果などの統計がここで計算されます。報告フレームワークの成熟度に応じて、ユーザーはメトリックのキャッピングや分散の削減を行うことができます。各ステップでは、最後のUIで処理される前に個別の計算が必要です。

https://cdn-images-1.medium.com/max/1600/1*1bJye64wHKTX81-AD2DBEA.png 参照:実験報告フレームワークの図

このフレームワークはどのようなワークフローを自動化するでしょう?データサイエンティストが実行しなければならないはずだった数百、数千回の実験における詳細な検証。

結論

https://cdn-images-1.medium.com/max/2000/1*PP9ZX6IoizG1neT1hcRbEQ.png イメージクレジット:私たちは最終的にデータエンジニアリングトンネルの終わりまできました:)

今までの議論で、DEフレームワークによる抽象化の力を理解していただければ幸いです。これらのフレームワークは、データサイエンティストの作業とワークフローに対する驚異的な増幅器です。日々の仕事のどれを抽象化することができるかを学ぶと、本当に驚くものでした。分析がレイヤー状に構築されているという哲学を強く信じるように、私はこれらのフレームワークが最初に置かれるべき基礎部分として見ています。これらのフレームワークますます共有、議論されるにつれ、今後数年間で私たちの仕事がどのようになるかワクワクします。

これでこのシリーズも終わりです:あなたがこれまでに得たことがあるとすれば、あなたがデータエンジニアリングの基礎を学んだことをお祝いします。もっと重要なことに、読んでいただきありがとうございます。何度も言及したように、これらのアイディアのどれも私のものではありませんでしたが、今日の素晴らしいデータエンジニアリング人材からこれらの概念を学ぶのは本当に幸運なことでした。データエンジニアリングは非常に重要だがしばしば評価の低い分野、であることを考えると、私はそのために主張することで少しは役に立ったかと思います!このシリーズに興味を持ち、データエンジニアリング、特にAirflowについて学びたい場合は、これらのリソースから始めることをお勧めします。

学習を続けて、幸せなデータエンジニアリングをしていきましょう!

このシリーズのフィードバックをいただいた友人Jason Goodmanにもう一度感謝します。この記事の第三者の商標は、それぞれの所有者に帰属します。 Max、Arthur、Aaron、Michael、Laurenに感謝します。彼らはデータエンジニアリングに関連するすべてのことを(直接的または間接的に)教えてくれました。

(翻訳) データエンジニアリングビギナーズガイド 第二部

訳者まえがき

原著者の Robert Chang の許可を得て以下の記事を翻訳・公開しました。

medium.com

第一部の翻訳はこちら。

satoshihirose.hateblo.jp

以下から翻訳内容です。

データエンジニアリングビギナーズガイド 第二部 データモデリング、データパーティショニング、Airflow、ETLのベストプラクティス

https://cdn-images-1.medium.com/max/2000/1*BC9-kpfjCPtY-w-GOjPBDA.png イメージクレジットマドリード(CortesíadeIñaquiCarnicero Arquitectura)のHangar 16で改装された現代の倉庫

復習

データエンジニアリングビギナーズガイド 第一部では、組織の分析能力はレイヤー状に構築されることを説明しました。そして、生データの収集とデータウェアハウスの構築から機械学習の適用まで、これらの分野すべてでデータエンジニアリングが重要な役割を果たす理由を知りました。

データエンジニアの最も強く求められるスキルのひとつに、データウェアハウスの設計、構築、および保守の能力があります。そこで、データウェアハウスとは何かを明確にし、ETLという名前の由来となる抽出、変換、およびロードの3つの一般的な要素について説明しました。

ETLプロセスに馴染みがない人向けにLinkedIn、PinterestSpotifyなどの企業によって作られたオープンソースフレームワークをいくつか紹介し、AirbnbオープンソースツールのAirflowを紹介しました。最後に、データサイエンティストがSQLベースのETLパラダイムを使用することで、データエンジニアリングをはるかに効果的に学べると述べました。

第二部概要

第一部の議論はやや抽象的でした。第二部(この記事)では、優れたデータパイプラインを構築する方法に関する技術的な詳細を紹介し、ETLのベストプラクティスを見ていきます。議論では主にPython、Airflow、SQLを使用します。

まず、ビジネスメトリックとディメンションを捉えるためのテーブルスキーマとデータの関連性を注意深く定義する設計プロセスであるデータモデリングの概念を紹介します。そして、より効率的なクエリとデータのバックフィルを可能にする方法であるデータパーティショニングについて学習します。この章を終えれば、読者はデータウェアハウスとパイプライン設計の基礎を理解するでしょう。

その後の章では、Airflowジョブの詳細な構造を解説します。読者は、抽出、変換、およびロードの概念を操作するために、センサー、オペレーター、およびトランスファーをどのように使用するかを学習します。 Airbnb、Stitch Fix、Zymergenなどの実例をもとに、ETLのベストプラクティスを紹介します。

この記事の最後で、読者はAirflowの多才さとConfiguration as Codeの概念を理解するでしょう。実際、Airflowにはこれらのベストプラクティスの多くが既に組み込まれていることがわかります。

データモデリング

https://cdn-images-1.medium.com/max/2000/1*dJApyGzVLr99OAUBh1DcPw.png イメージクレジット:スタースキーマを正しく使用すると、現実の空と同じくらい美しくなります。

あるユーザーがMediumのようなサービスを使うと、その自身のアバター、保存された投稿、閲覧回数などの情報はすべてシステムに保存されます。これらの情報を正確かつ時間通りにユーザに提供するためには、オンライントランザクション処理(OLTP)のための本番データベースを最適化することが重要です。

オンライン分析処理システム(略してOLAP)を構築する場合は、目的はかなり異なります。設計者は洞察を生むことに対して焦点を当てる必要があります。つまり、分析推論を簡単にクエリに変換し、統計を効率的に計算しなければいけません。この分析ファーストなアプローチには、データモデリングと呼ばれる設計プロセスが多くの場合に必要となります。

データモデリング、正規化、およびスタースキーマ

設計上の意思決定の例を示すために、どのテーブルを正規化するかその範囲を決定する必要性がよく発生します。一般に、正規化されたテーブルは、スキーマが単純で、標準化されたデータが多く、冗長性が少ないものです。しかし、小さなテーブルが増えると、データの関連性を追跡するには努力する必要が増え、クエリパターンはより複雑になり(JOINが増えます)、メンテナンスするETLパイプラインが増えます。

一方で、すべてのメトリックとディメンションがすでに事前結合されているような非正規化されたテーブル(別名ワイドテーブル)に問合せを行う方がはるかに簡単です。ただし、サイズが大きい場合は、ワイドテーブルのデータ処理は遅くなり、より上流の依存関係が必要となります。それに伴い、作業単位がモジュール化されず、ETLパイプラインのメンテナンスがより困難になります。

このバランスを取ろうとする数多くのデザインパターンの中で、最も一般的に使用されるパターンの一つであり、Airbnbで使用されるパターンにスタースキーマと呼ばれているものがあります。スタースキーマで編成されたテーブルは星のような型に視覚化することができるため、この名前が生まれました。この設計では、特にファクトテーブルとディメンションテーブルなどの正規化されたテーブルの構築に重点を置いています。必要に応じて、これらの小さな正規化されたテーブルから非正規化テーブルを構築することができます。この設計は、ETLの保守性と分析の容易さの両立を目指しています。

https://cdn-images-1.medium.com/max/800/1*keaNlycSUfgMGHawlcWnzw.png 星のような型に構成されたスタースキーマは、中心にファクトテーブルがあり、ディメンションテーブルに囲まれています。

ファクトテーブルとディメンションテーブル

ファクトテーブルとディメンションテーブルから非正規化テーブルを作成する方法を理解するには、それぞれの役割についてさらに詳しく説明する必要があります。

  • ファクトテーブルには通常、ポイントインタイムのトランザクションデータが含まれています。テーブルの各行は非常にシンプルで、多くの場合トランザクションの単位として表されます。シンプルさのために、ビジネスメトリクスを導出するための「信頼できる唯一の情報源」となることがよくあります。たとえば、Airbnbでは、契約、予約、変更、キャンセルなどのトランザクション同様のイベントを追跡するさまざまなファクトテーブルがあります。

  • ディメンションテーブルは一般的には、緩やかに変化する特定のエンティティの属性を含み、その属性は階層構造で編成されることがあります。ファクトテーブルで使用可能な外部キーが存在するかぎり、これらの属性は多くの場合「ディメンション」と呼ばれ、ファクトテーブルと結合できます。 Airbnbでは、ユーザー、リスティング、マーケットなどのさまざまなディメンションテーブルを作成しており、データの分割、区分に役立っています。

以下は、ファクトテーブルとディメンションテーブル(どちらも正規化されたテーブル)を結合し、各マーケットで過去一週間に何回の予約が発生したかなどの、基本的な分析に関する質問に答える方法の簡単な例です。追加のメトリックm_a, m_b, m_cおよびディメンションdim_x, dim_y, dim_zが最後のSELECT句に投影されるとどうなるか、正規化されていない表をこれらの正規化された表から簡単に作成できることを鋭いユーザーはなら分かるでしょう。

正規化されたテーブルは、アドホックな質問への回答、もしくは非正規化テーブルの構築に使用することができます。

データのパーティショニングと履歴データのバックフィル

https://cdn-images-1.medium.com/max/2000/1*74QUEUyBvqauoROtoVppqQ.png

データストレージコストが低く、計算コストが低い時代においては、企業は過去のデータをすべて捨てるのではなく、ウェアハウスに保存する余裕があります。このようなアプローチの利点は、企業が新しい変化に対応して履歴データを適切に処理し直すことができることです。

データスタンプによるデータのパーティショニング

非常に多くのデータを安易に利用すると、クエリの実行と分析の実行は時間の経過とともに非効率になります。 「早期および頻繁にフィルタリング」、「必要なフィールドのみを射影する」などのSQLベストプラクティスに従うことに加え、クエリのパフォーマンスを向上させる最も効果的な手法の一つは、データをパーティショニングすることです。

データのパーティショニングの背後にある基本的な考え方はどちらかというと単純です。すべてのデータをひとかたまりに格納するのではなく、独立し自己充足なかたまりに分割します。同じかたまりのデータには同じパーティションキーが割り当てられます。つまり、データのサブセットを極めてすばやく検索できます。この手法で、クエリのパフォーマンスは大幅に向上します。

特に、よく使用するパーティションキーは、日付スタンプ(datestamp)dsと略される)であり、それにはきちんとした理由があります。第一に、S3のようなデータストレージシステムでは、生データはしばしば日付スタンプによって編成され、時間によりラベルされたディレクトリに保存されます。さらに、バッチETLジョブの作業単位は通常一日です。つまり、毎日の実行ごとに新しい日付パーティションが作成されます。最後に、多くの分析に関する質問には、指定された時間範囲内で発生したイベントのカウントが含まれるため、日付スタンプによるクエリは非常に一般的なパターンです。 日付スタンプはデータパーティショニングにおけるよくある選択肢です!

dsでパーティション化されたテーブル

履歴データのバックフィル

パーティションキーとして日付スタンプを使用するもう一つの重要な利点は、データのバックフィルの容易さです。 ETLパイプラインが構築されると、メトリックとディメンションが過去にではなく未来にむかって計算されます。たびたび、過去の傾向や動きを再確認したいと思うかもしれません。そのような場合は、過去のメトリックとディメンションを計算する必要があります。この処理のことをデータのバックフィルと呼びます。

バックフィルは非常に一般的です。Hiveは動的パーティションの機能を組み込みました。これは、多数のパーティションで同じSQL操作を実行し、複数の挿入を一度に実行する機能です。動的パーティションの有用性を示すために、各マーケットにおける予約数をearliest_dsからlatest_dsまでダッシュボードにバックフィルする必要があるタスクを考えてみましょう。その際にはこのような感じにするでしょう:

上記の操作は、同じクエリを何度も実行しているが異なるパーティションで実行しているので、やや退屈です。時間範囲が広い場合、すぐに繰り返しの作業となります。ただし、動的パーティションを使用すると、この作業を一つのクエリとして大幅に簡素化できます。

SELECT句とGROUP BY句に追加されたdsWHERE句の展開された範囲におけるPARTITION(ds = '{{ds}}')からPARTITION(ds)への構文の変化に注目してください。動的パーティションの美しさは、GROUP BY dsで必要とされているのと同じ作業をすべてラップし、その結果を関連するdsごとのパーティションに一度に挿入することにあります。このクエリパターンは非常に強力で、多くのAirbnbのデータパイプラインで使用されています。後のセクションでは、Jinjaを使用してバックフィルロジックを組み込んだAirflowジョブのコントロールフローをどのように記述できるかを示します。

Airflowパイプラインの詳細

https://cdn-images-1.medium.com/max/2000/1*Oyvj7KeD-VdlcM2nNb2htw.png

ファクトテーブル、ディメンションテーブル、日付パーティションの概念、データのバックフィルを行うことの意味について学んだので、これらの概念を具体化し、実際のAirflow ETLジョブに入れてみましょう。

Directed Acyclic Graph (DAG)の定義

以前の記事で述べたように、ETLジョブは、抽出、変換、ロードの3つの部品の上に構築されています。概念的に聞こえるほど単純かもしれませんが、現実のETLジョブは複雑で、E、T、Lタスクの大量の組み合わせから構成されています。その結果、グラフを使用して複雑なデータフローを視覚化することは多くの場合に有効です。視覚的には、グラフのノードはタスクを表し、矢印はタスクの依存関係を表します。データがタスクで一度だけ計算される必要がありその後順次進められるものの場合、グラフは方向性があり非周期的です。このため、Airflowジョブは一般に「DAG」(Directed Acyclic Graphs)と呼ばれます。

https://cdn-images-1.medium.com/max/800/1*sTUUer1dRmEeHuc1h0023g.png 出典Airbnbの実験レポートフレームワーク用のDAGのスクリーンショット

Airflow UIに関する巧妙なデザインの一つは、コードを設定として使用して、どのユーザーもグラフビューでDAGを視覚化できるということです。データパイプラインの作成者は、タスクを可視化するためにタスク間の依存関係の構造を定義する必要があります。この仕様は、多くの場合、Airflowジョブの構造を示すDAG定義ファイルと呼ばれるファイルに記述されます。

演算子:センサー、オペレーター、およびトランスファー

DAGはデータパイプラインが実行される方法を記述する一方で、演算子はデータパイプラインが何をするかを記述します。通常、演算子には3つの大きなカテゴリがあります。

  • センサー:特定の時間、外部ファイル、または上流のデータソースを待機する

  • オペレーター:特定のアクション(たとえば、bashコマンドの実行、Pythonの関数実行、Hiveクエリの実行など)をトリガーする

  • トランスファー:データをある場所から別の場所に移動させる

鋭い読者は、これらの演算子のそれぞれが、前述した、抽出、変換、およびロードの各ステップにどのように対応しているのかが分かるでしょう。センサーは、一定時間が経過した後、または上流のデータソースで発生するデータが使用可能になった後に、データフローのブロックを解除します。 Airbnbでは、ほとんどのETLジョブにHiveクエリが含まれていることから、NamedHivePartitionSensorsを使用して、Hiveテーブルの最新パーティションがダウンストリーム処理に使用可能かどうかを確認することがよくありました。

オペレーターはデータの変換をトリガします。これは変換ステップに対応します。 Airflowはオープンソースなので、コントリビューターはBaseOperatorクラスをextendして、適切なカスタムオペレーターを作成できます。 Airbnbでは、HiveOperator(Hiveクエリを実行する)が使用される最も一般的なオペレーターですが、PythonOperatorPythonスクリプトを実行するなど)やBashOperator(たとえばbashスクリプトを実行する、または思いつきのSparkジョブを実行するなど)も使用しています。ここでは可能性は無限大です!

最後に、ある場所から別の場所へデータをトランスファーする特別な演算子もあります。この演算子は、多くの場合ETLのロードステップに変換されます。 Airbnbでは、MySqlToHiveTransferまたはS3ToHiveTransferを使用する頻度はかなり高いですが、これは主にデータインフラストラクチャやデータウェアハウスの場所に強く依存します。

簡単な例

以下は、DAG定義ファイルを定義し、AirflowのDAGをインスタンス化し、上で説明したさまざまな演算子を使用してそれに対応したDAG構造を定義する方法を示す簡単な例です。

DAGがレンダリングされると、次のグラフビューが表示されます。

https://cdn-images-1.medium.com/max/800/1*OMp-WLUdleDWVlBImO4OJA.png

DAGのグラフビューの仮想的な例

従うべきETLのベストプラクティス

https://cdn-images-1.medium.com/max/2000/1*LSvoTalvZTyMU3qi07bgrg.png

イメージクレジット:あなたの技術を磨くには練習が必要であり、そのためにはベストプラクティスに従うことが賢明です

あらゆる技術と同様に、簡潔で読みやすくスケーラブルなAirflowのジョブを書くには練習が必要です。私の最初の仕事では、ETLは私がやらなければならなかった一連の日々の機械的な仕事に過ぎませんでした。それを技術として捉えたり、ベストプラクティスを知ることをしませんでした。 Airbnbでは、ベストプラクティスについて多くのことを学び、良いETLとその美しさを理解し始めました。以下は、網羅的ではありませんが、良いETLパイプラインが従うべき原則の一覧です。

  • データテーブルのパーティショニング:前述したように、データのパーティショニングは、長い履歴を持つ大規模なテーブルを処理する場合に特に役立ちます。データが日付スタンプを使用してパーティショニングされるとき、動的パーティションを活用することでバックフィルを並列化できます。
  • データをインクリメンタルにロードする:この原則により、特にファクトテーブルからディメンションテーブルを作成する場合に、ETLはモジュール化し、管理しやすくなります。各実行では、ファクトテーブルの履歴全体をスキャンするのではなく、以前の日付パーティションのディメンションテーブルに新しいトランザクションを追加するだけです。
  • 冪等性を強制する:多くのデータサイエンティストは、実績分析を実行するためにポイントインタイムスナップショットを頼ります。これは、基礎となるソーステーブルが時間の経過とともに変更可能であってはならないことを意味します。そうでなければ、異なる回答が得られることになってしまいます。同一のビジネスロジックと時間範囲に対して実行された同じクエリが同じ結果を返すように、パイプラインを構築する必要があります。この性質には、冪等性という奇抜な名前があります。

  • ワークフローをパラメータ化する:テンプレートがHTMLページの構成を大幅に簡略化するように、JinjaはSQLと組み合わせて使用​​できます。前述のように、Jinjaテンプレートの一般的な使用法の一つは、バックフィルロジックを典型的なHiveクエリに組み込むことです。Stitch Fixは、このテクニックをETLにどのように使用するかを要約したとても良い記事です。

  • 早期および頻繁にデータをチェックする:データを処理する場合、ステージングテーブルにデータを書き込み、データ品質をチェックしてから、ステージングテーブルを最終的な本番テーブルと交換する方法は有用です。Airbnbでは、これをステージ・チェック・エクスチェンジのパラダイムと呼んでいます。この三ステップのパラダイムでのチェックは、重要な保護機構です。レコードの総数が0より大きい場合や、見えないカテゴリや異常値をチェックする異常検出システムなど複雑な場合に、単純なチェックが可能です。

ステージ・チェック・エクスチェンジ操作の骨組み(データパイプライン用の「ユニットテスト」とも呼ばれる)

  • 有用なアラートと監視システムを構築する:ETLジョブは実行に時間がかかることが多いため、DAGの進行状況を常に監視しなくてもすむように、アラートを追加して監視すると便利です。さまざまな企業がDAGを多様で創造的な方法で監視しています。Airbnbでは、規則的にEmailOperatorsを使用して、SLAを逸したジョブのアラートメールを送信しています。他のチームは、実験の不均衡を警告するためにアラートを使用しています。もう一つの興味深い例に、ZymergenがSlackOperatorでR-squaredなどのモデルパフォーマンスメトリクスをレポートしているといったものがあります。

これらの原則の多くは、経験豊かなデータエンジニアとの会話、Airflow DAGの構築経験、Gerard ToonstraによるAirflowを使用したETLのベストプラクティスなどの全てからインスピレーションを受けています。好奇心旺盛な読者のために、私はMaximeの次のトークを強く勧めます:

出典:Airflowの原著者、Maxime、ETLベストプラクティスについて語る

第二部のまとめ

シリーズ二つ目のこの記事では、スタースキーマとデータモデリングについてさらに詳しく説明しました。ファクトテーブルとディメンションテーブルの区別を学び、特にバックフィル用に、パーティションキーとして日付スタンプを使用する利点を確認しました。さらに、Airflowジョブの詳細な構造を分析し、Airflowで利用できるさまざまな演算子を具体的に確認しました。また、ETLを構築するためのベストプラクティスを紹介し、JinjaとSlackOperatorsを組み合わせて柔軟なAirflowジョブを実行する方法を示しました。可能性は無限大です!

シリーズの最後の記事では、いくつかの先進的なデータエンジニアリングパターン、具体的には、パイプラインの構築からフレームワークの構築へどうやって移るかについて説明します。動機付けの例としてAirbnbで使用したいくつかのフレームワークの例を再び使います。

この記事が役に立った場合は、第一部をみながら第三部を楽しみにしていてください。

貴重なフィードバックを提供してくれたJason GoodmanとMichael Mussonに感謝します。

(翻訳) データエンジニアリングビギナーズガイド 第一部

訳者まえがき

原著者の Robert Chang の許可を得て以下の記事を翻訳・公開しました。

medium.com

原著者は、Airbnb で Data Scientist をしています。

以下から翻訳内容です。

データエンジニアリングビギナーズガイド 第一部 データエンジニアリング: データサイエンスの似た従兄弟

https://cdn-images-1.medium.com/max/2000/1*gjgczPgeWlqWEVHtKYpJUg.png イメージクレジット:IñaquiCarniceroが建築したマタデロマドリードの美しい元屠殺場/倉庫

記事を書いた動機

データサイエンティストとしての経験を経るほど、データエンジニアリングはデータサイエンティストのツールキットの中で最も重要かつ基礎的なスキルの1つであると確信しています。この考えは、プロジェクトや就職機会に対する評価、そして個人の職務領域の広がりの両方に当てはまります。

以前の記事では、データを価値のあるものに変換するデータサイエンティストの能力は、自社のデータ・インフラストラクチャーの段階とデータウェアハウスの成熟度と大きく関連していると指摘しました。これは、データサイエンティストが、自分のスキルが企業の段階と必要性に揃っているかを慎重に評価するために、データエンジニアリングについて十分に知っておく必要があることを意味します。さらに、私が知っている偉大なデータサイエンティストの多くはデータサイエンスに強いだけでなく、そうでなければ到達できないより大きく野心的なプロジェクトを行うための隣接する分野としてデータエンジニアリングを活用することに対して戦略的でもあります。

その重要性にもかかわらず、データエンジニアリングに対する教育は限られていました。生まれたばかりの分野であることもあり、多くの点でデータエンジニアリングの訓練を受ける唯一の実現可能な道は仕事上で学ぶことであり、時には遅すぎることもあります。私はこの科目を忍耐強く教えてくれたデータエンジニアと一緒に働いていたことは大変幸運です。しかし、誰もが同じ機会を得られるわけではありません。その結果として、私はこのビギナーズガイドを書き、そのギャップを埋めるために学んだことをまとめることにしました。

このビギナーズガイドの構成

私の議論の範囲は決して網羅的ではなく、Airflowバッチデータ処理、およびSQL-like言語を中心にするつもりです。つまり、読者がデータエンジニアリングの基本的な理解を得るのを妨げないでしょう。この急速に成長しつつある新しい分野についてもっと学ぶために興味をそそられることを願っています。

  • 第一部(この記事)は、高度な入門記事として作成されています。個人的な逸話と専門家の洞察を組み合わせて、データエンジニアリングが何であるか、それがなぜ難しいのか、それがどのようにしてあなたやあなたの組織が拡大するのを助けることができるのかを具体的に説明します。この記事の主な読者は、就職の機会を評価するための基礎を学ぶ必要のある野心のあるデータサイエンティスト、もしくは最初のデータチームを構築しようとしている初期段階の創業者です。

  • 第二部は本質的な技術に関するものです。この記事は、ワークフローをプログラムで作成、スケジュール、および監視するためのAirbnbオープンソースツールであるAirflowに焦点を当てています。具体的には、AirflowHiveバッチ・ジョブを作成する方法、スター・スキーマなどの手法を使用してテーブルのスキーマを設計する方法、最後にETLを構築するためのベスト・プラクティスについて説明します。この記事は、データエンジニアリングスキルを磨こうとしている初期段階のデータエンジニアおよびデータエンジニアに適しています。

  • 第三部ではこのシリーズの最後の記事となる予定しです。ここでは、高度なデータエンジニアリング・パターン、より高いレベルの抽象化、およびETLの構築をより簡単かつ効率的にする拡張フレームワークについて説明します。これらのパターンの多くは、難解な方法を学んだAirbnbの経験豊富なデータエンジニアから教えを受けたものです。これらの洞察は、ワークフローをさらに最適化しようとしている経験豊富なデータサイエンティストやデータエンジニアにとって特に役立ちます。

現在私が隣接する分野としてデータエンジニアリングを学ぶことについて影響力を持った支持者であることを考えると、数年前は正反対の意見であったことに驚くかもしれません。私が最初の職に就いていた間には動機付けや感情的な奮闘がありました。

大学院を出た私の最初の仕事

大学院を出た直後、私はワシントンポストに所属する小さなスタートアップで最初のデータサイエンティストとして雇われました。無限の願望と共に、最も洗練されたテクニックを使用して最も難解なビジネス問題に取り組むための分析に準備されたデータが提供されると確信していました。

仕事を始めてすぐ、私の主な責任は想像したほど魅力的ではないことを知りました。代わりに、私の仕事はより基礎的なものでした。自分のサイトにアクセスしたユーザーの数、各ユーザーがコンテンツの閲覧に費やした時間、およびユーザーが記事をライク、リツイートした頻度を追跡するという重要なパイプラインを維持することでした。質の高いコンテンツを無料で提供してくれる代わりに、私たちが所属する出版社に読者に関わる洞察を伝えたので、確かに重要な仕事でした。

公にはしていませんでしたが、私は近い将来にその仕事を完了させることで、ここで説明するように、次は素晴らしいデータ製品を作る仕事に移れるようになることをいつも願っていました。結局のところそれがデータサイエンティストに期待されていることだ、と自分に言い聞かせていました。チャンスが巡ってくることはなく、数ヶ月後、私は失望と共に会社を辞めました。残念なことに、私の個人的な体験は、新しい労働市場で経験の浅い初期のスタートアップ(需要側)または新米のデータサイエンティスト(供給側)に馴染みが薄い人々すべてには共感されないかもしれません。

https://cdn-images-1.medium.com/max/1600/1*a-gxXI6hPZe9uuRYl1MYWQ.png イメージクレジット:ETLパイプラインを一生懸命に構築している私(真ん中の青い男性)

この経験の結果、私の不満は現実世界のデータプロジェクトが実際にどのように機能しているかをほとんど理解していないことに根ざしていることに気付きました。私は生データのワイルド・ウェストに放り出されました。あらかじめ処理され整頓された.csvファイルが存在する快適な土地とは程遠いものでした。それが平常状態である環境においては、私はなすすべがなく、居心地の悪さを感じていました。

多くのデータサイエンティストは、キャリアの早い段階で同様の道のりを経ていました。上級者たちは、この現実とそれに関連する課題を迅速に理解していました。私自身も、ゆっくりと徐々にこの新しい現実や仕事に適応しました。時間がたつにつれて、計装のコンセプトを発見し、マシン生成のログと奮闘し、多くのURLとタイムスタンプをパースしました。その中で最も重要なのは、SQLを学習したことです(気になっている読者用に。最初の仕事に先立ってSQLへの私の唯一触れたJennifer Widomの素晴らしいMOOCはこちら

現在では、私は、慎重かつ思慮深く計測することが分析が主に対象としていることであることを理解しています。このタイプの基礎的作業は、生まれ続ける流行語やハイプで満たされた世界に住んでいるときには特に重要です。

分析の階層

退屈なデータサイエンスの一面とメディアが時に切り出すバラ色の描写との間に矛盾があることを指摘した多くの提唱者の中で、特にモニカ・ロガティ氏はAIを採用しようとしている企業に対して警告しました。

人工知能欲求のピラミッドの頂点と考えてください。もちろん自己実現(AI)は素晴らしいですが、まずは食料、水、シェルター(データリテラシー、データ収集、データインフラストラクチャー)が必要です。

このフレームワークは物事を大局的に捉えさせてくれます。企業がビジネスをより効率的に最適化したり、データ製品をより賢く構築したりするには、基礎的作業のレイヤーを最初に構築する必要があります。このプロセスは、将来的に自己実現する前には食糧や水のような生存の必需品を気にすなければならないという道のりに似ています。このルールは、企業がニーズの順番の通りにデータ人材を雇うべきであることを意味します。スタートアップ企業において災害を引き起こす原因の1つに、最初のデータ人材としてデータモデリングに特化し、他のすべての前提条件である基礎レイヤーを構築する経験がほとんどない人材を雇うことがあります(私はこれを「不適当な採用順序問題」と呼んでいます)。

https://cdn-images-1.medium.com/max/1600/1*2XybEH3eav63pBIu-tlRlw.png 出典:モニカ・ロガティの素晴らしいミディアム上の記事「AIにおける欲求の階層」

残念なことに、多くの企業では、既存のデータサイエンストレーニングプログラムのほとんどは学術的または専門的であり、ピラミッドの知識の頂点に集中する傾向にあることに気付きません。パブリックAPIを使用して生データをスクレイプし、用意、またはアクセスすることを学生に奨励する最新のコースでも、ほとんどの場合、テーブルスキーマを適切に設計する方法やデータパイプラインを構築する方法を学生に教えるわけではありません。その結果、現実のデータサイエンスプロジェクトの重要な要素の一部が翻訳で失われました。

幸いなことに、ソフトウェアエンジニアリングが職業としてフロントエンジニアリング、バックエンドエンジニアリング、サイト信頼性エンジニアリングに区別されているように、私たちの分野も同様により成熟したものになると予測しています。人材構成は時間が経つにつれてより専門化し、データ集約型アプリケーションの基礎を構築するスキルと経験を持つ人材が増えるでしょう。

この将来の眺望はデータサイエンティストにとって何を意味するでしょう?私は、すべてのデータサイエンティストがデータエンジニアリングの専門家になる必要があるとまでは主張はしません。しかし、私は、すべてのデータサイエンティストが、能力と問題の一致を最大化するために、プロジェクトと雇用機会を評価するために十分に基礎を知っているべきだと考えています。解決に興味のある問題の多くが更なるデータエンジニアリングスキルを必要としていることがわかったら、データエンジニアリングの学習にもっと投資するのに遅すぎることはありません。これは実際に私がAirbnbで取ったアプローチです。

データ基盤とウェアハウスの構築

データエンジニアリングを学ぶ上での目的や興味のレベルにかかわらず、データエンジニアリングが何であるかを正確に知ることが重要です。AirflowのクリエーターであるMaxime Beauchemin氏は、彼の素晴らしい記事「The Rise of Data Engineer」(訳注: 拙訳「データエンジニアの始まり」)でデータエンジニアリングを特徴づけました。

データエンジニアリング分野は、ソフトウェアエンジニアリングから多くを受け継いだBIデータウェアハウスの上位集合と考えられるかもしれません。この学問は、拡張されたHadoopエコシステム、ストリーム処理、および大規模計算に関する概念とともに、いわゆる「ビッグデータ」分散システムの運用周りの専門性を統合しています。

データエンジニアが行うたくさんの価値ある仕事の中で、とても求められるスキルの1つは、データウェアハウスの設計、構築、および保守の能力です。小売倉庫のように消耗品がパッケージ化されて販売されているのと同様に、データウェアハウスは生データが変換され、クエリ可能な形式で格納される場所です。

https://cdn-images-1.medium.com/max/1600/1*tcDY4JKmvgfR0x_x0gpS_Q.png 出典:UC Berkeley CS 194コースからのJeff Hammerbacherのスライド

多くの点において、データウェアハウスは、高度な分析、ビジネスインテリジェンス、オンライン実験、機械学習などを可能にするエンジンであり、燃料でもあります。以下は、さまざまな段階の異なる企業のデータウェアハウスの役割を強調する具体的な例です。

  • 500pxにおける分析環境の構築:この記事では、Samson Huが500pxが製品市場の範囲を超えて成長しようとした際に直面した課題について説明します。同氏は、データウェアハウスをどのようにゼロから構築したかを詳しく説明しています。

  • Airbnbの実験プラットフォームの拡張Jonathon Parksは、Airbnbのデータエンジニアリングチームが、実験レポートフレームワークのような内部ツールにパワーを供給する専門的なデータパイプラインを構築した方法を説明しています。この仕事は、Airbnbの製品開発文化の形成と拡大に不可欠です。

  • Airbnbで家の価値を予測するための機械学習の利用:私自身によって書かれたものですが、なぜバッチ学習を構築するのか、なぜオフラインスコアリングの機械学習モデルは前もって多くのデータエンジニアリング作業を必要とするのかにつちえ説明しています。特に、特徴量エンジニアリング、構築、および学習データのバックフィルに関連する多くのタスクは、データエンジニアリングの仕事に似ています。

これらの基礎的なウェアハウスがなければ、データサイエンスに関連するすべての活動は高価になるか、またはスケーラビリティに欠けます。たとえば、適切に設計されたビジネスインテリジェンスウェアハウスがなければ、データサイエンティストは、よく尋ねられる同じ基本的な質問に対して異なる結果を報告することがあります。最悪の場合、誤って本番データベースに直接問い合わせをし、遅延やシャットダウンを引き起こします。同様に、実験レポートパイプラインがなければ、深い調査が必要な実験は甚だ手作業を繰り返し行うことになるでしょう。最終的にはラベル収集または特徴量計算をサポートするデータインフラストラクチャがなくても、トレーニングデータの構築には非常に時間がかかるかもしれません。

ETL:抽出、変換、およびロード

上で参照したすべての例は、Extract、Transform、およびLoadを表すETLという共通のパターンに従います。これらの3つの概念的なステップは、ほとんどのデータパイプラインがどのように設計され、構造化されているかを表しています。生データが分析可能データに変換される方法の青写真として機能します。このフローをより具体的に理解するために、Robinhoodのエンジニアリングブログから引用した次の画像が非常に有用であることに気づきました。

https://cdn-images-1.medium.com/max/800/1*Uo56hrm9r5L_5fmPsY7I9A.png 出典Vineet GoelのMedium上の記事「RobinhoodがAirflowを使用する理由」

  • 抽出:センサが上流のデータソースが到着するのを待つステップです(上流のソースは、マシンまたはユーザが生成したログ、リレーショナルデータベースのコピー、外部データセットなどです)。利用可能になると、ソースの場所からさらなる変換に向けてデータを転送します。

  • 変換:これがETLジョブの中心です。このステップではビジネスロジックを適用し、フィルタリング、グルーピング、および集計などのアクションを実行して、生データを分析可能データセットに変換します。このステップには、多くのビジネスの理解とドメインの知識が必要です。

  • ロード:最後に、処理されたデータをロードして最終的な目的地に転送します。多くの場合、このデータセットはエンドユーザーによって直接消費されるか、別のETLジョブの上流の依存性として処理され、いわゆるデータ系列を形成します。

すべてのETLジョブはこの共通のパターンに従いますが、実際のジョブそのものは使用方法、効用、および複雑さの点においてかなり異なるでしょう。これは、Airflowのジョブの非常にシンプルで簡単な例です:

出典:DataEngConf SF 2017のArthur Wiedmerのワークショップ

上記の例は、実行日時になってから1秒後に毎日bashで日付を出力するだけですが、現実のETLジョブははるかに複雑になる可能性があります。たとえば、本番データベースから一連のCRUD操作を抽出し、ユーザーの非アクティブ化などのビジネスイベントを導出するETLジョブがあるかもしれません。別のETLは、いくつかの実験設定ファイルを取り込み、その実験に関連するメトリックを計算し、最後にUIでp値および信頼区間を出力して、製品の変更がユーザーの解約を妨げているかどうかを知らせることができます。さらに別の例としは、数日​​後にユーザーが解約するかどうかを予測するために、機械学習モデルのための特徴を日次計算するバッチETLジョブがあります。可能性は無限大です!

ETLフレームワークの選択

ETLの構築に関しては、さまざまな企業が異なるベストプラクティスを採用する可能性があります。長年にわたり、多くの企業がETLを構築する際の共通の問題を特定する上で大きな進歩を遂げ、これらの問題をよりエレガントに解決するためのフレームワークを構築しました。

バッチデータ処理の世界では、いくつかの明白なオープンソースの競合があります。いくつか例を挙げると、Linkedinがオープンソース化したAzkabanは、Hadoopのジョブ依存関係を簡単に管理できるようにします。Spotifyが2014年にオープンソース化したPythonベースのフレームワークLuigi、同様に2015年にPinterestがPinballを、AirbnbがAirflow(こちらもPythonベース)をオープンソース化しました。

異なるフレームワークにはそれぞれ長所と短所があり、多くの専門家がそれらを幅広く比較しています(こちらこちらを参照ください)。採用するフレームワークにかかわらず、いくつかの機能を検討することが重要です。

https://cdn-images-1.medium.com/max/800/1*WszG7tFQbuQrYAHnNzxnlg.png 出典:Marty TrencseniのLuigi、Airflow、およびPinballの比較

  • 設定:元来ETLは複雑であり、データパイプラインのデータフローを簡潔に記述できる必要があります。その結果として、ETLがどのように作成されるかを評価することが重要となります。 UI、ドメイン固有の言語、またはコードで設定を行いますか?今日では、「Configuration as Code」の概念は普及し続けています。なぜなら、ユーザーがプログラム可能、カスタマイズ可能なパイプラインを表現できるよう構築できるためです。

  • UI、モニタリング、アラート:ジョブ自体にバグがない場合でも、長時間実行されるバッチ処理は必然的にエラー(クラスタ障害など)に陥る可能性があります。その結果、長時間実行されているプロセスの進行状況を追跡するには、監視とアラートが不可欠です。フレームワークは仕事の進捗状況に関してどんな視覚的情報を提供しますか?適時かつ正確にアラートや警告は表示されますか?

  • バックフィル:データパイプラインが構築されたら、時間を遡って過去のデータを再処理する必要があることがよくあります。理想的には、過去のデータをバックフィルするためのジョブと現在または将来のメトリックを計算するジョブの2つの別々のジョブを構築することは理想的ではありません。フレームワークはバックフィルをサポートしていますか?標準化され、効率的でスケーラブルな方法でこれを行うことができますか?これらはすべて重要な検討事項です。

私は、もちろんAirbnbで働く人としてAirflowを楽しんでいて、データエンジニアリングの仕事で遭遇した多くの一般的な問題に見事に対処してくれることを本当に感謝しています。デファクトのETLオーケストレーションエンジンとしてAirflowを正式に使用している120社以上の企業があることを考えると、Airflowは新世代のスタートアップ企業向けのバッチ処理の標準となる可能性があると主張するまであるかもしれません。

2つのパラダイムSQL中心のETL v.s. JVM中心の ETL

上記からわかるように、企業ごとにETLを構築するためのツールとフレームワークは大幅に異なるため、新しいデータサイエンティストとしてどのツールに投資するかを決定するのは非常に混乱することがあります。

Washington Post Labsでは、ETLは主にCronで最初にスケジュールされ、ジョブはVerticaスクリプトとしてまとめられていました。 Twitterでは、ETLジョブはPigで構築されていましたが、最近はTwitterの独自のオーケストレーションエンジンScaldingによって書かれ、スケジュールされています。 Airbnbでは、データパイプラインは主にAirflowを使ってHiveで書かれています。

最初の数年間、私はデータサイエンティストとして働いていましたが、組織が選んだものをそのまま受け取り、従っていました。私がJosh Willの話に出会った後に、ETLのパラダイムは2つあり、データ・サイエンティストは会社に入社する前にどちらのパラダイムが好むのかを真剣に考えなるべきだと気づきました。

https://cdn-images-1.medium.com/max/800/1*x1yKCJqTFFftEfn6G4jTlA.png ビデオソース:Josh WillsのKeynote @ DataEngConf SF 2016

  • JVM中心のETLは、通常JVMベースの言語(JavaScalaなど)で構築されます。これらのJVM言語のデータパイプラインに対するエンジニアリングは、しばしば命令的な方法でデータ変換を考えることを伴います(例えば、キーと値のペアなど)。ユーザー定義関数(UDF)の作成には、あまり苦労はしません。なぜなら、異なる言語で記述する必要がなく、同じ理由でテストジョブを簡単に行うことができるためです。このパラダイムはエンジニアにとって非常に一般的です。

  • SQL中心のETLは、通常、SQL、Presto、またはHiveなどの言語で作成されます。 ETLジョブはしばしば宣言的な方法で定義され、ほとんどすべてがSQLとテーブルを中心としています。 UDFの作成は、異なる言語(JavaPythonなど)で記述する必要があるため、時には面倒であり、これによりテストがさらに難しくなる可能性があります。このパラダイムはデータサイエンティストに人気があります。

両方のパラダイムの下でETLパイプラインを構築したデータサイエンティストとして、私はもちろんSQL中心のETLが好きです。実際、私は、新しいデータサイエンティストとして、SQLパラダイムで操作するときに、データエンジニアリングについてもっと迅速に学ぶことができると主張しています。なぜでしょうか? SQLの学習はJavaScalaを習得するよりもはるかに簡単です(すでに慣れていない場合)。新しい言語の上に新しいドメインを構築するよりも、データエンジニアリングのベストプラクティスの学習に力を注ぐことができます。

ビギナーズガイドまとめ - 第一部

この記事では、分析がいくつかのレイヤーの上に構築されていることを学びました。そして、成長する組織をスケールさせるためには、データウェアハウスの構築などの基礎的な作業が不可欠です。 ETLを構築するためのさまざまなフレームワークパラダイムについて簡単に説明しました。しかし、学習することや議論することはまだまだあります。

このシリーズの二つ目の記事では、具体的な内容に触れ、AirflowでHiveバッチジョブを構築する方法をお見せします。具体的には、Airflowジョブの基本的な分析を学び、パーティションセンサやオペレータなどの構成要素を介してアクションの抽出、変換、ロードを確認します。スタースキーマなどのデータモデリング手法を使用してテーブルを設計する方法を学習します。最後に、非常に有用ないくつかのETLベストプラクティスについて説明します。

この記事が有用だと思ったら、第二部第三部をお楽しみに。

(翻訳) データエンジニアの没落

訳者まえがき

下記の翻訳記事と対になる、データエンジニアの役割についての記事を翻訳しました。

satoshihirose.hateblo.jp

オリジナルの記事は下記のリンク先のもので、原著者は上記記事と同様に、Apache Airflow や Apache Superset のクリエーターで現在は Lyft で Data Engineer をしている Maxime Beauchemin です。

medium.com

以下から、翻訳記事の内容です。

データエンジニアの没落(翻訳)

この記事では、データエンジニアリングを定義しようとした最近のブログ記事である「The Rise of the Data Engineer」(訳者注: 拙訳「データエンジニアの始まり」)をフォローアップし、この新しい役割がデータ空間において歴史的、現代的な役割にどのように関係しているかを説明します。 この記事では、データエンジニアを悩ませる課題とリスクを明らかにし、思春期を過ぎるに際しこの学問分野に対抗して働く力を列挙したいと考えています。

この記事のタイトルはセンセーショナルで、内容は非常に悲観的ですが、私はデータエンジニアリングを強く信じていることに留意してください。前の記事と対比的な強いタイトルが必要でした。その役割が直面している逆境を理解し明らかにすることは、解決策を見出すための第一歩です。

また、ここに記す意見は私自身のものであり、シリコンバレーの数十のデータチームの人々と会話をしながら観察した結果に基づいています。これらの見解は私の雇用主の見解ではなく、現在の私の立場に直接関係しています。

退屈 & コンテキストの切り替え

ペンキが乾くのを見ることは、Extract Transform and Load(ETL)ロジックを記述して維持することと比べ、エキサイティングです。ほとんどのETLジョブは実行に長い時間がかかり、エラーまたは問題は実行時に発生する傾向があり、実行後のアサーションとなりがちです。開発時間と実行時間の比率は通常低いため、高い生産性を保つことは複数のパイプラインを同時に処理することを意味し、本質的に多くのコンテキストの切り替えを実行することを意味します。あなたの5つの 「ビッグデータジョブ」のうちの1つが終了する頃には、何時間も前の精神空間に戻り、次のイテレーションを作り上げる必要があります。どのようにカフェインを摂取しているか、最後のイテレーションからどれぐらいの時間が経過しているか、あなたがどのくらい計画的かに依存しますが、短期記憶の中の完全なコンテキストを取り戻すことはできないでしょう。これは時間を無駄にする全体的で愚かなエラーにつながります。

イテレーションサイクルごとの待機時間を1時間ごとにカウントすることにより、「皿回し」状態を維持することに取り憑かれてしまいます。午後11時30分の5-10分の仕事により翌日の2-4時間が節約できる場合、不健全なワークライフバランスになりがちです。

コンセンサスの追求

旧来のデータウェアハウジングの概念が消えつつあるとあなた考えているかどうかにかかわらず、対応付けられたディメンションとメトリックを達成することの追求は、それまで通り関連があります。私たちの多くは、人々が依然として一日おきに「Single Source of Truth(信頼できる唯一の情報源)」と言っているのを聞いています。データウェアハウスはビジネスを反映する必要があり、ビジネスの分析方法を明確にする必要があります。異なる名前空間または「データマート」にわたる矛盾した命名法と矛盾したデータは問題となります。意思決定を支援するために信頼を構築したい場合は、一貫性整合性が最低限必要です。現代の大規模な組織では分析プロセスにおけるデータ生成に何百人もの人々が関与していますが、コンセンサスの追求は、直ちに不可能ではないにしろチャレンジングなものです。

歴史的に、さまざまなプラットフォームに散在する不均一な分析や一致しない参照用データに関する問題を名付けるために、人々は「データサイロ」という蔑称を使用しました。サイロはプロジェクトの開始に伴って自然に生まれ、データの取得が発生する際に必然的にチームは混乱します。Master Data Management(MDM)データ統合、野心的なデータウェアハウジングプログラムなどのコンセンサスを取り付けるための方法論を使用し、これらの問題を解決することがビジネスインテリジェンス(現在のデータエンジニアリング)チームのタスクです。近年、現代の変化のペースが早い企業において、サイロの問題は急速に拡大しています。そこでは、「ダークマター」という用語を使用して、起こっている混乱の拡大の結果を表現することができます。適任ではない人々の集団がことを始めると、パイプラインのネットワークはすぐに混沌とし、調和のない無駄なものとなる可能性があります。データエンジニアが「データウェアハウスの図書館員」だとしたら、彼らの使命は巨大なリサイクルプラントでの出版物の分類作業のようだと感じるかもしれません。

ダッシュボードのライフサイクルが数週間ごとに数えられるような世界では、コンセンサスは、ビジネスの変化率や転換に追いつかないバックグラウンドのプロセスになります。伝統主義者はデータ・スチュワードシップとオーナーシップ・プログラムの開始を提案していますが、一定の規模とペースにおいては、こうした努力は拡大には見合わない弱い勢力となっています。

変更管理

有用なデータセットが広く使用され、大規模かつ複雑な依存性の有向非循環グラフ(DAG)を使用した方法で生み出されるようになると、ロジックもしくはソースデータを変更により下流の構造を破壊および/または無効にしがちです。上流の変更を反映するために、派生データセット、レポート、ダッシュボード、サービスおよび機械学習モデルのような下流のノードは変更および/または再計算が必要です。一般にデータ系統に関するメタデータは、通常は不完全であるかコード中に埋め込まれており、選ばれし少数の人々のみが読むための能力と忍耐力を有しています。上流の変更は必然的に、下流のエンティティを複雑な方法で破壊し無効にします。組織がどの程度精度よりも安定性を評価するかによっては、変化は恐ろしいもので、パイプラインの便秘につながる可能性があります。データエンジニアのインセンティブが安定性と連動している場合、彼らは、何も壊さない最良の方法は何も変えないことである、と速やかに学ぶでしょうです。

パイプラインは一般的に大規模で高価なため、適切なユニットテストや統合テストの実施は多少なりとも比例的である可能性があります。ここでの要点は、サンプリングされたデータとドライランで検証できるのはごくわずかというです。そして、あなたが扱えないほど単一の環境が混乱していると思ったら、複雑で異なるコードとデータを使用する開発環境とステージング環境を捨てやったとしても、正常な状態にしてください!私の経験上、大規模データの世界では、まともな開発環境やテスト環境を見つけるのはまれです。多くの場合、あなたが見つけられる最高のものは、文書化されていなくとも人々が適切であるとするプロセスをサポートするために使用する、名前空間の「サンドボックス」です。

データエンジニアリングでは、「DevOpsのムーブメント」についてのボートを見逃してしまい、現代のエンジニアに提供される心の平安と平穏の恩恵を受けることはめったにありません。彼らは姿を表さなかったのでボートを逃したのではなく、貨物のためのチケットが高価すぎたのでボートを逃したのです。

テーブルの最悪の座席

現代のチームの動きは速いですが、組織がエンジニアリング駆動、PM駆動、デザイン駆動のいずれであっても、そしてデータ駆動型であると考えているかどうかに関わらず、データエンジニアはそれほど駆り立てられないでしょう。インフラストラクチャーの役割と考えるべきです。その役割は、人々が当然のことを考えるものであり、壊れているときや約束が満たされていないときに注意を集めるものです。

会話に参加しているデータエンジニアがいる場合は、おそらくデータサイエンティストとアナリストが必要としているデータを収集するのを手助けします。対象のデータがデータウェアハウスの構造化された部分でまだ利用可能でない場合、その機会はデータエンジニアがデータを適切に記録し最終的にそのデータをウェアハウスに運ぶのを助ける一方、アナリストは生データをクエリする短期的な解決策を進めることとなるでしょう。ほとんどの場合で回答はタイムリーに必要であり、新しいディメンションとメトリックがウェアハウスに埋め戻される頃には、すでに古いニュースであり、皆は他に移っています。アナリストはその洞察に対する名誉を得て、その他の皆はこの新しい情報をウェアハウスに統合する時間のかかるバックグラウンドプロセスの必要性について疑問を呈するかもしれません。

従業員の業績レビューでは「インパクト」(速度と混乱を意味する)が最も求められていますが、データエンジニアリングは小さな短期間のインパクトであり、時間のかかるバックグラウンドプロセスであると責められます。データエンジニアたちは、「目立った変化を起こす」人から取り除かれた等級に位置します。

運用負荷

運用負荷は、自らが構築するシステムをサポートする専門職にとっては難しい現実です。 Q/Aチームは、「自らで構築したものは自らでサポートする」というモットーとこのアイデアに集まった現場の人々に大部分が置き換えられました。このアプローチは、技術者の意識を高め、技術的負債の蓄積に責任をもたせる適切な方法とみなされています。

データエンジニアリングには通常メンテナンスにかなりの負担がかかりますので、運用負荷は早く現れ、エンジニアを雇うよりも早くエンジニアを武装解除させます。もちろん、現代的なツールは人々の生産性を向上させますが、それはおそらくパイプラインビルダーが一度に多くの円盤を回転させることを可能にする機械に過ぎないでしょう。

さらに、運用負荷によって従業員の転職率が高くなる可能性があり、最終的には品質が低く一貫性がなく、維持できないめちゃくちゃなものとなってしまいます。

本当のソフトウェアエンジニアか?

この分野の人々は、データエンジニアが「本当のソフトウェアエンジニア」であるのか、異なるクラスのエンジニアであるのかについての議論を聞いています。一部の組織では、役割は異なり、異なる(低い)給与バンドを持っているかもしれません。カジュアルな観測では、コンピュータサイエンスの学位を持つデータエンジニアの割合は、ソフトウェアエンジニアリング全体のそれよりも著しく低いようです。

この記事で述べた理由により、その役割は悪循環を生む悪評判を被る可能性があります。

しかし、待って - まだ希望がある!

まだ立ち去らないでください!データが重要な競争優位性を持つという大きなコンセンサスは存在しており、企業はこれまで以上に分析に投資しています。 「データ成熟度」は予測可能な成長曲線に従い、それは最終的にはデータエンジニアリングが非常に重要であるという認識につながります。これまで示してきたように、何百もの企業が長期的なデータ戦略を倍増させ、データエンジニアリングに投資しています。その役割は存続しており、成長しています。

多くの企業がデータROIを安定水準に保ち、「データ運用の先端」に対する不満を感じると、今後のイノベーションがここで述べた苦労点に対処し、最終的にはデータ工学の新しい時代を創造することは間違いありません。

可能性のある方向性は非専門化であると言えます。適切なツールが揃うようになった場合、おそらく単純な作業を情報作業者に委ねることができます。継続的デリバリー技術と方法論が登場した一方でQ/A やリリースエンジニアに起こったことのように、より複雑なワークロードは共通のソフトウェアエンジニアリング作業の範囲となるでしょう。

いずれにしても、適切なツールと方法論によりその役割の方向性は定義され、この記事で述べられている懸念につながる根本原因の大部分は解決可能であると、私は期待しています。

私は今後「次世代のデータ対応型ETL」というタイトルのブログ記事を計画しています。ここでは、アクセシビリティと保守性が非常に重要となる新しいフレームワークの設計を提案します。このまだ構築されていないフレームワークには厳しい制約がありますが、代わりにベストプラクティスを強制することで強力な保証を提供します。乞うご期待!