データベースを利用するアプリケーション開発において、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関数で使用するには、以下の手順を実行します。
- combined_queries.sqlファイルをLambda関数のデプロイパッケージに含める
- 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関数はかなり近所である可能性はありますね。