

# 教程：使用 Lambda 函数访问 Amazon RDS 数据库
<a name="rds-lambda-tutorial"></a>

在本教程中，您使用 Lambda 函数通过 RDS 代理将数据写入 [Amazon Relational Database Service](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html)（Amazon RDS）数据库。您的 Lambda 函数在 Amazon Simple Queue Service（Amazon SQS）队列中读取记录，每当添加消息时，都将新的项目写入您数据库的表中。在此示例中，您使用 AWS 管理控制台 手动向队列添加消息。下图显示了您用于完成教程的 AWS 资源。

![\[\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/images/TUT_Lambda_1.png)


借助 Amazon RDS，您可以使用 Microsoft SQL Server、MariaDB、MySQL、Oracle Database 和 PostgreSQL 等常见数据库产品在云中运行托管式关系数据库。通过使用 Lambda 访问您的数据库，您可以读取和写入数据以响应事件，例如在您的网站上注册的新客户。您的函数、数据库实例和代理可以自动扩展以满足高需求时段。

要完成本教程，请执行以下任务：

1. 在您的 AWS 账户的原定设置 VPC 中启动 RDS for MySQL 数据库实例和代理。

1. 创建并测试 Lambda 函数，该函数在您的数据库中创建新表并将数据写入其中。

1. 创建 Amazon SQS 队列并将其配置为在添加新消息时调用您的 Lambda 函数。

1. 使用 AWS 管理控制台向队列添加消息并使用 CloudWatch Logs 监控结果，来测试完整设置。

通过完成这些步骤，您将学习：
+ 如何使用 Amazon RDS 创建数据库实例和代理，并将 Lambda 函数连接到代理。
+ 如何使用 Lambda 对 Amazon RDS 数据库执行创建和读取操作。
+ 如何使用 Amazon SQS 调用 Lambda 函数。

您可以使用 AWS 管理控制台 或 AWS Command Line Interface (AWS CLI) 完成此教程。

## 先决条件
<a name="vpc-rds-prereqs"></a>

在开始之前，请完成以下各节中的步骤：
+ [注册 AWS 账户](CHAP_SettingUp.md#sign-up-for-aws)
+ [创建具有管理访问权限的用户](CHAP_SettingUp.md#create-an-admin)

## 创建 Amazon RDS 数据库实例
<a name="vpc-rds-create-RDS-instance"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/images/TUT_Lambda_step1.png)


Amazon RDS 数据库实例是在 AWS 云 中运行的独立数据库环境。实例可以包含一个或多个由用户创建的数据库。除非您另行指定，否则 Amazon RDS 会在您的 AWS 账户包含的原定设置 VPC 中创建新的数据库实例。有关 Amazon VPC 的更多信息，请参阅 [Amazon Virtual Private Cloud 用户指南](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html)。

在本教程中，您在您 AWS 账户 的默认 VPC 中创建一个新实例，并在该实例中创建一个名为 `ExampleDB` 的数据库。您可以使用 AWS 管理控制台或 AWS CLI 创建数据库实例和数据库。

**创建数据库实例**

1. 打开 Amazon RDS 控制台并选择**创建数据库**。

1. 保持**标准创建**选项处于选中状态，然后在**引擎选项**中选择 **MySQL**。

1. 在**模板**中，选择**免费套餐**或**沙盒**。对于免费套餐账户，将显示**免费套餐**。对于付费套餐账户，将显示**沙盒**。

1. 在**设置**中，为 **DB 实例标识符**输入 **MySQLForLambda**。

1. 请通过执行以下操作设置用户名和密码：

   1. 在**凭证设置**中，将**主用户名**设置为 `admin`。

   1. 对于**主密码**，输入并确认密码以访问您的数据库。

1. 通过执行以下操作指定数据库名称：
   + 将所有剩余的原定设置选项保持选中状态，然后向下滚动到**其他配置**部分。
   + 展开此部分并输入 **ExampleDB** 作为**初始数据库名称**。

1. 保持所有其余默认选项处于选中状态，然后选择**创建队列**。

## 创建 Lambda 函数和代理
<a name="auto-create-Lambda"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/images/TUT_Lambda_step2.png)


您可以使用 RDS 控制台在与数据库相同的 VPC 中创建 Lambda 函数和代理。

**注意**  
只有在数据库完成创建并处于**可用**状态时，才能创建这些关联的资源。

**创建关联函数和代理**

1. 从**数据库**页面，检查您的数据库是否处于**可用**状态。如果是，请继续执行下一步。否则，请等到您的数据库变为可用。

1. 选择您的数据库，并从**操作**中选择**设置 Lambda 连接**。

1. 在**设置 Lambda 连接**页面上，选择**创建新函数**。

   将**新的 Lambda 函数名称**设置为 **LambdaFunctionWithRDS**。

1. 在 **RDS 代理**部分，选择**使用 RDS 代理进行连接**选项。进一步选择**创建新代理**。
   + 对于**数据库凭证**，选择**数据库用户名和密码**。
   + 对于**用户名**，指定 `admin`。
   + 对于**密码**，输入您为数据库实例创建的密码。

1. 选择**设置**以完成代理和 Lambda 函数的创建。

向导完成设置，并提供指向 Lambda 控制台的链接以查看您的新函数。在切换到 Lambda 控制台之前，请记下代理端点。

## 创建函数执行角色
<a name="vpc-rds-create-execution-role"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/images/TUT_Lambda_step3.png)


在创建 Lambda 函数之前，您需要创建一个执行角色来为您的函数提供必要的权限。在本教程中，Lambda 需要权限来管理与包含您数据库实例的 VPC 的网络连接，以及轮询来自 Amazon SQS 队列的消息。

为了向您的 Lambda 函数提供其所需的权限，本教程使用 IAM 托管策略。这些策略可授予许多常见使用案例的权限，可在您的 AWS 账户 中使用。有关使用托管策略的更多信息，请参阅 [策略最佳实践](security_iam_id-based-policy-examples.md#security_iam_service-with-iam-policy-best-practices)。

**创建 Lambda 执行角色**

1. 打开 IAM 控制台的[角色](https://console.aws.amazon.com/iamv2/home#/roles)页面，然后选择**创建角色**。

1. 对于**受信任的实体类型**，选择 **AWS 服务**，对于**使用案例**，选择 **Lambda**。

1. 选择**下一步**。

1. 通过执行以下操作添加 IAM 托管策略：

   1. 使用策略搜索框，搜索 **AWSLambdaSQSQueueExecutionRole**。

   1. 在结果列表中，选中角色旁的复选框，然后选择**清除筛选条件**。

   1. 使用策略搜索框，搜索 **AWSLambdaVPCAccessExecutionRole**。

   1. 在结果列表中，选中角色旁的复选框，然后选择**下一步**。

1. 对于**角色名称**，输入 **lambda-vpc-sqs-role**，然后选择**创建角色**。

在本教程的后面部分，您需要提供刚刚创建的执行角色的 Amazon 资源名称（ARN）。

**查找执行角色 ARN**

1. 打开 IAM 控制台的[角色](https://console.aws.amazon.com/iamv2/home#/roles)页面，然后选择您的角色（`lambda-vpc-sqs-role`）。

1.  复制**摘要**部分中显示的 **ARN**。

## 创建 Lambda 部署包
<a name="vpc-rds-create-deployment-package"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/images/TUT_Lambda_step4.png)


以下示例 Python 代码使用 [PyMySQL](https://pymysql.readthedocs.io/en/latest/) 包打开与您的数据库的连接。首次调用函数时，它还会创建一个名为 `Customer` 的新表。该表使用以下架构，其中 `CustID` 是主键：

```
Customer(CustID, Name)
```

该函数还使用 PyMySQL 向该表添加记录。该函数使用您将添加到 Amazon SQS 队列的消息中指定的客户 ID 和名称来添加记录。

该代码在处理程序函数之外创建了与数据库的连接。在初始化代码中创建连接，使后续调用能够重用该连接并提高性能。在生产应用程序中，您还可以使用[预置并发](https://docs.aws.amazon.com/lambda/latest/dg/provisioned-concurrency.html)来初始化所请求数量的数据库连接。调用函数后，这些连接即可用。

```
import sys
import logging
import pymysql
import json
import os

# rds settings
user_name = os.environ['USER_NAME']
password = os.environ['PASSWORD']
rds_proxy_host = os.environ['RDS_PROXY_HOST']
db_name = os.environ['DB_NAME']

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# create the database connection outside of the handler to allow connections to be
# re-used by subsequent function invocations.
try:
        conn = pymysql.connect(host=rds_proxy_host, user=user_name, passwd=password, db=db_name, connect_timeout=5)
except pymysql.MySQLError as e:
    logger.error("ERROR: Unexpected error: Could not connect to MySQL instance.")
    logger.error(e)
    sys.exit(1)

logger.info("SUCCESS: Connection to RDS for MySQL instance succeeded")

def lambda_handler(event, context):
    """
    This function creates a new RDS database table and writes records to it
    """
    message = event['Records'][0]['body']
    data = json.loads(message)
    CustID = data['CustID']
    Name = data['Name']

    item_count = 0
    sql_string = f"insert into Customer (CustID, Name) values(%s, %s)"

    with conn.cursor() as cur:
        cur.execute("create table if not exists Customer ( CustID  int NOT NULL, Name varchar(255) NOT NULL, PRIMARY KEY (CustID))")
        cur.execute(sql_string, (CustID, Name))
        conn.commit()
        cur.execute("select * from Customer")
        logger.info("The following items have been added to the database:")
        for row in cur:
            item_count += 1
            logger.info(row)
    conn.commit()

    return "Added %d items to RDS for MySQL table" %(item_count)
```

**注意**  
在此示例中，您的数据库访问凭证将存储为环境变量。在生产应用程序中，建议您将 [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html) 用作更安全的选项。请注意，如果 Lambda 函数位于 VPC 中，您需要创建 VPC 端点才能连接到 Secrets Manager。要了解更多信息，请参阅[如何在虚拟私有云中连接到 Secrets Manager 服务](https://aws.amazon.com/blogs/security/how-to-connect-to-aws-secrets-manager-service-within-a-virtual-private-cloud/)。

 要在函数代码中包含 PyMySQL 依赖关系，请创建一个 .zip 部署包。以下命令适用于 Linux、macOS 或 Unix：

**创建 .zip 部署包**

1. 将代码示例保存为名为 `lambda_function.py` 的文件。

1. 在创建 `lambda_function.py` 文件的同一目录中，创建一个名为 `package` 的新目录并安装 PyMySQL 库。

   ```
   mkdir package
   pip install --target package pymysql
   ```

1. 创建一个包含您的应用程序代码和 PyMySQL 库的 zip 文件。在 Linux 或 MacOS 中，运行以下 CLI 命令。在 Windows 中，使用您首选的压缩工具来创建 `lambda_function.zip` 文件。您的 `lambda_function.py` 源代码文件和包含依赖项的文件夹必须安装在.zip 文件的根目录下。

   ```
   cd package
   zip -r ../lambda_function.zip .
   cd ..
   zip lambda_function.zip lambda_function.py
   ```

   您也可以使用 Python 虚拟环境创建部署包。参阅[使用 .zip 文件归档部署 Python Lambda 函数](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-create-package-with-dependency)。

## 更新 Lambda 函数
<a name="vpc-rds-update-function"></a>

现在，您可以使用刚创建的 .zip 程序包，通过 Lambda 控制台更新 Lambda 函数。要支持函数访问数据库，您还需要使用访问凭证配置环境变量。

**更新 Lambda 函数**

1. 打开 Lambda 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面，然后选择您的函数 `LambdaFunctionWithRDS`。

1. 在**运行时系统设置**选项卡中，选择**编辑**，以将函数的**运行时系统**更改为 **Python 3.10**。

1. 将**处理程序**更改为 `lambda_function.lambda_handler`。

1. 在**代码**选项卡中，选择**上传自**，然后选择 **.zip 文件**。

1. 选择您在前一阶段创建的 `lambda_function.zip` 文件，然后选择**保存**。

现在，使用您之前创建的执行角色来配置该函数。这会授予该函数访问您的数据库实例和轮询 Amazon SQS 队列所需的权限。

**配置函数的执行角色**

1. 在 Lambda 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面中，选择**配置**选项卡，然后选择**权限**。

1. 在**执行角色**中，选择**编辑**。

1. 在**现有角色**中，选择您的执行角色（`lambda-vpc-sqs-role`）。

1. 选择**保存**。

**配置函数的环境变量**

1. 在 Lambda 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面中，选择**配置**选项卡，然后选择**环境变量**。

1. 选择**编辑**。

1. 要添加数据库访问凭证，请执行以下操作：

   1. 选择**添加环境变量**，然后为**键**输入 **USER\$1NAME**，并为**值**输入 **admin**。

   1. 选择**添加环境变量**，然后为**键**输入 **DB\$1NAME**，并为**值**输入 **ExampleDB**。

   1. 选择**添加环境变量**，然后对于**键**输入 **PASSWORD**，对于**值**输入您在创建数据库时选择的密码。

   1. 选择**添加环境变量**，然后对于**键**输入 **RDS\$1PROXY\$1HOST**，对于**值**输入您之前记下的 RDS 代理端点。

   1. 选择**保存**。

## 在控制台中测试 Lambda 函数。
<a name="vpc-rds-test-function"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/images/TUT_Lambda_step5.png)


您现在可以使用 Lambda 控制台测试您的函数。您可以创建一个测试事件，该事件模仿您在教程的最后阶段使用 Amazon SQS 调用函数时将收到的数据。您的测试事件包含一个 JSON 对象，该对象指定要添加到您的函数创建的 `Customer` 表中的客户 ID 和客户名称。

**测试 Lambda 函数**

1. 打开 Lambda 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面，然后选择一个函数。

1. 选择**测试**部分。

1. 选择**创建新事件**，然后输入 **myTestEvent** 作为事件名称。

1. 将以下代码复制到**事件 JSON** 中，然后选择**保存**。

   ```
   {
     "Records": [
       {
         "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
         "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
         "body": "{\n     \"CustID\": 1021,\n     \"Name\": \"Martha Rivera\"\n}",
         "attributes": {
           "ApproximateReceiveCount": "1",
           "SentTimestamp": "1545082649183",
           "SenderId": "AIDAIENQZJOLO23YVJ4VO",
           "ApproximateFirstReceiveTimestamp": "1545082649185"
         },
         "messageAttributes": {},
         "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
         "eventSource": "aws:sqs",
         "eventSourceARN": "arn:aws:sqs:us-west-2:123456789012:my-queue",
         "awsRegion": "us-west-2"
       }
     ]
   }
   ```

1. 选择**测试**。

在**执行结果**选项卡中，您应该看到与**函数日志**中显示的以下内容类似的结果：

```
[INFO] 2023-02-14T19:31:35.149Z bdd06682-00c7-4d6f-9abb-89f4bbb4a27f The following items have been added to the database:
[INFO] 2023-02-14T19:31:35.149Z bdd06682-00c7-4d6f-9abb-89f4bbb4a27f (1021, 'Martha Rivera')
```

## 创建 Amazon SQS 队列
<a name="vpc-rds-create-queue"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/images/TUT_Lambda_step6.png)


您已成功测试了您的 Lambda 函数和 Amazon RDS 数据库实例的集成。现在，您创建了 Amazon SQS 队列，用于在本教程的最后阶段调用 Lambda 函数。

**创建 Amazon SQS 队列（控制台）。**

1. 打开 Amazon SQS 控制台的[队列](https://console.aws.amazon.com/sqs/v2/home#/queues)页面，然后选择**创建队列**。

1. 将**类型**保留为**标准**，然后为您的队列名称输入 **LambdaRDSQueue**。

1. 保持所有默认选项处于选中状态，然后选择**创建队列**。

## 创建一个事件源映射以调用您的 Lambda 函数
<a name="vpc-rds-create-event-source-mapping"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/images/TUT_Lambda_step7.png)


[事件源映射](https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventsourcemapping.html)是 Lambda 中的一种资源，它从流或队列中读取项目并调用 Lambda 函数。配置事件源映射时，可以指定批处理大小，以便可以将来自您的流或队列的记录批处理成单个负载。在此示例中，您将批处理大小设置为 1，这样每次向队列发送消息时都会调用 Lambda 函数。您可以使用 AWS CLI 或 Lambda 控制台配置事件源映射。

**创建事件源映射（控制台）**

1. 打开 Lambda 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面，然后选择一个函数 (`LambdaFunctionWithRDS`)。

1. 在**函数概述**部分中，选择**添加触发器**。

1. 对于源，选择 **Amazon SQS**，然后选择队列的名称（`LambdaRDSQueue`）。

1. 对于**批处理大小**，输入 **1**。

1. 将所有其他选项设置为默认值，然后选择**添加**。

现在，您可以通过向 Amazon SQS 队列添加一条消息来测试您的完整设置。

## 测试和监控您的设置
<a name="vpc-rds-test-setup"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/images/TUT_Lambda_step8.png)


要测试您的完整设置，请使用控制台向 Amazon SQS 队列添加消息。然后，您可以使用 CloudWatch Logs 确认您的 Lambda 函数正在按预期将记录写入数据库。

**测试和监控您的设置**

1. 打开 Amazon SQS 控制台的[队列](https://console.aws.amazon.com/sqs/v2/home#/queues)页面，然后选择创建队列 (`LambdaRDSQueue`)。

1. 选择**发送和接收消息**，然后将以下 JSON 粘贴到**发送消息**部分的**消息正文**中。

   ```
   {
       "CustID": 1054,
       "Name": "Richard Roe"
   }
   ```

1. 选择**发送消息**。

   将您的消息发送到队列将导致 Lambda 通过您的事件源映射调用您的函数。要确认 Lambda 已按预期调用您的函数，请使用 CloudWatch Logs 验证该函数是否已将客户名称和 ID 写入您的数据库表。

1. 打开 CloudWatch 控制台的[日志组](https://console.aws.amazon.com/cloudwatch/home#logsV2:log-groups)页面，为您的函数选择日志组 (`/aws/lambda/LambdaFunctionWithRDS`)。

1. 在**日志流**部分中，选择最新的日志流。

   您的表应包含两条客户记录，每次调用您的函数都有一条记录。在日志流中，您应看到类似以下内容的消息：

   ```
   [INFO] 2023-02-14T19:06:43.873Z 45368126-3eee-47f7-88ca-3086ae6d3a77 The following items have been added to the database:
   [INFO] 2023-02-14T19:06:43.873Z 45368126-3eee-47f7-88ca-3086ae6d3a77 (1021, 'Martha Rivera')
   [INFO] 2023-02-14T19:06:43.873Z 45368126-3eee-47f7-88ca-3086ae6d3a77 (1054, 'Richard Roe')
   ```

## 清除资源
<a name="rds-tutorial-cleanup"></a>

除非您想要保留为本教程创建的资源，否则可立即将其删除。通过删除您不再使用的 AWS 资源，可防止您的 AWS 账户产生不必要的费用。

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择 **Actions** 和 **Delete**。

1. 选择 **Delete**。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择您创建的执行角色。

1. 选择**删除角色**。

1. 选择 **Yes, delete**（是，删除）。

**删除 MySQL 数据库实例**

1. 打开 Amazon RDS 控制台的 [Databases（数据库）页面](https://console.aws.amazon.com//rds/home#databases:)。

1. 选择您创建的数据库。

1. 依次选择 **Actions**（操作）和 **Delete**（删除）。

1. 清除 **Create final snapshot**（创建最终快照）复选框。

1. 在文本框中输入 **delete me**。

1. 选择**删除**。

**删除 Amazon SQS 队列**

1. 登录到 AWS 管理控制台 并打开 Amazon SQS 控制台，网址：[https://console.aws.amazon.com/vpc/](https://console.aws.amazon.com/sqs/)。

1. 选择创建的队列。

1. 选择 **Delete**（删除）。

1. 在文本框中输入 **delete**。

1. 选择 **Delete**。