最近在网上看到了一个关于“mysql分表后如何插入新数据”的问题,对一个用户表进行分表,他的分表规则是按用户id来分的,但是当有一条新数据要插入时,还没有用户id,因此不知道插入哪一张分表中(因为只有当数据插入后才会有用户id)。
我的想法是这样的:专门建一张表,用来存放所有用户id,也就是说当有新数据插入时,先在这张表中产生一条记录,这条记录的主键就作为新用户id,然后再根据这个用户id来决定这个用户的注册数据插入到哪一张分表中。
这类问题,不知道大家是怎么处理的,想听听在家的做法……
回复内容: 最近在网上看到了一个关于“mysql分表后如何插入新数据”的问题,对一个用户表进行分表,他的分表规则是按用户id来分的,但是当有一条新数据要插入时,还没有用户id,因此不知道插入哪一张分表中(因为只有当数据插入后才会有用户id)。
我的想法是这样的:专门建一张表,用来存放所有用户id,也就是说当有新数据插入时,先在这张表中产生一条记录,这条记录的主键就作为新用户id,然后再根据这个用户id来决定这个用户的注册数据插入到哪一张分表中。
这类问题,不知道大家是怎么处理的,想听听在家的做法……
抱歉我忽视了用户名如今已经不是单一的登陆查询和用户识别的依据了。
现在这个时代,登录的依据如此庞杂(手机、邮箱、用户名、各种第三方认证),并且一个人往往有多个登录入口,总不可能把每个人的数据重复存储多次……
我们首先认可这个原则没有改变:查询什么,就必须根据什么来分表。那么在这个原则之下,就只好采取二级分表的方法了。
先建立一个前级表格,记录所有的登录依据,所对应的用户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位(或几位)。这样可以非常简单的把集合无限大的用户名,简单的分割成几个桶,同时各个桶之间无需维护就能达到非常好的均衡。
楼上的写的太复杂了吧。。。其实简单直白有效的做法就跟楼主写的差不多,单独用一张步长表,如果你分两个表的话,步长表的步长为2,表1从0开始每次加步长2,表2从1开始每次加步长2,这样就会取到表1和2各自的id又不会冲突,然后把这个生成的id记录到步长表中,然后每次算id的时候取出步长表的id加上步长,然后再把新的id写入到步长表中,拿到这个id后你就可以插入数据分表中了。