MySQL创建表的时候可以不设置主键吗?
MySQL创建表的时候是可以不主动设置主键的,但是表是一定需要一个主键的,MySQL会主动将第一个不为null的唯一索引设置为主键
为什么MySQL推荐使用自增id作为主键?
mysql官方推荐不要使用uuid或者不连续不重复的雪花作为主键,而是使用连续自增的主键id
使用自增id的内部结构
自增id的值是顺序的,所以innodb在索引B+树的叶子节点层面可以直接把每一条记录都存储在上一条记录的后面,当达到页面的最大填充因子的时候(页面容量已经满了)下一条记录就会写入新的页中,数据按照这种顺序的方式进行填充,主键页就会以近乎于顺序的记录填满,提升了页面的最大填充率,不会有页的浪费
新插入的行一定会在原有的最大数据行下一行,MySQL定位和寻址非常快,不会因为计算而做出额外的消耗,
并且能够减少页分裂和碎片的产生
页分裂:保证后一个数据页的所有行主键值比前一个数据页的主键值大,所以当ID不为自增的主键的时候,就会导致后一个页的所有行并不一定比前一个数据页的行的id大。这时就会触发页分裂的逻辑,对两个页之间的数据进行调整,甚至会涉及到多个数据页,导致性能降低
使用自增id的缺点
- 别人一旦爬取你的数据库,就可以根据数据库的自增id获取到你业务的增长信息,从而分析出经营情况
- 对于高并发的负载,innodb在按照主键进行插入的时候会造成明显的锁争用,auto_increment锁机制会造成自增锁的争夺,有一定的性能损失
为什么分布式系统不用自增id,而是要用雪花算法生成id
分布式id创建的业务需求
- 全局唯一
- 趋势递增 innodb引擎的叶子结点是有序的双向链表,趋势递增可以增加性能,不会打乱树的结构
- 信息安全
- 最好包含时间戳
为什么自增id不适合分布式系统?
当数据庞大的时候,在数据库分库分表之后,数据库自增id不能满足唯一id来表示数据;因为每个表都按照自己的节奏自增,会造成id冲突,从而无法满足需求
使用auto_increment实现廉价的分布式唯一主键
flickr有类似的方案,构建是一个专用的数据库服务器,上面只有一个数据库,在数据库里面有用于32位id和64位id的id表,id是auto自增的,所有数据库生成id都会向这个服务器发请求,然后服务器分发id下去,也能达到一种分布式唯一主键的效果
类似于session-redis的思想,把所有的sessionid都存在redis里面,所有的服务器实例在比较cookie的时候就先去redis里面比较,这样就能避免因为负载均衡导致的cookie失效问题
当然这个廉价的做法显然是有很大问题的
- 并发量很小,因为只有一台服务器
- 增加开销,并且整个请求流程变慢,因为需要向服务器发请求,并且是在硬盘层面进行操作的
- flickr服务器成了整个系统的瓶颈和隐患,如果服务器宕机整个系统直接崩掉了
雪花算法
是twitter开源的分布式id生成算法,结果是一个64位的longint类型,核心思想是用41位来作为时间戳,10位来作为机器的id,12位作为毫秒内的流水号(意味着每个节点可以在每毫秒生成4096个id),最后还有一个符号为永远为0
优点
- 完全在内存生成,高性能高可用
- 容量大,每秒可以生成几百万id
- 趋势递增,插入数据库索引树的时候,性能比较高
缺点
- 依赖系统时钟的一致性,如果某台机器的系统时钟回拨,有可能造成id冲突
- 多台机器的ID只能保证趋势增加,即每一台机器都能保证这台机器生成的ID是在增加的,但是多台机器并不一定绝对递增
- 41位时间戳只能保证69年无重复ID
- 因为是64位的ID,在传递给前端的时候需要用字符串的类型进行传递,因为js的number类型最大只支持53位
其他分布式ID方案
- UUID:JAVA自带的API,生成一个唯一性的字符串,不能保证有序递增
- UidGenerator:百度开源的分布式ID生成器,基于雪花算法
- Leaf:美团开源的分布式ID生成器,能保证全局唯一,趋势递增,但是需要依赖关系数据库、Zookeeper等中间件