抱歉我忽视了用户名如今已经不是单一的登陆查询和用户识别的依据了。
现在这个时代,登录的依据如此庞杂(手机、邮箱、用户名、各种第三方认证),并且一个人往往有多个登录入口,总不可能把每个人的数据重复存储多次……
我们首先认可这个原则没有改变:查询什么,就必须根据什么来分表。那么在这个原则之下,就只好采取二级分表的方法了。
先建立一个前级表格,记录所有的登录依据,所对应的用户ID。例如这样:
Criteria UserID
---------------------------
testuser1 1
testuser2 2
1@example.com 1
15200000001 1
**weibo_auth_data 2
对这个表进行分表的方法,仍然使用对关键词做hash的方式就不错。归根到底,无论用户名、邮箱、手机等各种登陆方法,本质无非是凭一个字符串查询用户ID,体现多对一的关系。
而真正存储用户信息的地方,就按用户的唯一标识分表就行了。
注意我没有用“用户ID”这个说法。因为在分表的情况下,还使用合表时适用的单一数字递增ID,恐怕就不是什么好主意了。有一个简单的结论:分表,自然是性能实在合不上了才要分。所以分布式的结构中,无法再包含集中式的结构。提问中所说的“单独组织一个用户ID总表”的办法,显然是行不通的。
我倾向于把用户的这个唯一标识做成一个二元组。
大的方面,用户注册时用Hash等任何算法,对用户的注册数据做个简单平摊,决定用户所在的桶编号iBucket。
小的方面,在每个单独的分表中按单一递增ID,决定用户在表内的局部编号iID。
查询用户的时候,一律用两个编号(iBucket, iID)决定一个用户。这样就既遵守了增加数据后才得到用户ID,又达到了分表的目的。
注意两点:
使用了Hash,别忘了一定要约束用户输入的最大长度,严防Hash冲撞攻击。
如果用户表真的大到了非分表不可的程度,实践中可能需要考虑采用NoSQL的数据库引擎,例如Redis。
哥们你真不嫌费劲吗?
分表的最大目的是为了高效查询,所以分表的依据,就必然和查询的关键字有直接的联系。从这个意义上看,除非你的用户用ID而不是用户名来登录,否则“按用户ID分表”本身就是一个馊的要死的主意。
我推荐的分表依据是对用户名做HASH,取HASH结果16进制串的头1位(或几位)。这样可以非常简单的把集合无限大的用户名,简单的分割成几个桶,同时各个桶之间无需维护就能达到非常好的均衡。