Skip to the content.

TeamsApp LukcyDraw升级之路 之 DB Infra 篇

今天继续 LuckyDraw 的升级之路,之前在1500以上用户同时使用的时候,特别是在短时间内大家一起点击参与抽奖参的时候,服务会出现大量的错误,分析后发现,出现错误的原因基本都是 Azure Storage Table 返回的。当时使用 Azure Storage Table 的最最主要原因是它便宜,太便宜,真心便宜!缺点就是:

  • 要花大量的时间去设计数据表,从而在查询的时候能得到较好的效率
  • 不能跨表查询,不能join,这也就又回到第一点,需要花大量的时间去设计数据表的结构
  • 不支持很长的字段,需要分几列存储
  • 没有很好的测试库,不像EF Core,可以使用 in-memory db 来测试
  • 并发请求有上限,当短时间内有大量请求时,会报错。

最初设计的时候只是考虑了几百人一起抽奖的情况,也没有特别考虑到上千人的抽奖。

所以,这次决定改成 Azure SQL,原因是 Azure SQL 有最低最便宜的配置,也有超高配置,配置的scale up,scale down 非常方便,而且 Azure SQL 也有自动的备份机制,当出现问题的时候,可以很方便的回退版本。

说干就干,我分了 3 步来完成 DB 的 infra 升级。

第一步 增加 SQL Server 和 Database 资源

我先在 arm template 里增加 sql server 和 database 资源,下面是简化后的 arm 配置。

    {
      "type": "Microsoft.Sql/servers",
      "name": "[variables('sqlServerName')]",
      "apiVersion": "2019-06-01-preview",
      "properties": {
        "administratorLogin": "sqladmin",
        "administratorLoginPassword": "[parameters('databasePassword')]",
        "version": "12.0"
      },
      "resources": [ ... ]
    },
    {
      "type": "Microsoft.Sql/servers/databases",
      "name": "[concat(variables('sqlServerName'), '/main')]",
      "apiVersion": "2020-08-01-preview",
      "sku": {
        "name": "[parameters('sqlDatabaseSkuTier')]",
        "tier": "[parameters('sqlDatabaseSkuTier')]",
        "capacity": "[parameters('sqlDatabaseSkuCapacity')]"
      },
      "dependsOn": [
        "[variables('sqlServerName')]"
      ]
    },

可以看到我指定了数据库的 SKU,但没有写死,而是通过参与传入,这样我可以在我的 dev,uat 环境下使用较低的 basic DTU 5 的配置,这个最便宜。在生产环境使用较高的配置,来处理高并发的情况。

在上面的 resources 节点下,我增加了网络防火墙和管理员配置,如下:

        {
          "type": "firewallrules",
          "name": "AllowAllWindowsAzureIps",
          "apiVersion": "2014-04-01",
          "properties": {
            "endIpAddress": "0.0.0.0",
            "startIpAddress": "0.0.0.0"
          },
          "dependsOn": [
            "[concat('Microsoft.Sql/servers/', variables('sqlServerName'))]"
          ]
        },
        {
          "type": "administrators",
          "name": "ActiveDirectory",
          "apiVersion": "2014-04-01",
          "properties": {
            "administratorType": "ActiveDirectory",
            "login": "LuckyDrawEngineers",
            "sid": "[parameters('luckyDrawEngineersObjectId')]",
            "tenantId": "[subscription().tenantId]"
          },
          "dependsOn": [
            "[concat('Microsoft.Sql/servers/', variables('sqlServerName'))]"
          ]
        }

从上面可以看到,我首先允许 azure 的服务来访问这个数据库,这样的话,我的 lucky draw bot api就可以访问这个数据库了。

然后我增加了管理员,我在AzureAD 里创建了一个secuirity group,叫 LuckyDrawEngineers,这样如果有需要,我可以将我自己加入到这个 group,从而就有了对这个数据库的管理权限。

第二步 添加 long-term retention

默认情况下,数据库有 7 天备份,但是我想有更加老的数据备份,以防止一些突然灾难。在 azure portal 里直接上可以手动操作。

LuckyDrawApp

但是我是一个有代码控的人,这个也必须使用 ARM 来解决。

    {
      "type": "Microsoft.Sql/servers/databases/backupLongTermRetentionPolicies",
      "apiVersion": "2022-05-01-preview",
      "name": "[concat(variables('sqlServerName'), '/main/default')]",
      "dependsOn": [
        "[concat('Microsoft.Sql/servers/', variables('sqlServerName'))]",
        "[resourceId('Microsoft.Sql/servers/databases', variables('sqlServerName'), 'main')]"
      ],
      "properties": {
        "weeklyRetention": "P3W",
        "monthlyRetention": "P2M",
        "yearlyRetention": "P1M",
        "weekOfYear": 1
      }
    },

第三步 资源锁定

虽然我一般都不会手动去修改 azure 资源的配置,通常都是通过代码去新建删除资源,但为了防止不小心的人为删除数据库,我还是决定添加一个 lock,这个 lock 有两个级别:一个是防止修改,一个是防止删除。

LuckyDrawApp

操作比较简单,先进入 SQL server 资源,点击左边菜单的 Lock,然后点击 “Add” 按钮,输入一个名字,我这里叫它 avoid-deletion,然后 lock type 选择 delete,保存就好了。这样我就可以放心的使用了。

下一篇,我来讲讲我如何把 lucky draw bot api 代码升级,既能支持 azure storage table,同时也支持 SQL DB。

Written on December 17, 2022