精华内容
下载资源
问答
  • 数据库里面的值重复怎么办?单独删一条删不掉 新建一个结构一样的空表,然后用旧表数据像新表里面插入ID字段,注意要group by 一下重复的数据 例如: insert into A_copy(id) ( select id fromA group by id) ...

    当数据库里面的值重复怎么办?单独删一条删不掉

    新建一个结构一样的空表,然后用旧表数据像新表里面插入ID字段,注意要group by 一下重复的数据  例如:

    insert into A_copy(id) ( select id from A group by id)

    添加完新表的ID后  然后根据ID更新成旧表的数据  即可

     

    注意:

    新表设置主键

    展开全文
  • 在单库单表时,业务 ID 可以依赖数据库的自增主键实现,现在我们把存储拆分到了多处,如果还是用数据库的自增主键,势必会导致主键重复。 那怎么办呢? 生成主键的常见方案 一个最直接的方案是使用单独的自增数据...

    在这里插入图片描述


    Question

    在单库单表时,业务 ID 可以依赖数据库的自增主键实现,现在我们把存储拆分到了多处,如果还是用数据库的自增主键,势必会导致主键重复。 那怎么办呢?

    在这里插入图片描述


    生成主键的常见方案

    一个最直接的方案是使用单独的自增数据表,存储拆分以后,创建一张单点的数据表,比如现在需要生成订单 ID,我们创建下面一张数据表:

    CREATE TABLE IF NOT EXISTS `order_sequence`(
    
       `order_id` INT UNSIGNED AUTO_INCREMENT,
    
       PRIMARY KEY ( `order_id` )
    
    )ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    

    当每次需要生成唯一 ID 时,就去对应的这张数据表里新增一条记录,使用返回的自增主键 ID 作为业务 ID。

    这个方案实现起来简单,但问题也很明显。

    • 首先,性能无法保证,在并发比较高的情况下,如果通过这样的数据表来创建自增 ID,生成主键很容易成为性能瓶颈。
    • 其次,存在单点故障,如果生成自增 ID 的数据库挂掉,那么会直接影响创建功能。

    在实际开发中,实现唯一主键有多种方案可选,下面介绍几种常见的实现思路,分别是使用 UUID、使用 Snowflake 算法,以及配置自增区间在内存中分配的方式。

    在这里插入图片描述


    UUID ? (不推荐)

    UUID 虽然很好地满足了全局唯一这个要求,但是并不适合作为数据库存储的唯一主键。我们输出一个 UUID 看一下,比如:135c8321-bf10-46d3-9980-19ba588554e8,这是一个 36 位的字符串。

    • 首先 UUID 作为数据库主键太长了,会导致比较大的存储开销
    • 其次 UUID 是无序的,如果使用 UUID 作为主键,会降低数据库的写入性能。

    以 MySQL 为例,MySQL 建议使用自增 ID 作为主键,我们知道 MySQL InnoDB 引擎支持索引,底层数据结构是 B+ 树,如果主键为自增 ID 的话,那么 MySQL 可以按照磁盘的顺序去写入;如果主键是非自增 ID,在写入时需要增加很多额外的数据移动,将每次插入的数据放到合适的位置上,导致出现页分裂,降低数据写入的性能。


    Snowflake

    Snowflake 是 Twitter 开源的分布式 ID 生成算法,由 64 位的二进制数字组成,一共分为 4 部分,下面是示意图:

    在这里插入图片描述

    • 第 1 位默认不使用,作为符号位,总是 0,保证数值是正数;

    • 41 位时间戳,表示毫秒数,我们计算一下,41 位数字可以表示 241 毫秒,换算成年,结果是 69 年多一点,一般来说,这个数字足够在业务中使用了;

    • 10 位工作机器 ID,支持 210 也就是 1024 个节点;

    • 12 位序列号,作为当前时间戳和机器下的流水号,每个节点每毫秒内支持 212 的区间,也就是 4096 个 ID,换算成秒,相当于可以允许 409 万的 QPS,如果在这个区间内超出了 4096,则等待至下一毫秒计算。

    https://github.com/twitter-archive/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala

    Snowflake 算法可以作为一个单独的服务,部署在多台机器上,产生的 ID 是趋势递增的,不需要依赖数据库等第三方系统,并且性能非常高,理论上 409 万的 QPS 是一个非常可观的数字,可以满足大部分业务场景,其中的机器 ID 部分,可以根据业务特点来分配,比较灵活。


    缺点:时钟回拨

    Snowflake 算法优点很多,但有一个不足,那就是存在时钟回拨问题,时钟回拨是什么呢?

    因为服务器的本地时钟并不是绝对准确的,在一些业务场景中,比如在电商的整点抢购中,为了防止不同用户访问的服务器时间不同,则需要保持服务器时间的同步。为了确保时间准确,会通过 NTP 的机制来进行校对,NTP(Network Time Protocol)指的是网络时间协议,用来同步网络中各个计算机的时间。

    如果服务器在同步 NTP 时出现不一致,出现时钟回拨,那么 SnowFlake 在计算中可能出现重复 ID。除了 NTP 同步,闰秒也会导致服务器出现时钟回拨,不过时钟回拨是小概率事件,在并发比较低的情况下一般可以忽略。关于如何解决时钟回拨问题,可以进行延迟等待,直到服务器时间追上来为止。

    在这里插入图片描述


    数据库维护区间分配

    下面介绍一种基于数据库维护自增ID区间,结合内存分配的策略,这也是淘宝的 TDDL 等数据库中间件使用的主键生成策略。

    步骤如下

    • 首先在数据库中创建 sequence 表,其中的每一行,用于记录某个业务主键当前已经被占用的 ID 区间的最大值。

    sequence 表的主要字段是 name 和 value,其中 name 是当前业务序列的名称,value 存储已经分配出去的 ID 最大值。

    CREATE TABLE `sequence` (
    
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Id',
    
      `name` varchar(64) NOT NULL COMMENT 'sequence name',
    
      `value` bigint(32) NOT NULL COMMENT 'sequence current value',
    
       PRIMARY KEY (`id`),
    
      UNIQUE KEY `unique_name` (`name`)
    
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
    
    
    • 接下来插入一条行记录,当需要获取主键时,每台服务器主机从数据表中取对应的 ID 区间缓存在本地,同时更新 sequence 表中的 value 最大值记录。

    现在我们新建一条记录,比如设置一条 order 更新的规则,插入一行记录如下:

    INSERT INTO sequence (name,value) values('order_sequence',1000); 
    
    

    当服务器在获取主键增长区段时,首先访问对应数据库的 sequence 表,更新对应的记录,占用一个对应的区间。比如我们这里设置步长为 200,原先的 value 值为 1000,更新后的 value 就变为了 1200。

    • 取到对应的 ID 区间后,在服务器内部进行分配,涉及的并发问题可以依赖乐观锁等机制解决。

    有了对应的 ID 增长区间,在本地就可以使用 AtomicInteger 等方式进行 ID 分配。

    不同的机器在相同时间内分配出去的 ID 可能不同,这种方式生成的唯一 ID,不保证严格的时间序递增,但是可以保证整体的趋势递增,在实际生产中有比较多的应用。

    为了防止单点故障,sequence 表所在的数据库,通常会配置多个从库,实现高可用。


    总结

    主要分享了实现唯一主键的几种思路,也就是我们通常说的分布式发号器,主要有使用 UUID、使用 Snowflake 算法,以及数据库存储区间结合内存分配的方式。

    现在再来总结一下,一个生产环境中可用的主键生成器,应该具备哪些特性呢?

    • 首先是生成的主键必须全局唯一,不能出现重复 ID,这对于主键来说是最基础的需求。

    • 其次,需要满足有序性,也就是单调递增,或者也可以满足一段时间内的递增,这是出于业务上的考虑。一方面在写入数据库时,有序的主键可以保证写入性能,另一方面,很多时候都会使用主键来进行一些业务处理,比如通过主键排序等。如果生成的主键是乱序的,就无法体现一段时间内的创建顺序。

    • 再一个是性能要求,要求尽可能快的生成主键,同时满足高可用。因为存储拆分后,业务写入强依赖主键生成服务,假设生成主键的服务不可用,订单新增、商品创建等都会阻塞,这在实际项目中是绝对不可以接受的。

    在这里插入图片描述

    展开全文
  • 当需要插入的数据比较少时,即可以通过一条一条的插入时,我们可以指定该列(id)的值,但是新插入的值不能和已有的值重复,而且必须大于其中最大的一个值 方式二: 当需要插入的数据量比较大时,需要时通过储存...

    在这里插入图片描述

    方式一:
    当需要插入的数据比较少时,即可以通过一条一条的插入时,我们可以指定该列(id)的值,但是新插入的值不能和已有的值重复,而且必须大于其中最大的一个值

    在这里插入图片描述

    方式二:
    当需要插入的数据量比较大时,需要时通过储存过程或者其他方式来批量生成数据插入时,显然方式一的方法不可行。那现在又该怎么办呢?

    有两种解决方法:
    
    1)可以把该列(id)的值设为null或者0,这样MySQL会自己做处理
    

    INSERT INTO user VALUES(NULL,‘bo’, 12,111111);
    INSERT INTO user VALUES(0,‘bo’, 12,111111);

    2)手动指定需要插入的列,但是不指定该列(id)

    ————————————————
    版权声明:本文为CSDN博主「lykion_881210」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/lykio_881210/article/details/80267500在这里插入图片描述

    展开全文
  • 初入学习 JDBC 操作数据库,想必大家都写过下面的代码: 数据库为:H2 如果需要处理特定 SQL 异常,比如 SQL 语句错误,这个时候我们应该怎么办? ...查看SQLException源码,...如重复主键错误码在MySQL中是 106...

    初入学习 JDBC 操作数据库,想必大家都写过下面的代码:

    数据库为:H2

    JDBC 操作数据库

    如果需要处理特定 SQL 异常,比如 SQL 语句错误,这个时候我们应该怎么办?

    查看 SQLException 源码,我们可以发现两个重要的方法。

    SQLException.getErrorCode:返回数据库特定的错误码,由数据库厂商制定,不同厂商错误码不同。如重复主键错误码在 MySQL 中是 1062,而在 Oracle 中却是 1。

    SQLException.getSQLState:返回 XOPEN 或 SQL:2003 制定的错误码规范。数据库厂商会将不同错误消息映射成同一个错误码

    所以我们可以根据 SQLException.getErrorCode 处理相应的数据库异常。

    JDBC 异常处理

    由于数据库厂商错误码不相同,这就导致如果我们更换数据库,上面判断逻辑就必须重写。

    下面我们使用 Spring 操作数据库。

    Spring 操作数据库

    Spring 数据库处理

    使用 Spring 之后,我们不再需要强制捕获异常。如果 SQL 语句运行存在异常,Spring 会抛出其内置特定的异常。如上面 SQL 语句异常将会抛出 BadSqlGrammarException。除了这个异常之外,Spring 还定义很多数据库异常。

    Spring 数据库异常

    每个 Spring 数据库异常的基类都是 DataAccessException。由于 DataAccessException 继承自 RuntimeException,所以在这类异常无需强制捕获。

    在 Spring 中使用 SQLExceptionTranslator 进行异常转换,默认的转换规则会根据 SQLException.getErrorCode 返回的错误码进行相应的转换。

    下面我们从源码分析转换过程。

    实现细节

    调试 JdbcTemplate 的源码。

    JdbcTemplate

    可以看到这里捕获了 SQLException,转换之后再将其抛出。

    整个转换过程,最后交给 SQLExceptionTranslator 进行转换。

    首先我们查看 SQLExceptionTranslator 类图。

    类图

    可以看到其实现了一个抽象类以及三个子类。

    抽象方法

    抽象类中会首先会使用子类转换,若未能转换成功,将会启动 fallback机制,再次转换,作为兜底。

    接着我们先看下三个子类的区别。

    SQLErrorCodeSQLExceptionTranslator:

    1. 默认转换类
    2. 主要根据 SQLException.getErrorCode 进行转换。
    3. 默认使用 SQLExceptionSubclassTranslator 作为 fallback 对象。

    SQLExceptionSubclassTranslator

    1. 基于 JDBC 的 SQLException 标准子类判断,如 java.sql.SQLTransientException
    2. 使用 SQLStateSQLExceptionTranslator 作为 fallback 对象。

    SQLStateSQLExceptionTranslator

    1. 基于 SQLException.getSQLState 规则判断。

    下面分析 SQLErrorCodeSQLExceptionTranslator ,其他两个比较类似,同学们可以自己看源码分析。

    SQLErrorCodeSQLExceptionTranslator 转换器主要根据 SQLException.getErrorCode 进行判断。Spring 默认在 org/springframework/jdbc/support/sql-error-codes.xml 归纳不同数据库厂商相关错误码。该配置文件会在第一次发生 SQL 异常时由 SQLErrorCodesFactory 进行加载,最后生成 SQLErrorCodes

    SQLErrorCodes

    另外在 SQLErrorCodes 提供扩展方法,可以根据错误码转换成自定义的异常。

    最后查看 SQLErrorCodeSQLExceptionTranslator 里的转换方法。

    SQLErrorCodeSQLExceptionTranslator

    前三个方法是 Spring 留下扩展方法,可以根据自己需求分别扩展。若都没有实现,将会根据错误码判断转换成具体的异常。

    默认转换规则

    自定义异常转换

    上面说到 Spring 总共给我们留下三处扩展点。

    1. 继承 SQLErrorCodeSQLExceptionTranslator,重写 customTranslate
    2. 继承 SQLExceptionTranslator,重写 translate,然后在 sql-error-codes.xml注入。
    3. 使用 SQLErrorCodes#customTranslations ,然后在 sql-error-codes.xml 配置相关错误码转换的规则。

    第三种方式改动最小,比较简单。首先在 classpath 下生成 sql-error-codes.xml,复制原有配置,最后配置 customTranslations 。

    customTranslations

    这里需要注意的是,需要转化的异常类型必须为 DataAccessException 子类。下面面我们自定义一个异常。

    自定义异常

    总结

    Spirng 异常处理将 SQL 异常转化成内置异常,屏蔽不同数据库返回码不一致的带来的问题。

    最后总结本文的知识点,希望帮助到大家。

    知识图谱

     如果有想学习java的程序员,可来我们的java学习扣qun:83078,3865,免费送java的视频教程噢!我每晚上8点还会在群内直播讲解Java知识,欢迎大家前来学习哦

    展开全文
  • 数据库的表中有自增长主键时(如图所示),我们该怎么插入数据呢? 方式一: 当需要插入的数据比较少时,即可以通过一条一条的插入时,我们可以指定该列(id)的值,但是新插入的值不能和已有的值重复,而且...
  • 数据库的表中有自增长主键时(如图所示),我们该怎么插入数据呢?方式一: 当需要插入的数据比较少时,即可以通过一条一条的插入时,我们可以指定该列(id)的值,但是新插入的值不能和已有的值重复,而且必须大于...
  • Litepal是一款Android上我感觉很好很好很好的数据库框架,用...不能对字段作出约束,主键也都是自增长的Id,那在插入数据的时候就可能会插入相同的数据,数据少还可以遍历一下对比有没有重复数据,那数据大了怎么办阿。
  •  但是如果数据库中的数据过大,那么int类型数据可能不够用,改怎么办呢?  在SQL Server中有一种数据类型UniqueIdentifier类型,该类型存储16字节的二进制值,为该列分配必须具有的唯一的标识符,因此该列的数值不...
  • mysql与hibernate的问题

    2012-02-10 13:13:23
    配置应该是没有问题的。但是我有这样的表user(string ...如果加了默认值,它却提示我的主键重复,查看数据库,它把默认值当了主键,把我在hibernate上当主键的覆盖了。这应该怎么办啊。小弟菜鸟啊,解决不了这个问题~
  • 初入学习 JDBC 操作数据库,想必大家都写过下面的代码: 数据库为:H2 如果需要处理特定 SQL 异常,比如 SQL 语句错误,这个时候我们应该怎么办? ...查看SQLException源码,...如重复主键错误码在MySQL中是 106...
  • 初入学习 JDBC 操作数据库,想必大家都写过下面的代码: 数据库为:H2 如果需要处理特定 SQL 异常,比如 SQL 语句错误,这个时候我们应该怎么办? 查看 SQLException 源码,...如重复主键错误码在 MySQL 中是 106...
  • 电商

    2018-05-25 14:53:00
    主键肯定不重复,并且由数据库自增 用户名不能重复 设为numque 时间戳 价格,商业计算中的高精度 20-2位整数,2位小数 购物车如果很多商品怎么办,怎么表示? 好像没有存数组的办法? ...
  • 在做一对多出现的问题,引发的思考:当数据库表中,主表的主键id和明细表的中的字段名相同时怎么办?Mybatis进行自动映射赋值的时候会不会出现异常? 注意:Mybatis中做多表联查的时候,不管是一对一、一对多、...
  • 当我们有做数据同步需求时,如果表中除了主键不能重复,还有其他字段比如名称不能重复,那么在做同步时可能会出现以下情况: A平台上数据已经删除,此时B平台的数据库还未同步,这时A平台新增了一个名称与原来一样的...
  • §3.2.8 当参数指定错误时怎么办? 70 §3.3 参数内容说明 70 §3.4 DBA常用参数说明 71 §3.4.1 跟踪文件路径(BACKGROUND_DUMP_DEST) 71 §3.4.2 在缓冲区驻留对象(BUFFER_POOL_KEEP) 71 §3.4.3 版本兼容...
  • 1.3.4 如果让你来设计一个支持数据库、NOSQL 和大数据之间数据实时流动的数据流及处理的系统,你会考虑哪些问题?如何设计? 1.3.5 给定一个整数数组和一个整数,返回两个数组的索引,这两个索引指向的数字的加和...
  • 44、一个几千万数据,发现数据查询很慢,怎么办? 55 六、Java高级部分 56 1、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 56 2、sleep() 和 wait() 有...
  • 分库分表之后,id 主键如何处理? 读写分离 如何实现 MySQL 的读写分离?MySQL 主从复制原理是啥?如何解决 MySQL 主从同步的延时问题? 高并发系统 如何设计一个高并发系统? 分布式系统 面试连环炮 ...

空空如也

空空如也

1 2
收藏数 23
精华内容 9
关键字:

数据库主键重复怎么办