SQLクエリの管理と実行をPythonで効率化 – AWS Lambdaの活用事例

SQLクエリの管理と実行をPythonで効率化 - AWS Lambdaの活用事例 プログラミング

データベースを利用するアプリケーション開発において、SQLクエリの管理と実行は重要な課題です。
特に、AWS Lambdaのようなサーバーレス環境で開発を行う場合、効率的な開発と検証プロセスが求められます。

本記事では、PythonとTiDB Serverlessを使ってSQLクエリを管理し、
ローカル環境での検証とAWS Lambda上での実行を効率化する方法を紹介します。

TiDB Serverlessは、MySQLと互換性のあるサーバーレスのデータベースです。
DBサーバーを自分で構築する手間が省けるので、検証には最適と言えます。
検証であれば、無料で使える枠が用意されています。

ローカル環境での開発とSQL検証の重要性

AWS Lambda上で直接開発を行うことは、以下のような理由から非効率的です。

  • Lambda関数の実行にはコストがかかる
  • Lambda上での開発ツールは、ローカル環境ほど充実していない
  • Lambda上でのデバッグとテストは時間がかかる

そのため、SQLクエリの検証はローカル環境で行うことをお勧めします。
ローカル環境では、充実した開発ツールを使ってクエリの動作を確認し、必要な修正を加えることができます。

Lambda関数にも無料枠はあります。
しかし、工夫をしなければLambda関数上での開発は非効率です。

コードとSQLの分離によるメリット

本事例では、SQLクエリをPythonコードから分離し、combined_queries.sqlというファイルで管理しています。
このファイルを用いた開発方法は、次の記事をご覧ください。

この方法には、以下のようなメリットがあります。

  • SQLクエリの再利用性が向上する
  • コードの可読性とメンテナンス性が向上する
  • ローカル環境とLambda関数間でのクエリの共有が容易になる

ローカル環境で検証したSQLクエリを、Lambda関数にデプロイする際は、combined_queries.sqlファイルを更新するだけで済みます。
これにより、開発と検証のプロセスが大幅に効率化されます。

ローカル環境でのSQL検証

ローカル環境でSQLクエリを検証するには、以下のPythonコードを使用します。

import pymysql
import time
from config import Config

config = Config()

db_config = {
    'host': config.tidb_host,
    'port': 4000,
    'user': config.tidb_user,
    'password': config.tidb_password,
    'database': config.tidb_db_name,
    'ssl_verify_cert':  True,
    'ssl_verify_identity': True,
    'ssl_ca': config.ca_path
}

def get_query(query_name):
    with open('combined_queries.sql', 'r', encoding='utf-8') as file:
        content = file.read()
        query_start = content.find(f'-- [{query_name}]\n')
        if query_start == -1:
            raise ValueError(f"Query '{query_name}' not found.")
        query_end = content.find('-- [', query_start + 1)
        if query_end == -1:
            query_end = len(content)
        query_text = content[query_start:query_end].strip()
        query_lines = query_text.split('\n')
        query = query_lines[-1].strip()
        return query

def execute_query(query_name, params):
    query = get_query(query_name)
    query = query.replace('?', '%s')  # プレースホルダーを%sに変換
    connection = pymysql.connect(**db_config)
    cursor = connection.cursor()
    start_time = time.perf_counter()
    cursor.execute(query, params)
    data = cursor.fetchall()
    end_time = time.perf_counter()
    cursor.close()
    connection.close()
    return data, end_time - start_time

def main():
    city_id = 300
    data, execution_time = execute_query('get_city_data_by_id', (city_id,))
    print(f"Result of get_city_data_by_id (city_id {city_id}): {data}")
    print(f"Execution time: {execution_time:.5f} seconds")
    print()


if __name__ == '__main__':
    main()

get_query関数でcombined_queries.sqlからクエリを取得し、execute_query関数でTiDBに接続してクエリを実行しています。
ローカル環境では、TiDBの接続情報をdb_config変数で管理する形となります。

具体的には、.envファイルに設定情報を登録しています。
その.envをpython-dotenvで読み込んでいるということです。

このコードを使って、ローカル環境でSQLクエリの動作を確認し、必要な修正を加えます。
なお、データにはMySQLのサンプルデータベースである「world database」を利用しています。

TiDB Serverlessの管理画面で次のように確認できます。

Lambda関数へのデプロイ

ローカル環境で検証したSQLクエリをLambda関数で使用するには、以下の手順を実行します。

  1. combined_queries.sqlファイルをLambda関数のデプロイパッケージに含める
  2. Lambda関数のコードを更新する

Lambda関数のコードは、以下のようになります。

import pymysql
import time
import os

def get_query(query_name):
   with open('combined_queries.sql', 'r', encoding='utf-8') as file:
       content = file.read()
       query_start = content.find(f'-- [{query_name}]\n')
       if query_start == -1:
           raise ValueError(f"Query '{query_name}' not found.")
       query_end = content.find('-- [', query_start + 1)
       if query_end == -1:
           query_end = len(content)
       query_text = content[query_start:query_end].strip()
       query_lines = query_text.split('\n')
       query = query_lines[-1].strip()
       return query

def execute_query(query_name, params):
   query = get_query(query_name)
   query = query.replace('?', '%s')  # プレースホルダーを%sに変換
   db_config = {
       'host': os.environ['TIDB_HOST'],
       'port': int(os.environ['TIDB_PORT']),
       'user': os.environ['TIDB_USER'],
       'password': os.environ['TIDB_PASSWORD'],
       'database': os.environ['TIDB_DB_NAME'],
       'ssl_verify_cert': True,
       'ssl_verify_identity': True,
       'ssl_ca': os.environ['CA_PATH']
   }
   connection = pymysql.connect(**db_config)
   cursor = connection.cursor()
   start_time = time.perf_counter()
   cursor.execute(query, params)
   data = cursor.fetchall()
   end_time = time.perf_counter()
   cursor.close()
   connection.close()
   return data, end_time - start_time

def lambda_handler(event, context):
   city_id = 300
   data, execution_time = execute_query('get_city_data_by_id', (city_id,))
   print(f"Result of get_city_data_by_id (city_id {city_id}): {data}")
   print(f"Execution time: {execution_time:.5f} seconds")

   return {
       'statusCode': 200,
       'body': 'Query executed successfully'
   }

Lambda関数では、TiDBの接続情報を環境変数から取得しています。
また、TiDBへの接続にはPyMySQLライブラリを利用しています。

AWS LambdaへのPythonライブラリのインストールは、以下の記事が参考になります。

まとめ

PythonとTiDBを使ってSQLクエリを管理する方法を紹介しました。

SQLクエリをコードから分離することで、再利用性と可読性が向上します。
その結果、ローカル環境とLambda関数間でのクエリの共有が容易になります。

また、ローカル環境でSQLクエリを検証することで、開発と検証のプロセスを大幅に効率化できます。
本事例の方法を採用することで、AWS Lambdaを使ったデータベースアプリケーションの開発と運用の生産性が向上するでしょう。

ちなみに、各環境における検証コードの実行結果は以下となります。

ローカルマシン

Result of get_city_data_by_id (city_id 300): ((300, 'Gravataí', 'BRA', 'Rio Grande do Sul', 223011),)
Execution time: 0.01424 seconds

Lambda関数

Result of get_city_data_by_id (city_id 300): ((300, 'Gravataí', 'BRA', 'Rio Grande do Sul', 223011),)
Execution time: 0.00859 seconds

Lambda関数は、かなり高速です。
TiDB ServerlessはAWSの東京リージョンで動いていることが判明しています。

そして、Lambda関数も東京リージョンで動かしています。
つまり、TiDB ServerlessとLambda関数はかなり近所である可能性はありますね。

タイトルとURLをコピーしました