我正在开发LAMP(Linux + Apache + MySQL + PHP),因为我记得自己。但是有一个问题困扰我多年了。我希望你能帮我找到答案并指出正确的方向。这是我的挑战:
说,我们正在创建一个社区网站,我们允许用户注册。我们存储所有用户的MySQL表看起来像这样:
CREATE TABLE `users` (
`uid` int(2) unsigned NOT NULL auto_increment COMMENT 'User ID',
`name` varchar(20) NOT NULL,
`password` varchar(32) NOT NULL COMMENT 'Password is saved as a 32-bytes hash, never in plain text',
`email` varchar(64) NOT NULL,
`created` int(11) unsigned NOT NULL default '0' COMMENT 'Timestamp of registration',
`updated` int(11) unsigned NOT NULL default '0' COMMENT 'Timestamp of profile update, e.g. change of email',
PRIMARY KEY (`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
因此,从这个片段中你可以看到我们为每个新用户'uid'字段都有一个唯一的自动递增。与每个良好和忠诚的社区网站一样,如果用户想要取消他们在社区中的参与,我们需要为用户提供完全删除其个人资料的可能性。
这是我的问题。假设我们有3个注册用户:Alice(uid = 1),Bob(uid = 2)和Chris(uid = 3)。现在鲍勃想要删除他的个人资料并停止使用我们的社区。如果我们从'users'表中删除Bob的个人资料,那么他缺少的'uid'将创建一个永远不会再填充的空白。在我看来,这是对uid的巨大浪费。我在这里看到了3个可能的解决方案:
1)将我们表中'uid'字段的容量从SMALLINT(int(2))增加到例如BIGINT(int(8)),并忽略一些uid将被浪费的事实。
2)引入新字段'is_deleted',它将用于标记已删除的配置文件(但将它们保留在表中,而不是删除它们),以便为新注册的用户重新使用它们的uid。该表看起来像这样:
CREATE TABLE `users` (
`uid` int(2) unsigned NOT NULL auto_increment COMMENT 'User ID',
`name` varchar(20) NOT NULL,
`password` varchar(32) NOT NULL COMMENT 'Password is saved as a 32-bytes hash, never in plain text',
`email` varchar(64) NOT NULL,
`is_deleted` int(1) unsigned NOT NULL default '0' COMMENT 'If equal to "1" then the profile has been deleted and will be re-used for new registrations',
`created` int(11) unsigned NOT NULL default '0' COMMENT 'Timestamp of registration',
`updated` int(11) unsigned NOT NULL default '0' COMMENT 'Timestamp of profile update, e.g. change of email',
PRIMARY KEY (`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
3)编写脚本,以便在删除上一条记录后移动所有后续用户记录。例如。在我们的情况下,当Bob(uid = 2)决定删除他的个人资料时,我们会将他的记录替换为Chris的记录(uid = 3),这样Chris的uid就会变为2并且标记(is_deleted ='1')克里斯作为新用户空缺的旧记录。在这种情况下,我们根据注册时间保持uid的时间顺序,以便较旧的用户具有较低的uid。
请告诉我现在哪种方法是处理auto_increment字段中的差距的正确方法。这只是用户的一个例子,但在我的编程经验中经常出现这种情况。
提前致谢!
答案 0 :(得分:2)
绝对不是移动用户ID的想法 - 这会在某些时候杀死你或你的mysql服务器。 假设你有1,000,000个用户,用户2被删除了 - 你必须将999,999个记录一个向下移动......就像查询一样简单,它仍会锁定你的数据库一段时间。 另外我认为你的auto_increment值会在每个表的每个插页上设置。 插入 - > AI + 1 - >插入 - > AI + 1 - >删除 - > AI保持不变...如果您要移动所有ID,则下一个auto_increment值仍然是1,000,001,这将使1,000,000为空。
我说无符号BIGINT并忽略它 - 因为如果你接近bigint的极限,你还有许多其他问题需要解决;)
答案 1 :(得分:1)
我编写了一个简单的PHP函数来“填充”由“删除”查询引起的auto_increment间隙,并设置正确的“next auto_increment”值。
function mysql_fix_aigap($table,$column){
$fix_aigap=1;
$query_results=mysql_query("select * from $table");
while($row=mysql_fetch_array($query_results)){
mysql_query("update $table set `$column`='$fix_aigap' where `$column` like {$row[$column]};");
$fix_aigap=$fix_aigap+1;
}
mysql_query("alter table `$table` AUTO_INCREMENT =$fix_aigap");
}
并将其命名为:
mysql_fix_aigap("gapped_table_to_be_fixed","column"); //"users" and "uid" in your case.
(此脚本假定您已连接到服务器并且已选择数据库!)
这是技术上的答案。
在我的诚实意见中,我不建议为用户名分配“变量”uid,这是非精神分裂的方式! (ID =标识)
吨。
答案 2 :(得分:0)
首先;为什么你认为这是uid的“浪费”?我的意思是,它只是一个整数(或BIGINT),这不再是70年代了。
其次,如果您实施一个建议的选项,您获得的性能损失远远大于您从“浪费”uid中获得的空间损失。如果某个用户删除了他的个人资料,最糟糕的是,在他之后注册的每个用户都会获得一个新的ID,所以你必须更新非常非常多的记录......
我必须承认,当我刚刚开始编程时,我记得必须习惯自动增量列中的间隙。但你必须接受它们,继续前进,让它们存在......
答案 3 :(得分:0)
我只是忽略了这些差距,并确保您拥有所需的大范围的ID。差距没有真正的伤害。试图通过更新数据来修复它们可能会引入更麻烦的破坏关系。
顺便提一句,MySQL INT(2)
中的2指定maximum display width,但不会影响存储量。 INT(8)
使用与INT(2)
相同的存储空间 - 正如您所暗示的那样使用BIGINT。
答案 4 :(得分:0)
unsigned int的最大值是4,294,967,295。目前的互联网人口约为18亿人。我建议您使用unsigned int作为目的,不要担心序列中的间隙。
在哲学上:Donald Knuth曾经说过“我们应该忘记效率很低,大约97%的时间说:过早的优化是所有邪恶的根源。”