AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 269189
Accepted
Déjà vu
Déjà vu
Asked: 2020-06-16 05:41:37 +0800 CST2020-06-16 05:41:37 +0800 CST 2020-06-16 05:41:37 +0800 CST

MySQL UUID() 行为

  • 772

4 年前,我在 SO 上回答了一个问题,“如何为每一行批量设置 UUID”。

我试了之后回答

UPDATE table SET uuidcol = UUID();

它对我有用,但大约一半(似乎)的人报告说它不起作用,说所有行的 ID 都是相同的。

所以今天,我再次尝试,每次删除列,并创建一个具有新类型的新列,例如

text, char(36), varchar(36)

它再次起作用,每次都为每行创建一个唯一的 UUID。

是什么让它对其他人不起作用?
比如发动机?(在 MySQL 5.7 上使用 InnoDB)

编辑

做了另一个测试

  • 创建一个简单t的表uu varchar(36)
  • uu插入10,000,000行null
  • 做了UPDATE t SET uu=UUID();

并且所有 10M 行都有不同的唯一UUID。

mysql innodb
  • 3 3 个回答
  • 2960 Views

3 个回答

  • Voted
  1. Davi Cavalcanti
    2020-10-23T19:44:37+08:002020-10-23T19:44:37+08:00

    我现在有这个完全相同的问题。

    我编写了一个示例 java 应用程序来演示该问题:

    MariaDBTestUUID.java:

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class MariaDBTestUUID
    {
        private Connection connect;
        private Statement statement;
        private ResultSet resultSet;
    
        public void updateUUID() throws Exception
        {
            try
            {
                // This will load the MySQL driver, each DB has its own driver
                Class.forName("org.mariadb.jdbc.Driver");
                // Setup the connection with the DB
                connect = DriverManager
                        // AWS Aurora (MySQL server 5.6.10) - found with SQL "show variables like '%version%';"
                        .getConnection("jdbc:mariadb://<hostname>:3306/<DB name>?user=<user>&password=<password>");
    //                     // local MySQL server (5.7.22)
    //                    .getConnection("jdbc:mariadb://localhost:3306/licenceserver4?user=root&password=<password>");
    //                     // docker container MySQL server (5.7.30)
    //                    .getConnection("jdbc:mariadb://localhost:3307/testDB?user=root&password=<password>");
    
                // PreparedStatements can use variables and are more efficient
                PreparedStatement preparedStatement =
                    connect.prepareStatement("UPDATE testUUID SET new_id = uuid(), new_id2 = REPLACE(uuid(), '-', ''), new_id3 = UNHEX(REPLACE(uuid(), '-', ''))");
                preparedStatement.executeUpdate();
    
                queryStrings("select ID, new_ID, new_ID2, hex(new_ID3) as new_ID3 from testUUID");
                queryStrings("select count(distinct(new_ID)) as distinct_rows from testUUID");
                queryStrings("select count(distinct(new_ID2)) as distinct_rows2 from testUUID");
                queryStrings("select count(distinct(new_ID3)) as distinct_rows3 from testUUID");
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                close();
            }
        }
    
        private void queryStrings(String sql) throws SQLException
        {
            System.out.println("Running \"" + sql + "\"... ");
            statement = connect.createStatement();
            // Result set get the result of the SQL query
            resultSet = statement
                .executeQuery(sql);
            writeResultSet(resultSet);
        }
    
        private void writeResultSet(ResultSet resultSet) throws SQLException
        {
            // ResultSet is initially before the first data set
            while (resultSet.next())
            {
                // we can only do this because we know they are all strings
                for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++)
                {
                    String value = resultSet.getString(i);
                    System.out.print(resultSet.getMetaData().getColumnName(i) + ": " + value + ", ");
                }
                System.out.println();
            }
        }
    
        // You need to close the resultSet
        private void close()
        {
            try
            {
                if (resultSet != null)
                {
                    resultSet.close();
                }
    
                if (statement != null)
                {
                    statement.close();
                }
    
                if (connect != null)
                {
                    connect.close();
                }
            }
            catch (Exception e)
            {
                System.out.println("Error closing:");
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) throws Exception
        {
            new MariaDBTestUUID().updateUUID();
        }
    }
    

    客户端库的版本在这里设置:

    构建.gradle:

    plugins {
        id 'java'
    }
    
    sourceCompatibility = 11
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        // DB driver
        runtimeOnly 'org.mariadb.jdbc:mariadb-java-client:2.2.4'
    }
    

    要运行测试应用程序,只需:

    1 - 连接到每个数据库并运行以下查询:

    create table testUUID (ID int, new_ID varchar(100), new_ID2 varchar(100), new_ID3 BINARY(16));
    insert into testUUID (id) values (1),(2),(3);
    

    2 - 替换出于安全原因屏蔽的连接信息并在每个数据库上运行它。

    3 - 在 build.gradle 中设置要在测试中使用的 mariadb 客户端版本

    4 - 运行 main()

    如果输出为每个不同的查询显示 3,则更新 stmt 在每列中创建了不同的值。如果显示为 1,则更新为该列的所有行设置相同的值。查询都应该返回 3。

    我所看到的,对于所有数据库:

    • 对于 mariadb 客户端 2.2.3,它为所有查询返回 3。
    • 对于 mariadb 客户端 2.2.4,它为第二个和第三个查询(new_ID2 和 new_ID3)返回 1。

    所以,到目前为止我的发现:

    1. 在 mariadb 客户端库的 2.2.3 版本之前,一切都没有问题(每行中的所有列都设置了不同的值)。replace(uuid(),'-', '')从版本 2.2.4 开始,它开始为我们设置为使用单个更新语句的列的每一行设置相同的值。其他几个 mariadb 客户端版本也会发生这种情况,包括最新的 (2.7.0)。

    2. 该问题与 REPLACE() 函数有关,或者与更新中的函数调用嵌套有关。这绝对不是由函数 UUID() 引起的。

    3. 这个问题同样发生在 MySQL 5.7.22(本地安装在我的环境中)、MySQL 5.7.30(测试 docker 容器),更重要的是 Aurora DB (MySQL 5.6.10)。

    -------------------------------------------------- - 编辑 - - - - - - - - - - - - - - - - - - - - - - - - -----

    对我来说,这是由以下因素的结合引起的:

    • 数据库字符集
    • MariaDB J/Connector 从版本 2.2.4 开始的更改
    • UUID() 和字符串处理函数的组合(如 REPLACE())

    将 CONVERT() 添加到图片中解决了它:

    UPDATE sometable SET uuid=UNHEX(REPLACE(CONVERT(UUID() using utf8mb4), '-', '')) WHERE uuid IS NULL;

    有关更多详细信息(和赞成票):https ://stackoverflow.com/a/51393124/5154619

    • 1
  2. Best Answer
    Rick James
    2020-06-17T17:21:46+08:002020-06-17T17:21:46+08:00

    我在 9 版本的 MySQL/MariaDB 上试过这个:

    CREATE TABLE t (
        i INT,
        myuuid CHAR(36) NULL
        ) ENGINE=InnoDB;
    
    INSERT INTO t (i) VALUES
        (1), (2), (3), (4);
    
    UPDATE t SET myuuid = UUID();
    
    SELECT DISTINCT myuuid FROM t;
    

    每个版本有4 个不同的行,因此有 4 个不同的 uuid。

    如果您有生成相同uuid 的代码,那么您在做什么不同?

    • 0
  3. nbk
    2020-06-16T05:52:37+08:002020-06-16T05:52:37+08:00

    在 mysql中实现和定义的问题uuid()在于它并不是真正随机生成的,因此可能会发生冲突,尤其是在批量执行时。

    更好的是使用 uuid v4,它保证了整个过程的随机性,请参阅实现https://stackoverflow.com/a/32965744/5193536它有点慢。

    • -1

相关问题

  • 是否有任何 MySQL 基准测试工具?[关闭]

  • 我在哪里可以找到mysql慢日志?

  • 如何优化大型数据库的 mysqldump?

  • 什么时候是使用 MariaDB 而不是 MySQL 的合适时机,为什么?

  • 组如何跟踪数据库架构更改?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve