【发布时间】:2020-02-03 03:28:56
【问题描述】:
我有一个 MariaDB 表 users,大致如下:
id INT PRIMARY KEY AUTOINCREMENT,
email_hash INT, -- indexed
encrypted_email TEXT,
other_stuff JSON
出于隐私原因,我无法在数据库中存储实际的电子邮件。
用于电子邮件的加密不是一对一的,即一封电子邮件可以被加密为许多不同的加密表示。这使得在 encrypted_email 列上添加索引毫无意义,因为它永远不会捕获重复项。
数据库中已有数据,更改加密方式或散列方式是不可能的。
email_hash 列也不能有唯一索引,因为它应该是一个短散列来加速重复检查。它不能太独特,因为它会使所有隐私保证无效。
如何防止两个具有相同电子邮件的条目出现在数据库中?
另一个限制:根据文档https://mariadb.com/kb/en/library/lock-tables/,我可能无法使用LOCK TABLE
LOCK TABLES在使用 Galera 集群时不起作用。与 Galera 一起使用时,您可能会遇到崩溃或锁定。
LOCK TABLESimplicitly 提交活动事务,如果有的话。此外,启动事务总是会释放使用LOCK TABLES获取的所有表锁。
(我确实使用 Galera 并且我确实需要事务,因为插入新用户会伴随着其他几个插入和更新)
由于后端应用程序服务器(单体)只要不存储个人信息(例如用于发送电子邮件、验证登录等),就可以处理个人信息,因此我在应用程序中进行了重复检查。
目前,我正在做这样的事情(伪代码):
perform "START TRANSACTION"
h := hash(new_user.email)
conflicts := perform "SELECT encrypted_email FROM users WHERE email_hash = ?", h
for conflict in conflicts :
if decrypt(conflict) == new_user.email :
perform "ROLLBACK"
return DUPLICATE
e := encrypt(new_user.email)
s := new_user.other_stuff
perform "INSERT INTO users (email_hash, encrypted_email, other_stuff) VALUES (?,?,?)", h, e, s
perform some other inserts as part of the transaction
perform "COMMIT"
return OK
如果两次尝试及时分开,效果很好。但是,当两个线程尝试同时添加同一个用户时,两个事务并行运行,执行选择,查看没有冲突的重复项,然后都继续添加用户。如何防止这种情况发生,或者至少优雅地立即恢复?
这是比赛的样子,简化了:
-
两个线程开始他们的事务
-
两个线程都执行选择,并且选择在两种情况下都返回零行。
-
两个线程都假设不会有重复。
-
两个线程都添加了用户。
-
两个线程都提交事务。
-
现在有两个用户使用相同的电子邮件。
【问题讨论】:
-
other stuff中是否有任何内容可能有助于唯一地识别个人(即使他们只能将其拉到少数匹配项而不能完全唯一地识别他们)?我的想法是,如果这可行,您可以检查所有匹配项...... -
@user2366842 没有。至于“把它拉到少数匹配”,这就是哈希的用途。
-
“一封电子邮件可以被加密为许多不同的加密表示”——也许你的意思正好相反?
-
@RickJames 不。我的意思是每封电子邮件都可以使用不同的初始化向量甚至不同的密钥进行加密,因此我无法检测到仅比较加密数据的重复电子邮件。
-
听起来您已经无法解密。正如其他人所说,单向哈希可能是一种更好的去重方法。
标签: mysql sql indexing mariadb