Java 使用子选择插入-原子操作?
我知道,mysql支持自动增量值,但不支持依赖的自动增量值 i、 e.如果您有这样一张桌子:Java 使用子选择插入-原子操作?,java,mysql,transactions,atomic,Java,Mysql,Transactions,Atomic,我知道,mysql支持自动增量值,但不支持依赖的自动增量值 i、 e.如果您有这样一张桌子: id | element | innerId 1 | a | 1 2 | a | 2 3 | b | 1 如果插入另一个b-元素,则需要自己计算innerId(插入除外为“2”) 有一个数据库支持这样的东西吗 实现这种行为的最佳方式是什么?我不知道元素的数量,所以我不能为它们创建专用表,在那里我只能驱动一个id (示例已简化) 应该实现的目标是,任何元素“
id | element | innerId
1 | a | 1
2 | a | 2
3 | b | 1
如果插入另一个b
-元素,则需要自己计算innerId(插入除外为“2”)
- 有一个数据库支持这样的东西吗李>
可能是infine-1
都应该有自己的、无间隙的id
如果我想用像
INSERT INTO
myTable t1
(id,element, innerId)
VALUES
(null, 'b', (SELECT COUNT(*) FROM myTable t2 WHERE t2.element = "b") +1)
这在所有情况下都会返回预期结果吗?我的意思是它可以工作,但是并发性如何呢?带有子选择的插入是否仍然是原子的,或者是否会有一个szenario,其中两个插入将尝试插入相同的id?(特别是在事务性插入挂起的情况下?)
使用编程语言(如Java)来实现这一点会更好吗?还是尽可能靠近数据库引擎来实现这一逻辑更容易
因为我使用聚合来计算下一个innerId,所以我认为使用SELECT…FOR UPDATE
无法避免其他事务具有挂起提交的问题,对吗
注:我可以使用ofc。只需在(element,innerId)
上使用唯一的键约束来强制插入-从每个元素的当前最大值开始-直到没有foreignKey冲突-但是没有更好的方法吗
根据我的例子,innerId和element
上有一个复合主键,这是可能的。但根据这一点,它只适用于MyIsam(我有InnoDB)
现在我更困惑了。我尝试使用两个不同的php脚本插入数据,使用上面的查询。虽然脚本一有15秒的“睡眠”时间,以便允许我调用脚本二(应该模拟并发修改),但使用一个查询时,结果是正确的 (附言:
mysql(?!i)
-仅用于快速调试的函数)
基础数据:
脚本1:
mysql_query("START TRANSACTION");
mysql_query("INSERT INTO insertTest (id, element, innerId, fromPage)VALUES(null, 'a', (SELECT MAX(t2.innerID) FROM insertTest t2 WHERE element='a') +1, 'page1')");
sleep(15);
//mysql_query("ROLLBACK;");
mysql_query("COMMIT;");
脚本2:
//mysql_query("START TRANSACTION");
mysql_query("INSERT INTO insertTest (id, element, innerId, fromPage)VALUES(null, 'a', (SELECT MAX(t2.innerID) FROM insertTest t2 WHERE element='a') +1, 'page2')");
//mysql_query("COMMIT;");
我本以为page2
insert会在page1
insert之前发生,因为它运行时没有任何事务。但事实上,page1 insert是先发生的,导致第二个脚本也延迟了大约15秒
(忽略AC Id,播放一段时间)
在第一个脚本上使用Rollback
时,第二个脚本仍会延迟15秒,然后选择正确的innerId
:
所以:
- 事务处于活动状态时阻止非事务性插入
- 带有子选择项的插入似乎也被阻止
- 因此,在结尾处,带有子选择的Insert似乎是一个原子操作?或者为什么第二页的
会被其他页面阻止SELECT
innerid
的必要功能。它被称为触发器,特别是插入前触发器
您的特定版本在多用户环境中无法一致工作。很少(如果有的话)数据库在启动insert时会在表上生成读取锁。这意味着两条insert语句非常接近地发出,将为innerid
生成相同的值
出于并发性考虑,您应该在数据库中使用触发器而不是在应用程序端执行此计算
您总是可以在需要时计算innerid
,而不是在插入值时。这在计算上很昂贵,需要一个order by
(使用变量)或者相关子查询。其他数据库支持窗口/分析函数,使这样的计算更易于表达。从我在这里读到的内容来看:您的查询似乎是原子的。我用InnoDB在MySQL上对其进行了测试,有4个不同的程序试图执行每个查询100000次。之后,我能够创建一个组合的Unique键在(element,innerid)上,它工作得很好,所以它似乎没有生成重复项。但是我得到了:
尝试获取锁时发现死锁
<>你可能想考虑这个
编辑:看来我可以通过将SQL更改为
插入测试(id、元素、innerId)
值(null,“b”,
(从测试t2中选择Count(*),其中元素='b'用于更新()+1);Thx作为您的答案。是的,可以计算innerID,基本上它是某一类型元素中的行号。问题是,(我讨厌那个句子)客户希望在复合模式布局中使用大量元素(限制为n=5)。对新数据的期望大约是每天数千次插入,因此存储生成的
子id
会更好-性能方面,当您必须找到元素125.5685.125.2145.3369时-因为我可以从第5个innerId列(其他列)中选择innerId 3369请查看我的更新。我还玩过并发插入,它看起来像是一个带有subselects I的插入
$nextId = mysql_query("SELECT MAX(t2.innerID) as a FROM insertTest t2 WHERE element='a'");
$nextId = mysql_fetch_array($nextId);
$nextId = $nextId["a"] +1;
mysql_query("INSERT INTO insertTest (id, element, innerId, fromPage)VALUES(null, 'a', $nextId, 'page2')");