0

我使用以下代码接受regsiter来自客户端的呼叫

.post('/regsiter', async (ctx) => {
  requestfrom = JSON.parse(JSON.stringify(ctx.request.body))
  let regxemail = /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
  let email = requestfrom.email
  let password = md5(requestfrom.password)
  if (regxemail.test(email)) {
    await userModel.checkemailexist([email])
        .then(async(result) => {
            if (result.length === 0) {
                console.log("insert email to database")
                await userModel.insertUser([email,password])
            } else {
                console.log("email exist")
            }
        })
      }
})

如果访问者的网络很好,这个功能会很好,但是如果访问者的网络很慢,没有足够的响应时间,它会向数据库中插入一些相同的数据,result.length总是===0,我该如何停止这个,任何想法?

4

1 回答 1

1

你有一个竞争条件。具体来说,如果您的节点服务器在等待您的insertUser()方法完成时收到重复请求,则第二个请求可能会发现该checkmailexist()方法返回 false。因此,两个并发请求处理器都将尝试插入相同的电子邮件值。当您的(不耐烦的)用户在网络速度较慢时单击刷新或再次单击提交按钮时,可能会发生这种情况。

换句话说,对相同值的两个并发请求email有时都可能对您的checkmailexist()函数产生错误。然后他们都会继续打电话insertUser()提及相同的事情email。所以你得到一个重复的记录。

数据库系统提供了多种方法来处理这个非常常见的问题。一种是在您的表上创建唯一索引,因此第二次尝试插入相同的值将引发错误。然后您的程序可以捕获并忽略重复键错误。

数据库系统也提供事务。您可以在查询数据库时开始事务并在执行插入后提交它,或者如果电子邮件已经存在则回滚它。这将使第二次调用checkemailexist()await 完成第一次检查/插入序列。在不知道您的方法中有什么的情况下,很难告诉您如何实现此类数据库事务。

MySQL 提供INSERT ... IGNOREINSERT ... ON DUPLICATE KEY UPDATE ...声明。这些与电子邮件列上的唯一键相结合,可让您处理 SQL 中的重复逻辑。

同样,这是一个常见问题。所有可扩展的数据库应用程序都必须处理它。

于 2018-07-26T22:13:10.230 回答