我有一个MySQL表(使用InnoDB作为存储引擎)来存储用户事务。
// OS.hpp -------------------------------------------------
// !!!! HERE I WILL NOT INCLUDE APPLICATION.HPP !!!!
// I JUST USE THE APPLICATION DEFINITION TO ACCESS THE
// STATIC FUNCTION
extern class Application;
class OS {
public:
OS() {}
~OS() {}
int getInfo() { return Application::get(); }
}
// Middleware.hpp -----------------------------------------
#include "OS.hpp"
class Middleware {
OS os;
public:
Middleware() {}
~Middleware() {}
void doSomething() { int a = os.getInfo(); }
}
// Application.hpp ----------------------------------------
#include "Middleware.hpp"
class Application {
Middleware mw;
public:
Application() {}
~Application() {}
void run() { mw.doSomething(); }
static int get() { return 0; } // !!!! this function (static) i would like to access from OS
}
// main.cpp -----------------------------------------------
#include "Application.hpp"
int main() {
Application app;
app.run();
}
我在多线程环境中工作,并希望确保:
我提出了以下解决方案:
当线程想要为用户插入事务时,获取与该用户对应的行的独占锁
CREATE TABLE `transactions` (
`id` int(11) NOT NULL,
`correlation_id` char(36) NOT NULL,
`user_id` char(36) NOT NULL,
`currency` char(3) NOT NULL,
`time_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`transaction_amount` double NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `transactions`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `correlation_id_unique` (`correlation_id`),
ADD INDEX (`user_id`);
当线程想要读取用户余额时(通常通过对用户的所有事务求和),它首先获取与该用户对应的行的共享锁
BEGIN;
-- Acquire an exclusive lock on the rows with user_id=1
SELECT * FROM transactions WHERE user_id = 1 FOR UPDATE;
-- Insert transactions
...
COMMIT;
但是,似乎独占锁是锁定整个表,而不仅仅是SELECT ... FOR UPDATE语句返回的行。这是一个例子。
主题1:
SELECT SUM(transaction_amount)
FROM transactions
WHERE user_id=1
LOCK IN SHARE MODE;
主题2:
mysql> select user_id, transaction_amount from transactions;
+---------+--------------------+
| user_id | transaction_amount |
+---------+--------------------+
| 1 | 10 |
| 1 | -2 |
| 2 | 5 |
| 2 | 10 |
+---------+--------------------+
4 rows in set (0.00 sec)
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM transactions WHERE user_id = 1 FOR UPDATE;
+----+----------------+---------+----------+---------------------+--------------------+
| id | correlation_id | user_id | currency | time_created | transaction_amount |
+----+----------------+---------+----------+---------------------+--------------------+
| 1 | 1 | 1 | CHF | 2018-03-06 09:54:28 | 10 |
| 2 | 2 | 1 | CHF | 2018-03-06 09:54:28 | -2 |
+----+----------------+---------+----------+---------------------+--------------------+
2 rows in set (0.01 sec)
阅读MySQL's documentation之后,我原本预计会这样做:
选择...锁定共享模式
在读取的任何行上设置共享模式锁定。 其他会话可以读取行,但在事务提交之前无法修改它们
SELECT ... FOR UPDATE
对于搜索遇到的索引记录,锁定行和任何关联的索引条目,就像为这些行发出UPDATE语句一样。阻止其他交易更新那些行,从进行SELECT ... LOCK IN SHARE MODE,或者从某些事务隔离级别读取数据。
现在,我找到this topic,说明在我的情况下,-- Retrieve transactions of user 2
mysql> SELECT * FROM transactions WHERE user_id = 2 LOCK IN SHARE MODE;
[[Hangs]]
字段应该有一个索引 - 而且确实如此。
我觉得问题可能是由请求user_id
没有使用索引引起的:
SELECT * FROM transactions WHERE user_id=1
有什么想法吗?
答案 0 :(得分:1)
我用MySQL 5.6.31测试了你的表,并在其中填充了50万行随机值,介于1和1000之间。
即使强迫指数也没有帮助:
mysql> EXPLAIN SELECT * FROM `transactions` force index (user_id) where user_id=1;
+----+-------------+--------------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | transactions | ALL | user_id | NULL | NULL | NULL | 520674 | Using where |
+----+-------------+--------------+------+---------------+------+---------+------+--------+-------------+
但是,即使没有索引提示,搜索整数字符串仍然有效:
mysql> EXPLAIN SELECT * FROM `transactions` where user_id='1';
+----+-------------+--------------+------+---------------+---------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+------+---------------+---------+---------+-------+------+-----------------------+
| 1 | SIMPLE | transactions | ref | user_id | user_id | 36 | const | 1 | Using index condition |
+----+-------------+--------------+------+---------------+---------+---------+-------+------+-----------------------+
varchar列与二进制整数的比较似乎打败了可索引性。