mysql - 如何从并发表读取中的范围选择中获取唯一主键

标签 mysql sql

早上好

我有多个客户试图在表中获取唯一的主键。

只有当它们与成功的范围扫描匹配时,由该 PK 标识的行才被视为“有效”。范围扫描是SELECT id FROM lookup WHERE allowed='Y' and updated<=NOW() LIMIT 1

------------+---------------+------+-----+-------------------+----------------+
| Field      | Type          | Null | Key | Default           | Extra          |
+------------+---------------+------+-----+-------------------+----------------+
| id         | int(11)       | NO   | PRI | NULL              | auto_increment |
| fullname   | varchar(250)  | NO   | UNI | 0                 |                |
| allowed    | enum('Y','N') | NO   | MUL | N                 |                |
| updated    | timestamp     | NO   |     | CURRENT_TIMESTAMP |                |
| hits       | smallint(6)   | NO   | MUL | 0                 |                |
| stop_allow | enum('Y','N') | NO   | MUL | N                 |                |
+------------+---------------+------+-----+-------------------+----------------+

完成第一个选择后,将执行另一个 SELECT 以检索内容。

问题是许多客户端同时在做同样的事情(或者他们随机找到一种方法来相互匹配 grrrr...)。

到目前为止,我已经尝试过:

1)

 start transaction;
 *range scan* LIMIT 1 FOR UPDATE;
 SELECT * from lookup WHERE id=(result of the range scan);
 *perform stuff*
 commit;

这是一个性能 killer 。东西被永远锁定,一段时间后“Mysql 服务器升天”。

2)

 start transaction;
 *range scan*
 SELECT * from lookup WHERE id=(result of the previous query) FOR UPDATE;
 *perform stuff*
 commit;

autocommit=0 失败得很惨,但是速度很快

3) 在这一点上,我开始认为事务是问题所在

 no transaction;
 //get a row that is not being processed
 *range scan* LEFT OUTER JOIN temp_mem_table WHERE **temp_mem_table.id IS NULL**
 $rid = (result of the range scan)

 //check if another client is doing the same thing, if so then stop here
 select 1 from temp_mem_table WHERE id=$rid
 //if there is a result => return null; this is not enough to block stuff going through

 //signal to other client that this ID is being processed
 insert into temp_mem_table(id) values($rid)
 //get the content
 SELECT * from lookup WHERE id=($rid); 
 *perform time intensive operations*

编辑:temp_mem_table 实际上是一个内存表,它会在一段时间内刷新。它看起来像这样:

CREATE TABLE temp_mem_table(id int(11), primary key(id)) engine=memory

思路是:如果正在处理的内容存储在所有客户端都可以访问的内存表中,那么他们应该能够知道他们的 friend 在做什么。支票应该停止任何进一步的处理。但不知何故,他们找到了一种方法来通过:(

短时间后,似乎有近 50% 的主键至少被处理了两次。

我会想办法做到这一点,但也许你们中的一些人遇到过类似情况并且可以提供帮助。

谢谢

最佳答案

好的,对于那些遇到著名的“如何在 Mysql 中选择未锁定的行?”的人来说比如在这里看到http://bugs.mysql.com/bug.php?id=49763还有很多其他地方。这里有一个肮脏的 hack 来解决它。

这是在 READ REPEATABLE MODE 中完成的,它应该是 ACID 超过 9000 或至少不会破坏任何东西(也许)。

起点是有某种“范围”的行需要锁定以供读取,这样其他客户端无论如何都不会得到它。

 SELECT pk FROM tbl LIMIT 0,10
 SELECT pk FROM tbl where *large range scan*

我确实创建了一个内存表(因为它应该更快),例如:

CREATE TABLE `jobs` (
`pid` smallint(6) DEFAULT NULL,
`tid` int(11) DEFAULT NULL,
 UNIQUE KEY `pid` (`pid`),
 UNIQUE KEY `tid` (`tid`)
 ) ENGINE=MEMORY DEFAULT CHARSET=utf8 |

Pid是客户端的唯一标识。在我的例子中,它是实际的进程 ID。 Tid 是与我们执行范围王扫描的那个巨大表的主键匹配的任务 ID。

那么伪代码是这样的:

 SELECT pk from tbl WHERE (range scan) or limit 0,100
 delete from jobs where pid=$my_pid
 foreach of those pk do
 if(insert IGNORE into jobs(pid,tid) values(1234,pk)) break;
 done;
 select pk from jobs where pid=$my_pid
 select * from big_tbl where id=pk

已使用 2、10、25、50 和 100 个并发客户端对此进行了测试,并在每个客户端上获得了 100% 唯一的任务分配。

现在这可能不是 super 复杂,或者看起来可能不优雅,但只要 CPU 保持凉爽,我不在乎。

关于mysql - 如何从并发表读取中的范围选择中获取唯一主键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23129338/

相关文章:

MySQL 5.0.11 错误代码 #1215 : cannot add foreign key constraint

php - 在 LIKE 中使用逗号分隔的 mySQL 列值

php 脚本行为怪异,即使记录不存在也会显示记录

java - 如何解决 java.lang.ClassCastException : hibernate

sql - 在 Hive SQL 中引用计算列的输出

mysql - 如何根据与特定纬度/经度的距离查询 Amazon Redshift 表中的用户 ID 列表?

php - 条件准备语句不切换

php - 在 MySQL 中使用表别名作为列

php - 在 Symfony2 中有多个数据库连接是好的做法吗

mysql - 插入行详细信息(例如用户率平均 SQL)后触发自动平均