自增主键是否会降低数据库insert性能?如果会的话为什么还有很多公司采用?

首先自增主键在insert的时候,因为是自增,那么是不是要有一个自增值锁来防止这个自增值出现重复?有的话那岂不是会影响性能?

我观察过BAT以及国内很多大型互联网公司的网站,比如说京东的商品id,百度的用户id,新浪微博的用户id,贴吧的帖子id都是自增主键(长期观察可以发现他们随时间增长,应该是自增),当然也有反例,比如说QQ空间的说说(内部名称应该是mood,因为说说最早以前是由心情模块改来的)说说id用的就是类似于UUID这样的主键(其实格式很像md5)。

他们这样设计有什么考虑吗?(好处坏处都有哪些?)

阅读 14k
12 个回答

其他数据库没用过,不做评价。
对于MySQL来说,使用自增还是比较划算的,因为索引使用的底层数据结构(B+树)的原因,如果是自增ID的话,那么数据插入时最多就是引起节点的分裂,而如果是使用非自增ID,则有可能会导致节点移动和分裂。
如果使用MySQL又使用UUID作为主键的话,那么插入效率必然大幅下降。
当然,使用自增也会带来问题,就是日后如果要做水平切分的话,那么有可能会产生冲突的主键, 所以也可以选择不使用数据库的自增,而是自己生成全局唯一的递增整数作为主键。

京东的商品id,百度的用户id,新浪微博的用户id,贴吧的帖子id,这些应该不像某些流水单号瞬时插入的量比较大,使用自增 ID 可以方便开发吧。

  1. MySQL 中如果有自增字段的话,MySQL 会维护一个自增锁InnoDB 表引擎会在内存里保存一个计数器来记录 AUTO_INCREMENT 值。

  2. 当插入一个新行数据时,就会用一个表锁来锁住这个计数器,直到插入结束。

  3. 如果是一行一行的插入是没有问题的,但是在高并发情况下,表锁会引起 SQL 阻塞,极大的影响性能,还可能会达到 max_connections 值。

  4. MySQL 中数据文件本身是一棵 B+Tree,特别是对于 InnoDB 的数据文件本身就是索引文件,在物理上按照主键大小顺序存储。

  5. 非自增 ID 的话,没办法保证顺序插入,插入新记录时数据文件为了维持 B+Tree 的特性而频繁的分裂调整,效率很低

  6. 自增 ID 可以确保不会出现重复,大部分数据库都支持,使用非自增 ID 需要额外的代码实现。

参考搬运以下:
用mysql自增主键生成id研究
mysql的auto_increment详解

深入的东西不懂,说一点比较浅的东西
如果需要数据库自己保证自增的话,性能不好说,但是肯定会给数据库带来压力的
主键自增不一定要交给数据库,也可以有单独的id生成器,应用插入前先拿到id然后再执行插入

我这方面不太懂,但是感觉吧,如果我们手动来控制自增的话,那效率没有数据库自带来的快。

计算机的各个优化领域都面临取舍问题,利与弊的权衡选着...

先将添加请求生效在cache中,比如说redis,然后给用户显示正常,然后将这些sql修改放入到一个队列中存储起来,队列满或者每隔一段时间,合并为一个执行。并将innodb_autoinc_lock_mode设置为1。不一定能解决问题,但是这是个思路。

谢邀。

据说数据库自带的查询或者某些操作都是效率比自己去处理高非常多的

是自增主键的目的是为了保持唯一性,使用主键一般并不会降低数据库的insert性能,如果使用主键都降低性能,那其他的操作就更不用说了。如果使用主键导致性能不好,一般社区核心开发人员都会解决,不需要我们担心。

总的来说,使用自增主键的原因就是开发简单,一般情况下不会成为性能瓶颈。

主键为了保证不重复,如果不用GUID类似的机制,必须有一个集中的控制点,目前关系型数据库大部分都支持自增字段的特性进行控制,对开发来说比较简单,且对性能影响较小(Oracle的序列可以设置缓存,提前取出一批的序号,因此批量插入的时候并不是每插入一条就需要计算序号,mysql不知道是否有类似的机制)。

当然,这个控制点也可以放到应用层进行控制,像 @疯狂的爱因斯坦 所说的那样,自己定制一套序列号生成的方法。

但从数据库的角度来说的话,mysql的高票已经解释过了,本身也并不慢(单独建自增表性能更好)。从架构来说的话(题主你不会认为BAT京东这几家的后面就是个数据库在抗吧),发号经常是单独的一个服务,看上去是整数或者看上去是UUID都不能代表内部的机制

单中心的自增常见的优化可以做跳步之类,也可以单中心多buffer,性能可以逼近分布式。分布式的发号的话就是设计算法保证不重复了,经典的可以看mongodb的ObjectId算法

你说的那几个公司肯定不是用主键自增的, 因为太low了, 用主键自增的一般就是小系统+单库
牛逼点的都是自己写一套逻辑, 比如加一些前后缀什么的, 优点就是定制化强而且可以限制长度,缺点就是需要能写出完全随机且支持高并发的程序, 那肯定就有用到pool的一些机制, 以及如何down机的持久化问题, 一般人写不出.
一般点儿的比如我就是就直接用java的uuid方法去生成, 优点就是简单,很轻松的就支持了分库分表, 缺点当然就是长度太长, 占空间大, 且无规律(如果你想要规律的话).
tips: 再告诉你一个, 主键和订单号是不一样的, 主键是与业务无关, 而订单号是与业务相关的, 所以不可能完全避免不去写这种生成唯一id的程序, 虽然很难, 订单号可以与时间戳相关加一些随机的后缀(uuid随机截取4位)来实现, 我目前是这么干的

你看上去像是自增的,但实际它并不一定用的是自增主键啊。
自增主键意味着只有一个库一个表。按那些公司的业务量,
比如百度贴吧的帖子的数量,那是亿以上的级别,不可能是一个表,肯定会拆库。

"依赖数据库自增机制达到全局ID唯一
使用如下语句:
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();"
可以参考一下我以前在知乎写的。
https://www.zhihu.com/questio...

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏