【问题标题】:how to enable multi-thread/connection modify the same mysql table?如何启用多线程/连接修改同一个mysql表?
【发布时间】:2011-11-14 12:18:30
【问题描述】:

我有一个运行 2 个线程的程序,每个线程都有自己的数据库 JDBC 连接,它们将访问/修改同一个数据库表 A,如下所示。表A只有2列(id、name),主键是id和name的组合。

statement stmt;

// first delete it if the record has exist in table
stmt.addBatch("delete from A where id='arg_id' and name='arg_name';");

// then insert it to table
stmt.addBatch("insert into A values (arg_id, arg_name);");

stmt.executeBatch();

这2个线程可能会向表中插入相同的数据,我得到了以下异常,

java.sql.BatchUpdateException: Duplicate entry '0001-joey' for key 1
        at com.mysql.jdbc.Statement.executeBatch(Statement.java:708)
        at com.mchange.v2.c3p0.impl.NewProxyStatement.executeBatch(NewProxyStatement.java:743)
        at proc.Worker.norD(NW.java:450)

您知道如何解决此问题吗?谢谢。

问候, 乔伊

【问题讨论】:

    标签: java mysql acid


    【解决方案1】:

    为什么不在数据库上引入一个简单的乐观锁定机制?

    在执行删除或更新事务时添加版本列并跟踪版本号。

    你的桌子看起来像

    create table test(
    id int not null primary key,
    name varchar,
    rowversion int default = 0);
    

    每次检索行时,您都应该检索行版本,以便您可以这样做

    update test set name='new name' rowversion=rowversion+1 where id=id and rowversion=retrieved row version;
    

    删除也一样

    delete from test where id=id and rowversion=retrievedRowVersion;
    

    这是一种利用 dbms 并发管理功能的简单机制。查看此链接以获取有关乐观锁定的更多信息http://en.wikipedia.org/wiki/Optimistic_concurrency_control#Examples

    这显然只是一个非常简单的并发管理实现,但你的问题必须考虑到这些。

    对于双重插入,您的交易被拒绝这一事实也很好,这意味着没有插入重复的密钥。您应该只处理异常并通知用户发生了什么。

    【讨论】:

    • 谢谢卡尔。看来在数据库级别实现occ需要更改表结构,所以我将转向java代码级别添加一些同步以确保不同的线程按顺序访问表。
    【解决方案2】:

    将两个语句包装在一个事务中:

    BEGIN;
    DELETE FROM a WHERE ...;
    INSERT INTO a VALUES (...);
    COMMIT;
    

    注意,只要表只包含主键,这种冲突只有在表最后没有被修改时才会出现;我假设您想添加更多列,在这种情况下您应该使用 UPDATE ... WHERE 语法来更改值。

    【讨论】:

    • 你好西蒙,我尝试了以下语句,stmt.executeUpdate("delete from A where id='arg_id' and name='arg_name'; insert into A values (arg_id, arg_name);") ;我得到了以下异常,您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的“INSERT INTO A VALUES (1, 'joey')”附近使用正确的语法。但是此语句可以在 mysql 控制台中运行而不会出错。你知道为什么不能一次执行 2 个 sql 吗?谢谢。
    • 我假设executeUpdate 对语句做了一些额外的处理,因为它需要能够理解结果。请注意,以分号分隔的两个语句与事务不同。
    【解决方案3】:

    您是否正在使用任何类型的同步?首先,您需要将修改表的代码包装在:

    synchronized(obj)
    {
        // code
    }
    

    其中 obj 是两个线程都可以访问的对象。 我不知道你的表修改的确切语义,但如果它们都插入 id,你还需要保存一个“全局”id 并在每个线程中原子地递增它,这样它们就不会得到相同的值.

    【讨论】:

    • 感谢您的回复,都铎王朝。不,这 2 个线程没有同步机制,并且 id 可以是相同的值。有没有办法从 Mysql 数据库级别让 2 个事务(因为它们有自己的连接)独立并按顺序运行?
    • 这两个事务将按照您运行它们的顺序发生。您在数据库端所做的任何事情都不会改变这一点。如果您希望它们以特定顺序应用于数据库,则需要使用某种锁定或同步来确保这一点。
    猜你喜欢
    • 1970-01-01
    • 2012-04-12
    • 2012-12-10
    • 1970-01-01
    • 1970-01-01
    • 2014-05-08
    • 2015-03-31
    • 1970-01-01
    • 2015-12-31
    相关资源
    最近更新 更多