AWS SDK for Python (Boto3) で Amazon Athena にクエリする

こんにちは、藤本です。

現地時間 5/19 に Amazon Athena の API が公開され、各種言語の AWS SDK や、AWS CLI でのアクセスもサポートされました。今までプログラムから扱う場合に JDBC しかなかったため、Java 以外の言語でクエリすることができませんでした。(OSS ツールなどでやっている方もいるようですが。)それが AWS SDK を利用するだけで簡単に各種言語のプログラムからアクセスできるようになりました。

Amazon Athena adds API/CLI, AWS SDK support, and audit logging with AWS CloudTrail

弊社ブログエントリでも AWS CLI を利用したクエリの使い方が早速エントリされました。

新機能 AWSCLIから Amazon Athena のクエリを実行する

API や流れは上記エントリを参照してもらうとして、私の方では Boto3 で API をひたすら叩いていきます。

環境

  • OS : MacOS 10.12.4
  • Python : 3.6.0
  • Boto3 : 1.4.4
  • Botocore : 1.5.53
  • IPython : 6.0.0

※ Boto3 で API を実行していますが、大事なのは Botocore のバージョンです。Amazon Athena の API を叩くためには Botocore のバージョンが1.5.52以上である必要があります。

$ pip list
appnope (0.1.0)
boto3 (1.4.4)
botocore (1.5.53)
decorator (4.0.11)
docutils (0.13.1)
ipython (6.0.0)
ipython-genutils (0.2.0)
jedi (0.10.2)
jmespath (0.9.2)
pexpect (4.2.1)
pickleshare (0.7.4)
pip (9.0.1)
prompt-toolkit (1.0.14)
ptyprocess (0.5.1)
Pygments (2.2.0)
python-dateutil (2.6.0)
s3transfer (0.1.10)
setuptools (28.8.0)
simplegeneric (0.8.1)
six (1.10.0)
traitlets (4.3.2)
wcwidth (0.1.7)

試してみた

それでは早速、Boto3 の関数を叩いて行きましょう。流れは AWS CLI のエントリと同じです。分かりやすく REPL(IPython)でやっていきます。

$ ipython
Python 3.6.0 (default, Apr 17 2017, 14:43:12)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.0.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]:

テストデータ準備

今回のテストデータには CloudTrail のログを利用します。CloudTrail のテーブル作成は下記エントリをご参照ください。sdktestデータベースにcloudtrail_logsテーブルを作成しました。

Amazon Athena CloudTrailSerdeを利用してAWSのアクティビティを探索する

準備

Athena の Client を呼び出します。AWS CLI の設定ファイルのデフォルトリージョンを ap-northeast-1 にしている方は Athena がリリースされているリージョンをご指定ください。今回はバージニア北部リージョン(us-east-1)を利用します。

In [1]: import boto3

In [2]: athena = boto3.client('athena', region_name='us-east-1')

クエリ実行の開始:start_query_execution

クエリの実行を開始します。クエリはバックグラウンドで実行されるのでこの関数では結果を取得することはできません。返ってくる結果のクエリID を利用して、実行結果を取得します。

Docstring 表示
In [4]: athena.start_query_execution?
Signature: athena.start_query_execution(*args, **kwargs)
Docstring:
.. _Examples and Code Samples: http://docs.aws.amazon.com/athena/latest/ug/code-samples.html


Runs (executes) the SQL query statements contained in the ``Query`` string.



For code samples using the AWS SDK for Java, see `Examples and Code Samples`_ in the *Amazon Athena
 User Guide* .



See also: `AWS API Documentation <https://docs.aws.amazon.com/goto/WebAPI/athena-2017-05-18/StartQu
eryExecution>`_


**Request Syntax**
::

  response = client.start_query_execution(
      QueryString='string',
      ClientRequestToken='string',
      QueryExecutionContext={
          'Database': 'string'
      },
      ResultConfiguration={
          'OutputLocation': 'string',
          'EncryptionConfiguration': {
              'EncryptionOption': 'SSE_S3'|'SSE_KMS'|'CSE_KMS',
              'KmsKey': 'string'
          }
      }
  )
<snip>
API 実行

データ件数をカウントするクエリを実行します。QueryString に発行する SQL クエリを、QueryExecutionContext に対象のデータベース名を、ResultConfiguration の OutputLocation に結果出力先となる S3 のパスを指定します。

In [12]: athena.start_query_execution(
    QueryString='select count(*) from cloudtrail_logs',
    QueryExecutionContext={
        'Database': 'sdktest'
    },
    ResultConfiguration={
        'OutputLocation': 's3://<OutputBucketName>/'
    }
)
Out[13]:
{'QueryExecutionId': '092b61a1-c468-4859-9035-39361cc405fc',
 'ResponseMetadata': {'HTTPHeaders': {'connection': 'keep-alive',
   'content-length': '59',
   'content-type': 'application/x-amz-json-1.1',
   'date': 'Tue, 23 May 2017 03:07:43 GMT',
   'x-amzn-requestid': 'fd218080-3f64-11e7-913d-a53b16848cc1'},
  'HTTPStatusCode': 200,
  'RequestId': 'fd218080-3f64-11e7-913d-a53b16848cc1',
  'RetryAttempts': 0}}

クエリはバックグラウンドで実行されるのですぐに結果が返ってきます。QueryExecutionIdを使って、Athena にクエリの状況や、結果を受け取ることができます。

クエリ実行状況の確認:get-query-execution

バックグラウンドで実行されているクエリの実行状況を確認します。

Docstring 表示
In [14]: athena.get_query_execution?
Signature: athena.get_query_execution(*args, **kwargs)
Docstring:
Returns information about a single execution of a query. Each time a query executes, information ab
out the query execution is saved with a unique ID.



See also: `AWS API Documentation <https://docs.aws.amazon.com/goto/WebAPI/athena-2017-05-18/GetQueryExecution>`_


**Request Syntax**
::

  response = client.get_query_execution(
      QueryExecutionId='string'
  )
<snip>

API 実行

先ほど実行したクエリの実行状況を確認します。QueryExecutionIdにクエリ実行のレスポンスに含まれるQueryExecutionIdの値を指定します。

In [15]: athena.get_query_execution(
    QueryExecutionId='092b61a1-c468-4859-9035-39361cc405fc'
)
Out[15]:
{'QueryExecution': {'Query': 'select count(*) from cloudtrail_logs',
  'QueryExecutionContext': {'Database': 'sdktest'},
  'QueryExecutionId': '092b61a1-c468-4859-9035-39361cc405fc',
  'ResultConfiguration': {'OutputLocation': 's3://<OutputBucketName>/092b61a1-c468-4859-9035-39361cc405fc.csv'},
  'Statistics': {'DataScannedInBytes': 94623484,
   'EngineExecutionTimeInMillis': 8794},
  'Status': {'CompletionDateTime': datetime.datetime(2017, 5, 23, 12, 7, 52, 601000, tzinfo=tzlocal()),
   'State': 'SUCCEEDED',
   'SubmissionDateTime': datetime.datetime(2017, 5, 23, 12, 7, 43, 472000, tzinfo=tzlocal())}},
 'ResponseMetadata': {'HTTPHeaders': {'connection': 'keep-alive',
   'content-length': '1063',
   'content-type': 'application/x-amz-json-1.1',
   'date': 'Tue, 23 May 2017 03:08:01 GMT',
   'x-amzn-requestid': '07f5ded6-3f65-11e7-b0f2-a33e056554a8'},
  'HTTPStatusCode': 200,
  'RequestId': '07f5ded6-3f65-11e7-b0f2-a33e056554a8',
  'RetryAttempts': 0}}

Stateにクエリのステータスや、ResultConfigurationにクエリ結果の CSV ファイルの出力パスが返ってきます。

クエリ実行結果の取得:get-query-results

クエリの実行結果を取得します。上記結果のStateSUCCEEDEDの時にクエリの結果を取得することができます。

Docstring 表示
In [21]: athena.get_query_results?
Signature: athena.get_query_results(*args, **kwargs)
Docstring:
Returns the results of a single query execution specified by ``QueryExecutionId`` . This request does not execute the query but returns results. Use  StartQueryExecution to run a query.



See also: `AWS API Documentation <https://docs.aws.amazon.com/goto/WebAPI/athena-2017-05-18/GetQueryResults>`_


**Request Syntax**
::

  response = client.get_query_results(
      QueryExecutionId='string',
      NextToken='string',
      MaxResults=123
  )
<snip>
API 実行

先ほど実行したクエリの実行結果を取得します。QueryExecutionIdにクエリ実行のレスポンスに含まれるQueryExecutionIdの値を指定します。

In [22]: athena.get_query_results(
    QueryExecutionId='092b61a1-c468-4859-9035-39361cc405fc'
)
Out[22]:
{'ResponseMetadata': {'HTTPHeaders': {'connection': 'keep-alive',
   'content-length': '574',
   'content-type': 'application/x-amz-json-1.1',
   'date': 'Tue, 23 May 2017 04:57:28 GMT',
   'x-amzn-requestid': '523b176c-3f74-11e7-b4ae-9b1216f5a3f4'},
  'HTTPStatusCode': 200,
  'RequestId': '523b176c-3f74-11e7-b4ae-9b1216f5a3f4',
  'RetryAttempts': 0},
 'ResultSet': {'ResultSetMetadata': {'ColumnInfo': [{'CaseSensitive': False,
     'CatalogName': 'hive',
     'Label': '_col0',
     'Name': '_col0',
     'Nullable': 'UNKNOWN',
     'Precision': 19,
     'Scale': 0,
     'SchemaName': '',
     'TableName': '',
     'Type': 'bigint'}]},
  'Rows': [{'Data': [{'VarCharValue': '_col0'}]},
   {'Data': [{'VarCharValue': '370889'}]}]}}

ResultSetに結果が記載されています。今回はレコード件数が 370,889 件あるということを取得できました。

実行クエリの中断:stop-query-execution

バックグラウンドしているクエリを中断します。

Docstring 表示
Signature: athena.stop_query_execution(*args, **kwargs)
Docstring:
.. _Examples and Code Samples: http://docs.aws.amazon.com/athena/latest/ug/code-samples.html


Stops a query execution.



For code samples using the AWS SDK for Java, see `Examples and Code Samples`_ in the *Amazon Athena User Guide* .



See also: `AWS API Documentation <https://docs.aws.amazon.com/goto/WebAPI/athena-2017-05-18/StopQueryExecution>`_


**Request Syntax**
::

  response = client.stop_query_execution(
      QueryExecutionId='string'
  )
<snip>
API実行

State が RUUNING のクエリに対して、実行することで実行中のクエリを中断します。

### State が RUNNING のクエリに対して
In [49]: athena.get_query_execution(
    QueryExecutionId=result['QueryExecutionId']
)
Out[49]:
{'QueryExecution': {'Query': 'select count(*) from cloudtrail_logs',
  'QueryExecutionContext': {'Database': 'sdktest'},
  'QueryExecutionId': '7c30fd12-1326-46da-bfca-b3092dc3517c',
  'ResultConfiguration': {'OutputLocation': 's3://<OutputBucketName>/7c30fd12-1326-46da-bfca-b3092dc3517c.csv'},
  'Statistics': {},
  'Status': {'State': 'RUNNING',
   'SubmissionDateTime': datetime.datetime(2017, 5, 23, 14, 33, 23, 282000, tzinfo=tzlocal())}},
 'ResponseMetadata': {'HTTPHeaders': {'connection': 'keep-alive',
   'content-length': '784',
   'content-type': 'application/x-amz-json-1.1',
   'date': 'Tue, 23 May 2017 05:33:23 GMT',
   'x-amzn-requestid': '56a6e3a6-3f79-11e7-9fdf-9d7f8fc70550'},
  'HTTPStatusCode': 200,
  'RequestId': '56a6e3a6-3f79-11e7-9fdf-9d7f8fc70550',
  'RetryAttempts': 0}}

### Stop API を実行すると
In [50]: athena.stop_query_execution(
    QueryExecutionId=result['QueryExecutionId']
)
Out[50]:
{'ResponseMetadata': {'HTTPHeaders': {'connection': 'keep-alive',
   'content-length': '2',
   'content-type': 'application/x-amz-json-1.1',
   'date': 'Tue, 23 May 2017 05:33:24 GMT',
   'x-amzn-requestid': '572c40ac-3f79-11e7-b4ae-9b1216f5a3f4'},
  'HTTPStatusCode': 200,
  'RequestId': '572c40ac-3f79-11e7-b4ae-9b1216f5a3f4',
  'RetryAttempts': 0}}

### CANCELLED になる
In [50]: athena.get_query_execution(
    QueryExecutionId=result['QueryExecutionId']
)
Out[52]:
{'QueryExecution': {'Query': 'select count(*) from cloudtrail_logs',
  'QueryExecutionContext': {'Database': 'sdktest'},
  'QueryExecutionId': '7c30fd12-1326-46da-bfca-b3092dc3517c',
  'ResultConfiguration': {'OutputLocation': 's3://<OutputBucketName>/7c30fd12-1326-46da-bfca-b3092dc3517c.csv'},
  'Statistics': {'DataScannedInBytes': 5903681,
   'EngineExecutionTimeInMillis': 1071},
  'Status': {'CompletionDateTime': datetime.datetime(2017, 5, 23, 14, 33, 24, 426000, tzinfo=tzlocal()),
   'State': 'CANCELLED',
   'SubmissionDateTime': datetime.datetime(2017, 5, 23, 14, 33, 23, 282000, tzinfo=tzlocal())}},
 'ResponseMetadata': {'HTTPHeaders': {'connection': 'keep-alive',
   'content-length': '1058',
   'content-type': 'application/x-amz-json-1.1',
   'date': 'Tue, 23 May 2017 05:33:43 GMT',
   'x-amzn-requestid': '62e02a0a-3f79-11e7-b2a1-1fc3451d9eb1'},
  'HTTPStatusCode': 200,
  'RequestId': '62e02a0a-3f79-11e7-b2a1-1fc3451d9eb1',
  'RetryAttempts': 0}}

実行したクエリ一覧の取得:list-query-executions

実行中、実行済みのクエリ ID を一覧で取得します。クエリ ID しか取得しないので詳細は get-query-execution で取得する必要があります。

Docstring 表示
Signature: athena.list_query_executions(*args, **kwargs)
Docstring:
.. _Examples and Code Samples: http://docs.aws.amazon.com/athena/latest/ug/code-samples.html


Provides a list of all available query execution IDs.



For code samples using the AWS SDK for Java, see `Examples and Code Samples`_ in the *Amazon Athena User Guide* .



See also: `AWS API Documentation <https://docs.aws.amazon.com/goto/WebAPI/athena-2017-05-18/ListQueryExecutions>`_


**Request Syntax**
::

  response = client.list_query_executions(
      NextToken='string',
      MaxResults=123
  )
<snip>
API 実行

全ての API 実行履歴を取得します。

In [57]: athena.list_query_executions()
Out[57]:
{'NextToken': 'eJxVzMEKAiEUQNF/cWvCPHXeaLtpRqJlRB8g6sJFGo7CSPTvBa3aXg73RZ4lPmzp5Eii3+m1hdLNHlyrMSeTaqydnkP979upz87llurFUyFBS9TTCEqRA9mCy8n/jiAVTnriXCFX9HZfFmNWs1LUeuAII8MAgknwjn0FZ2BRWO+lGyyS9wcxMy//',
 'QueryExecutionIds': ['7c30fd12-1326-46da-bfca-b3092dc3517c',
  '86c223cb-1d40-4d43-b6e8-9c093d2071a5',
  '5e9ef952-791d-4a75-b11d-b86e9315e608',
  '5d114c8e-8b8e-4bf6-8f95-b68658defa19',
  '3c1fdcc7-d87e-4960-bcb6-65e00212120b',

<snip>

  'a9543896-e423-4520-a5ae-196047e57636',
  '51ab1316-3b79-40c6-a4d3-065db5ff05a8',
  '5c538d07-2625-4f48-aaee-4b3413d8414e',
  '471e8383-c5fb-4d38-ac00-3fbc8249ce59',
  '69902615-6e13-41dc-8622-1a63add4c0a6'],
 'ResponseMetadata': {'HTTPHeaders': {'connection': 'keep-alive',
   'content-length': '2172',
   'content-type': 'application/x-amz-json-1.1',
   'date': 'Tue, 23 May 2017 05:52:16 GMT',
   'x-amzn-requestid': 'fa2252e0-3f7b-11e7-b03f-edb3a58fd066'},
  'HTTPStatusCode': 200,
  'RequestId': 'fa2252e0-3f7b-11e7-b03f-edb3a58fd066',
  'RetryAttempts': 0}}

クエリ実行状況の一括取得:batch-get-query-execution

クエリ実行状況は一括取得することができます。

Docstring 表示
Signature: athena.batch_get_query_execution(*args, **kwargs)
Docstring:
Returns the details of a single query execution or a list of up to 50 query executions, which you provide as an array of query execution ID strings. To get a list of query execution IDs, use  ListQueryExecutions . Query executions are different from named (saved) queries. Use  BatchGetNamedQuery to get details about named queries.



See also: `AWS API Documentation <https://docs.aws.amazon.com/goto/WebAPI/athena-2017-05-18/BatchGetQueryExecution>`_


**Request Syntax**
::

  response = client.batch_get_query_execution(
      QueryExecutionIds=[
          'string',
      ]
  )
<snip>

get-query-executionと比べて、QueryExecutionIdsでクエリ ID を list で指定できるようになっただけですね。

API 実行
In [72]: athena.batch_get_query_execution(
    QueryExecutionIds=[
        '3c1fdcc7-d87e-4960-bcb6-65e00212120b',
        '092b61a1-c468-4859-9035-39361cc405fc'
    ]
)
Out[72]:
{'QueryExecutions': [{'Query': 'select count(*) from cloudtrail_logs',
   'QueryExecutionContext': {'Database': 'sdktest'},
   'QueryExecutionId': '092b61a1-c468-4859-9035-39361cc405fc',
   'ResultConfiguration': {'OutputLocation': 's3://<OutputBucketName>/092b61a1-c468-4859-9035-39361cc405fc.csv'},
   'Statistics': {'DataScannedInBytes': 94623484,
    'EngineExecutionTimeInMillis': 8794},
   'Status': {'CompletionDateTime': datetime.datetime(2017, 5, 23, 12, 7, 52, 601000, tzinfo=tzlocal()),
    'State': 'SUCCEEDED',
    'SubmissionDateTime': datetime.datetime(2017, 5, 23, 12, 7, 43, 472000, tzinfo=tzlocal())}},
  {'Query': 'select count(*) from cloudtrail_logs',
   'QueryExecutionContext': {'Database': 'sdktest'},
   'QueryExecutionId': '3c1fdcc7-d87e-4960-bcb6-65e00212120b',
   'ResultConfiguration': {'OutputLocation': 's3://<OutputBucketName>/3c1fdcc7-d87e-4960-bcb6-65e00212120b.csv'},
   'Statistics': {'DataScannedInBytes': 95200201,
    'EngineExecutionTimeInMillis': 11393},
   'Status': {'CompletionDateTime': datetime.datetime(2017, 5, 23, 14, 30, 49, 342000, tzinfo=tzlocal()),
    'State': 'SUCCEEDED',
    'SubmissionDateTime': datetime.datetime(2017, 5, 23, 14, 30, 37, 623000, tzinfo=tzlocal())}}],
 'ResponseMetadata': {'HTTPHeaders': {'connection': 'keep-alive',
   'content-length': '1012',
   'content-type': 'application/x-amz-json-1.1',
   'date': 'Tue, 23 May 2017 07:09:16 GMT',
   'x-amzn-requestid': 'bc5e3e88-3f86-11e7-b537-57c69699542d'},
  'HTTPStatusCode': 200,
  'RequestId': 'bc5e3e88-3f86-11e7-b537-57c69699542d',
  'RetryAttempts': 0},
 'UnprocessedQueryExecutionIds': []}

QueryExcecutions に配列でそれぞれのクエリ実行状況が含まれています。

まとめ

いかがでしたでしょうか? Python プログラムから Athena へクエリしたくて、標準 SDK の登場を待望んだ方も多くいらっしゃるのではないでしょうか。AWS SDK for Python(Boto3)を利用することで Amazon Athena へ簡単にクエリすることができるようになりました。