精华内容
下载资源
问答
  • 新增数据时使用序列比较麻烦,不易于迁移 ...//主键定义为varchar类型 <selectKey resultType="java.lang.String" keyProperty="主键id" order="BEFORE"> SELECT SYS_GUID() FROM dual </selectKey> ...

    新增数据时使用序列比较麻烦,不易于迁移

    SELECT SYS_GUID() FROM dual
    
    //主键定义为varchar类型
    <selectKey resultType="java.lang.String" keyProperty="主键id" order="BEFORE">
    	SELECT SYS_GUID() FROM dual
    </selectKey>
    
    展开全文
  • 前言 主要使用到的技术:...可以向数据库中增加数据,但是主键id是一个长串的随机数。 二、问题分析 新创建一个数据库表,然后手动在数据库表中添加数据时,字段的id值是正常的。但是只要一经过mybatisPlus操...

    前言

    主要使用到的技术:springboot、mybatisPlus、mybatisPlus的代码生成…
    要进行的操作:通过mybatisPlus代码生成器生成代码,然后再向数据库增加数据

    一、遇到的问题

    可以向数据库中增加数据,但是主键id是一个长串的随机数。
    在这里插入图片描述

    二、问题分析

    新创建一个数据库表,然后手动在数据库表中添加数据时,字段的id值是正常的。但是只要一经过mybatisPlus操作添加数据到数据库就会出现这种问题。那么可能就是生成代码时出了问题

    三、解决问题

    1、根据数据库表生成的domain类。问题所在:在代码生成器生成代码时,忘记了数据库表的主键id自增

    在这里插入图片描述

    2、而将数据库表删除后重新建表后,及时将主键id设置为自增,然后再操作代码生成器生成的domain类代码。

    很明显多了个注解,而此时就可以正常添加数据到数据库表中了
    在这里插入图片描述

    展开全文
  • 国产神通数据库创建表时主键的分类 以下是基于springboot jpa去分析的,由于未找到spring.jpa.properties.hibernate.dialect配置相关的类,所以用了mysql和oracle的方言去展示 1、使用UUID创建方言,此时主键为字符...

    神通数据库创建表时主键的分类

    • 以下是基于springboot jpa去分析的,由于未找到spring.jpa.properties.hibernate.dialect配置相关的类,所以用了mysql和oracle的方言去展示
    • 1、使用UUID创建方言,此时主键为字符串类型,与方言联系不是特别密切,配置oracle或者mysql方言都行
    • 实体类中
    @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id", unique = true, nullable = false)
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
    • 2、使用Long类型,mysql方言展示
    • 实体类中
    private Long id;
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name="ID",unique=true,nullable=false)
        public Long getId() {
            return id;
        }
        public void setId(Long id) {
            this.id = id;
        }
    
    • -配置文件application.properties中
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
    
    • 数据库中给ID列绑定序列.先创建序列,然后通过数据库可视化界面或者编写sql语句创建。结果如下:
    • 表中id会随着序列号去变化,缺点是插入的时候,调用jpaRepository.save()方法时,返回对象id错误,是个固定值,与数据库中id不符合
    • 3、使用Long类型,oracle方言展示
    • 实体类中
    private Long id;
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name="ID",unique=true,nullable=false)
        public Long getId() {
            return id;
        }
        public void setId(Long id) {
            this.id = id;
        }
    

    或者

    private Long id;
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name="ID",unique=true,nullable=false)
        public Long getId() {
            return id;
        }
        public void setId(Long id) {
            this.id = id;
        }
    
    • -配置文件application.properties中
    spring.jpa.properties.hibernate.dialect=oorg.hibernate.dialect.Oracle10gDialect
    
    • 数据库中会自动创建序列
    • 结果如下:
      表中id会随着序列号去变化。缺点是多张表所共用一个序列,这个序列为hibernate_sequence序列
    展开全文
  • 一、前言在日常开发中,数据库主键id的生成方案,主要有三种数据库自增ID采用随机数生成不重复的ID采用jdk提供的uuid对于这三种方案,我发现在数据量少的情况下,没有特别的差异,但是当...

    一、前言

    在日常开发中,数据库中主键id的生成方案,主要有三种

    • 数据库自增ID

    • 采用随机数生成不重复的ID

    • 采用jdk提供的uuid

    对于这三种方案,我发现在数据量少的情况下,没有特别的差异,但是当单表的数据量达到百万级以上时候,他们的性能有着显著的区别,光说理论不行,还得看实际程序测试,今天就带着大家一探究竟!

    二、程序实例

    首先,我们在本地数据库中创建三张单表tb_uuid_1tb_uuid_2tb_uuid_3,同时设置tb_uuid_1表的主键为自增长模式,脚本如下:

    CREATE TABLE `tb_uuid_1` (
      `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='主键ID自增长';
    
    CREATE TABLE `tb_uuid_2` (
      `id` bigint(20) unsigned NOT NULL,
      `name` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='主键ID随机数生成';
    
    CREATE TABLE `tb_uuid_3` (
      `id` varchar(50)  NOT NULL,
      `name` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='主键采用uuid生成';
    

    下面,我们采用Springboot + mybatis来实现插入测试。

    2.1、数据库自增

    以数据库自增为例,首先编写好各种实体、数据持久层操作,方便后续进行测试

    /**
     * 表实体
     */
    public class UUID1 implements Serializable {
    
        private Long id;
    
        private String name;
      
      //省略set、get
    }
    
    /**
     * 数据持久层操作
     */
    public interface UUID1Mapper {
    
        /**
         * 自增长插入
         * @param uuid1
         */
        @Insert("INSERT INTO tb_uuid_1(name) VALUES(#{name})")
        void insert(UUID1 uuid1);
    }
    
    /**
     * 自增ID,单元测试
     */
    @Test
    public void testInsert1(){
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            uuid1Mapper.insert(new UUID1().setName("张三"));
        }
        long end = System.currentTimeMillis();
        System.out.println("花费时间:" +  (end - start));
    }
    

    2.2、采用随机数生成ID

    这里,我们采用twitter的雪花算法来实现随机数ID的生成,工具类如下:

    public class SnowflakeIdWorker {
    
        private static SnowflakeIdWorker instance = new SnowflakeIdWorker(0,0);
    
        /**
         * 开始时间截 (2015-01-01)
         */
        private final long twepoch = 1420041600000L;
        /**
         * 机器id所占的位数
         */
        private final long workerIdBits = 5L;
        /**
         * 数据标识id所占的位数
         */
        private final long datacenterIdBits = 5L;
        /**
         * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
         */
        private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
        /**
         * 支持的最大数据标识id,结果是31
         */
        private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
        /**
         * 序列在id中占的位数
         */
        private final long sequenceBits = 12L;
        /**
         * 机器ID向左移12位
         */
        private final long workerIdShift = sequenceBits;
        /**
         * 数据标识id向左移17位(12+5)
         */
        private final long datacenterIdShift = sequenceBits + workerIdBits;
        /**
         * 时间截向左移22位(5+5+12)
         */
        private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        /**
         * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
         */
        private final long sequenceMask = -1L ^ (-1L << sequenceBits);
        /**
         * 工作机器ID(0~31)
         */
        private long workerId;
        /**
         * 数据中心ID(0~31)
         */
        private long datacenterId;
        /**
         * 毫秒内序列(0~4095)
         */
        private long sequence = 0L;
        /**
         * 上次生成ID的时间截
         */
        private long lastTimestamp = -1L;
        /**
         * 构造函数
         * @param workerId     工作ID (0~31)
         * @param datacenterId 数据中心ID (0~31)
         */
        public SnowflakeIdWorker(long workerId, long datacenterId) {
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            if (datacenterId > maxDatacenterId || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }
        /**
         * 获得下一个ID (该方法是线程安全的)
         * @return SnowflakeId
         */
        public synchronized long nextId() {
            long timestamp = timeGen();
            // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(
                        String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }
            // 如果是同一时间生成的,则进行毫秒内序列
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                // 毫秒内序列溢出
                if (sequence == 0) {
                    //阻塞到下一个毫秒,获得新的时间戳
                    timestamp = tilNextMillis(lastTimestamp);
                }
            }
            // 时间戳改变,毫秒内序列重置
            else {
                sequence = 0L;
            }
            // 上次生成ID的时间截
            lastTimestamp = timestamp;
            // 移位并通过或运算拼到一起组成64位的ID
            return ((timestamp - twepoch) << timestampLeftShift) //
                    | (datacenterId << datacenterIdShift) //
                    | (workerId << workerIdShift) //
                    | sequence;
        }
        /**
         * 阻塞到下一个毫秒,直到获得新的时间戳
         * @param lastTimestamp 上次生成ID的时间截
         * @return 当前时间戳
         */
        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
        /**
         * 返回以毫秒为单位的当前时间
         * @return 当前时间(毫秒)
         */
        protected long timeGen() {
            return System.currentTimeMillis();
        }
    
        public static SnowflakeIdWorker getInstance(){
            return instance;
        }
    
    
        public static void main(String[] args) throws InterruptedException {
            SnowflakeIdWorker idWorker = SnowflakeIdWorker.getInstance();
            for (int i = 0; i < 10; i++) {
                long id = idWorker.nextId();
                Thread.sleep(1);
                System.out.println(id);
            }
        }
    }
    

    其他的操作,与上面类似。

    2.3、uuid

    同样的,uuid的生成,我们事先也可以将工具类编写好:

    public class UUIDGenerator {
    
        /**
         * 获取uuid
         * @return
         */
        public static String getUUID(){
            return UUID.randomUUID().toString();
        }
    }
    

    最后的单元测试,代码如下:

    @RunWith(SpringRunner.class)
    @SpringBootTest()
    public class UUID1Test {
    
        private static final Integer MAX_COUNT = 1000000;
    
        @Autowired
        private UUID1Mapper uuid1Mapper;
    
        @Autowired
        private UUID2Mapper uuid2Mapper;
    
        @Autowired
        private UUID3Mapper uuid3Mapper;
    
        /**
         * 测试自增ID耗时
         */
        @Test
        public void testInsert1(){
            long start = System.currentTimeMillis();
            for (int i = 0; i < MAX_COUNT; i++) {
                uuid1Mapper.insert(new UUID1().setName("张三"));
            }
            long end = System.currentTimeMillis();
            System.out.println("自增ID,花费时间:" +  (end - start));
        }
    
        /**
         * 测试采用雪花算法生产的随机数ID耗时
         */
        @Test
        public void testInsert2(){
            long start = System.currentTimeMillis();
            for (int i = 0; i < MAX_COUNT; i++) {
                long id = SnowflakeIdWorker.getInstance().nextId();
                uuid2Mapper.insert(new UUID2().setId(id).setName("张三"));
            }
            long end = System.currentTimeMillis();
            System.out.println("花费时间:" +  (end - start));
        }
    
        /**
         * 测试采用UUID生成的ID耗时
         */
        @Test
        public void testInsert3(){
            long start = System.currentTimeMillis();
            for (int i = 0; i < MAX_COUNT; i++) {
                String id = UUIDGenerator.getUUID();
                uuid3Mapper.insert(new UUID3().setId(id).setName("张三"));
            }
            long end = System.currentTimeMillis();
            System.out.println("花费时间:" +  (end - start));
        }
    }
    

    三、性能测试

    程序环境搭建完成之后,啥也不说了,直接撸起袖子,将单元测试跑起来!

    首先测试一下,插入100万数据的情况下,三者直接的耗时结果如下:

    在原有的数据量上,我们继续插入30万条数据,三者耗时结果如下:

    可以看出在数据量 100W 左右的时候,uuid的插入效率垫底,随着插入的数据量增长,uuid 生成的ID插入呈直线下降!

    时间占用量总体效率排名为:自增ID > 雪花算法生成的ID >> uuid生成的ID

    在数据量较大的情况下,为什么uuid生成的ID远不如自增ID呢

    关于这点,我们可以从 mysql 主键存储的内部结构来进行分析。

    3.1、自增ID内部结构

    自增的主键的值是顺序的,所以 Innodb 把每一条记录都存储在一条记录的后面。

    当达到页面的最大填充因子时候(innodb默认的最大填充因子是页大小的15/16,会留出1/16的空间留作以后的修改),会进行如下操作:

    • 下一条记录就会写入新的页中,一旦数据按照这种顺序的方式加载,主键页就会近乎于顺序的记录填满,提升了页面的最大填充率,不会有页的浪费

    • 新插入的行一定会在原有的最大数据行下一行,mysql定位和寻址很快,不会为计算新行的位置而做出额外的消耗

    3.2、使用uuid的索引内部结构

    uuid相对顺序的自增id来说是毫无规律可言的,新行的值不一定要比之前的主键的值要大,所以innodb无法做到总是把新行插入到索引的最后,而是需要为新行寻找新的合适的位置从而来分配新的空间。

    这个过程需要做很多额外的操作,数据的毫无顺序会导致数据分布散乱,将会导致以下的问题:

    • 写入的目标页很可能已经刷新到磁盘上并且从缓存上移除,或者还没有被加载到缓存中,innodb在插入之前不得不先找到并从磁盘读取目标页到内存中,这将导致大量的随机IO

    • 因为写入是乱序的,innodb不得不频繁的做页分裂操作,以便为新的行分配空间,页分裂导致移动大量的数据,一次插入最少需要修改三个页以上

    • 由于频繁的页分裂,页会变得稀疏并被不规则的填充,最终会导致数据会有碎片

    在把值载入到聚簇索引(innodb默认的索引类型)以后,有时候会需要做一次OPTIMEIZE TABLE来重建表并优化页的填充,这将又需要一定的时间消耗。

    因此,在选择主键ID生成方案的时候,尽可能别采用uuid的方式来生成主键ID,随着数据量越大,插入性能会越低!

    四、总结

    在实际使用过程中,推荐使用主键自增ID和雪花算法生成的随机ID。

    但是使用自增ID也有缺点:

    1. 别人一旦爬取你的数据库,就可以根据数据库的自增id获取到你的业务增长信息,很容易进行数据窃取。

    2. 其次,对于高并发的负载,innodb在按主键进行插入的时候会造成明显的锁争用,主键的上界会成为争抢的热点,因为所有的插入都发生在这里,并发插入会导致间隙锁竞争。

    总结起来,如果业务量小,推荐采用自增ID,如果业务量大,推荐采用雪花算法生成的随机ID。

    本篇文章主要从实际程序实例出发,讨论了三种主键ID生成方案的性能差异, 鉴于笔者才疏学浅,可能也有理解不到位的地方,欢迎网友们批评指出!

    五、参考

    1、方志明 - 使用雪花id或uuid作为Mysql主键,被老板怼了一顿!


    展开全文
  • 一个可能被忽略的问题:数据库表中必须有主键吗? 答案是否定的。数据库中的表可以不要主键主键的作用 保证实体的完整性; 加快数据库的操作速度 在表中添加新记录时,数据库会自动检查新记录的主键值,不...
  • 数据库主键sqlEvery great story starts with an identity crisis. Luke, the great Jedi Master, begins unsure - "Who am I?" - and how could I be anyone important? It takes Yoda, the one with the Force, ...
  • // 随机数 1-10 randomNumber = Long.valueOf(new Random().nextInt(100)); return (new Date()).getTime() * 100000L + index.longValue() * 100L + randomNumber.longValue(); } }
  • 关系型数据库主键知识

    千次阅读 2020-09-29 10:33:27
    在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,students表的两行记录: id class_id name gender score 1 1 小明 M 90 2 1 小红 ...
  • ipad和iphone的适配 ... 第二百八十五节,MySQL数据库-MySQL函数 MySQL数据库-MySQL函数 1.MySQL内置函数 SELECT执行函数,后面跟要执行的函数 CHAR_LENGTH(str)函数:返回字符串的字符长度 -- CHAR_LENGTH(str)函 ...
  • 数据库自增主键可能的问题

    千次阅读 2019-06-30 15:26:19
    单表的情况下数据库自增id并没有什么问题,在一张表分布到多个数据库的情况下,使用表自增将会出现id重复的问题。 解决的办法有两个方向,一个是在应用层做处理,一个是数据库上去做处理。 目前生成主键方法主要有...
  • 数据库主键的生成方式介绍

    千次阅读 2017-07-01 12:22:11
    使用一个高/低位算法来高效的生成long, short或者 int类型的标识符,给定一个数据库序列(sequence)的名字。  uuid.hex 用一个128-bit的UUID算法生成字符串类型的标识符。在一个网络中唯一(使用了IP...
  • Sequence IDUUIDGUIDCOMBSnowflake最开始的自增ID为了实现分库分别的需求,会在自增的前提下,使用不同起点,但需要做数据库拓展时,极其麻烦。 比如刚开始时,我们设计某个系统的数据库时,这个数据库中会有10个表...
  • 最近在写接口,发现主键数字以0开头和非零开头用不好,会有些许小麻烦。 以0123和123为例: 当主键格式设为int时,查询结果是一致的 ...so数据库主键尽量不要以0开始,有时候程序默认补零又会增加一系列麻烦
  • 前段时间用Struts开发了一个B/S结构的信息管理系统,其中有一个功能是要求管理员能够对数据字典进行修改,数据字典的表结构基本上都是table(id, name),id为数据库其它表中所的内容,表示方式为A01、A02、A08、B10、...
  • MySQL数据库主键和外键详解主键主键的定义主键:表中经常有一个列或多列的组合,其值能唯一地标识表中的每一行。这样的一列或多列称为表的主键,通过它可强制表的实体完整性。当创建或更改表时可通过定义 PRIMARY ...
  • 数据库主键生成策略 方法集合

    千次阅读 2015-10-13 11:20:42
    主键生成方法主要有以下几种:  1、采用mysql自增长主键策略   优点 :简单,不需要程序特别处理   缺点:这种方法对以后如果项目移植到其它数据库上... 优点:实现简单,与数据库无关,移植性较好   缺点
  • ● Sequence ID● UUID● GUID● COMB● Snowflake最开始的自增 ID 为了实现分库分别的需求,会在自增的前提下,使用不同起点,但需要做数据库拓展时,极其麻烦。 比如刚开始时,我们设计某个系统的数据库时,这个...
  • 1 是否每张表都应该有自增主键?不一定自增主键可以加快行的插入速度,...但如果数据库的存储类型是ssd,那这个问题就不存在了。所以,大部分情况来看,表有自增主键是正确的。2 自增主键是否具有业务上的唯一性?...
  • MySQL数据库主键和外键详解

    千次阅读 2020-03-20 20:15:42
    MySQL数据库主键和外键详解 主键 主键的定义 主键:表中经常有一个列或多列的组合,其值能唯一地标识表中的每一行。这样的一列或多列称为表的主键,通过它可强制表的实体完整性。当创建或更改表时可通过定义 ...
  • mysql自定义生成随机主键函数

    千次阅读 2017-10-26 15:39:54
    DELIMITER $$ USE test $$ DROP FUNCTION IF EXISTS `random_primary`$$ CREATE FUNCTION `rand_string`(n INT) RETURNS VARCHAR(255) CHARSET latin1 BEGIN DECLARE chars_str VARCHAR(100) DEFAULT 'ABCDEF
  • 在基于关系型数据库设计时候,通常要为每张表指定一个主键,所谓主键就是能够唯一标识表中某一行记录的属性或属性组,一个表只能有一个主键,但可以 有多个候选索引。因为主键可以唯一标识某一行记录,所以可以确保...
  • 有的项目用的是uuid生成主键,有的项目用的是数据库自增,而有的等等。今天就好奇查了一下资料。总结如下。 生成数据库主键的方式主要有三种: 数据库自增ID 采用随机数生成不重复的ID 采用jdk提供的uuid 这三种...
  • 前几天处理数据使用到了随机数,然后做了一次错误实践,发现了一个很奇怪的问题。 按照rownum的定义,当随机数为2的时候查询结果应该为空,但是却存在一个查询结果。而且查询结果的条数存在0,1,2三种情况 ``` ...
  • ● Sequence ID● UUID● GUID● COMB● Snowflake最开始的自增 ID 为了实现分库分别的需求,会在自增的前提下,使用不同起点,但需要做数据库拓展时,极其麻烦。 比如刚开始时,我们设计某个系统的数据库时,这个...
  • 使用redis生成数据库主键自增

    万次阅读 2016-07-26 20:29:47
    数据库自增什么的麻烦死了,尤其是往后还需要考虑到分布式处理,然后偷了个懒,直接redis来搞起... 下面上代码 先定义个主键生成策略接口,往后方便扩展 public interface KeyGenerate { /** * 生成String类型主键 ...
  • 数据库自动生成:数据库,自增型主键。 插入操作完成后,返回主键值(以作他用),返回值写在传入参数的实体类中的主键属性中。另外,插入成功会返回1,代表受影响的记录条数(插入一条数据,受影响记录数自然为1)...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,430
精华内容 4,972
关键字:

数据库随机数主键实现