台式机/ Webapp共享数据库覆盖重复密钥上的数据

时间:2019-05-10 16:24:05

标签: php sqlanywhere

我正在开发一个Web应用程序,该Web应用程序与生产中的桌面应用程序共享数据库(也就是我无法修改数据库,只能尝试模仿行为)。我现在正在使用的模块将在notes表中将注释存储到该数据库中。我能够使它正常工作,添加了便笺,便笺便出现在桌面应用程序中,然后一段时间后,我意识到便笺的实际便笺文本和说明已被覆盖。在查看数据库中的行时,我注意到已设置modified_by用户,并告诉我插入时有重复的键,然后进行更新。该表的主键是自动递增,因此我很困惑。经过一些挖掘后,我发现了一个名为counters的表,该表的一个名为notes的列的计数与notes表的当前索引相匹配。在每次对每个插入区的计数器简单地+1之前,我就将Wireshark下载到db服务器上并记录了db端口上的流量,发现了这一点:

(从桌面应用添加便笺的过程)

UPDATE counters SET in_use = 'Y';
SELECT notes FROM counters WHERE key_col = 1;
/* Desktop app uses current count for new index */
UPDATE counters SET notes = /* current count +1 */ WHERE key_col = 1;
UPDATE counters SET in_use = 'N';
/* ...Inserts new note here with explicit ID = current count ... */

现在我更加困惑了。为什么将表格设置为自动递增?其次,在选择计数并添加一个之前,从未对in_use进行过任何检查...那么in_use的意义是什么?如果两个用户同时插入,此代码是否会导致覆盖?这样做的正确方法不是为每个操作锁定counters表吗?我可以尝试一下,但是我不确定桌面应用程序将如何处理遇到的锁定(基于经验-致命错误)。

除了精确地重复此过程并希望获得最佳结果外,我不确定从何而来。一种想法是:

<?php
const MAX_ATTEMPTS = 3;
$curKey;

for($i = 0; $i < MAX_ATTEMPTS; $i++){
    /*
    SELECT in_use, notes from counters where key_col = 1;
    ...
    */
    if( 'N' === $result['in_use'] ){
        $curKey = $result['notes'];
        /* INSERT count here - $curKey++ */
        break;
    }
    /* Sleep for .25 seconds to allow for current operation to finish */
    usleep(250000);
}

if( null == $curKey ){
    throw \Exception('Could not insert note because counter table locked after '. MAX_ATTEMPTS .' attempts');
}

/* INSET note code here... */

这似乎可以,但仍可能会覆盖,因为a)选择计数和插入新计数之间的时间b)桌面应用似乎没有进行任何检查。

有什么想法/建议吗?

编辑:进行存储过程以在选择和插入期间进行检查。

DELIMITER $$

CREATE DEFINER=`testUser`@`%` FUNCTION `getNextNoteIndex`(appKey INTEGER) RETURNS int(11)
BEGIN
    SELECT IF(`in_use` = 'N', `notes`, NULL) INTO @curIndex FROM `counters` WHERE `app_key` = appKey;
    IF @curIndex IS NOT NULL
    THEN
        SET @newIndex = @curIndex + 1;
        UPDATE `counters` SET `notes` = @newIndex WHERE `app_key` = appKey AND `in_use` = 'N' AND `notes` = @curIndex;
        IF ROW_COUNT() = 1
        THEN
            RETURN @newIndex;
        END IF;
    END IF;
RETURN NULL;
END

用法:

SELECT testDB.getNextNoteIndex(1) AS $index;

1 个答案:

答案 0 :(得分:0)

我不知道他们需要创建一个可以自动递增的表格的目的,这听起来不像是一种标准的解决方案。

我对可以更改和不能更改的内容(数据库,后端代码等)感到困惑:

如果您在内部,您是否无法询问构建该中间增量表的开发人员的用途,并可能在那里弄​​清楚或完全绕开它。

如果您不在外面,向他们询问API并使用他们给您的端点是否有意义?然后,由覆盖引起的任何问题都将落到法院。