我有一个网络应用程序。在其中处理表单的流程如下:
- 证实
- 列出错误或插入/更新数据
在这个特定的场景中,我正在开发一个用户注册过程,但我试图为所有类型的表单找到一个通用的解决方案,基于检查数据库表中唯一值的可用性。
在此用户注册中,用户的登录名必须是唯一的。在验证阶段,应用程序检查其在数据库表中的可用性,如果可用则插入一行。还有其他字段也必须验证,例如密码和密码确认。所有验证在一个 HTTP 请求中发生一次。
问题是我不能确定在应用程序检查其可用性之后,在第一个用户的进程插入它之前,它不会被另一个用户在并行进程中使用。我知道两个用户在同一毫秒内输入相同登录名的可能性非常小,但有一天可能会出现这种情况,即数千名用户同时向某个表单输入数据。
如果验证已经通过,用户不应该看到一些错误消息说他的登录已经注册。
我要解决的问题是确保唯一值在检查其可用性之后并将其插入一个HTTP 请求之前可用。另一个用户注册了相同的唯一登录名,而第一个用户弄乱了他的密码并且密码确认不一样,这没关系。
使用现有行可以轻松解决此问题,因为我可以 SELECT 它 FOR UPDATE 并且它将在事务期间被锁定。但是我不能对不存在的行做同样的事情。那就是问题所在。我该如何解决这个问题?
以下是我知道的一些解决方案。我不确定其中哪一个是最好的。另外,我不确定我知道最好的方法,所以请分享你知道的方法。
表锁定
过去我已经用表锁定解决了这个问题,但我不确定这是不是最好的方法。过程是这样的:
- 锁定表以进行写入
- 检查可用性
- 返回错误或插入行
- 解锁桌子
有人说锁定整个表是最糟糕的解决方案。也许是这样,但这是我自己想出的唯一可行的方法。
锁只在一个 HTTP 请求期间保留,当然不会在几个请求之间保留。
插入并捕获错误
这种方式是其他一些人向我建议的。他们建议将该列设为唯一索引列,并在两个阶段分别验证和检查唯一性。过程是这样的:
- 验证数据
- 如果验证成功,则插入该行
- 如果插入行失败显示唯一值不可用的错误
当然,我已将该列设为唯一索引列。但这并不意味着我想使用数据库的能力在验证时抛出错误;它应该在应用程序级别完成。
我不喜欢这种方式,因为我不喜欢这种情况下的 try-and-catch-an-exception 方式,因为在检查值的可用性并插入它的过程中没有任何异常。我认为它应该采用检查和保留和插入的方式。我认为验证用户输入不应该基于异常,因为用户输入错误并没有什么异常。
我可能错了,但这是我目前的观点。如果你认为我明显错了,请告诉我为什么。