今天继续 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 里直接上可以手动操作。
但是我是一个有代码控的人,这个也必须使用 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 有两个级别:一个是防止修改,一个是防止删除。
操作比较简单,先进入 SQL server 资源,点击左边菜单的 Lock,然后点击 “Add” 按钮,输入一个名字,我这里叫它 avoid-deletion,然后 lock type 选择 delete,保存就好了。这样我就可以放心的使用了。
下一篇,我来讲讲我如何把 lucky draw bot api 代码升级,既能支持 azure storage table,同时也支持 SQL DB。