我对数据库和更新行有疑问。
我正在运行一个烧瓶应用程序,端点运行这样的命令(请接受这种伪代码/速记语法)
select * from Accounts where used = "False"
username = (first/random row of returned set)
update Accounts set used = "True" where name = username
return username
但是,如果有100个人在同一时间对此终端点进行调用会怎么样?
我怎样才能避免出现问题? (意思是2个人没有从表中获得相同的用户名,因为更新语句还没有在第二个人查询之前运行。)
显而易见的解决方案是锁定,类似于 - 这样如果两个人在同一时间点击端点,第二个人将不得不等待锁定释放
Global lock
----
with lock:
select * from Accounts where used = "False"
username = (first/random row of returned set)
update Accounts set used = "True" where name = username
return username
我相信这会奏效,但这不是一个很好的解决方案。有没有人对此有任何更好的想法?我确信公司在数据一致性方面始终存在这个问题,他们如何解决这个问题?
谢谢!
答案 0 :(得分:2)
MySQL / InnoDB提供四个transaction isolation levels:READ UNCOMMITTED
,READ COMMITTED
,REPEATABLE READ
和SERIALIZABLE
。
假设您使用REPEATABLE READ
和SERIALIZABLE
隔离级别在单个事务中执行所有命令,则一次只能执行一个访问相同行的事务,因此对于100个用户,只有1个用户执行该事务,而其余99个将在队列中等待。
使用READ UNCOMMITTED
和READ COMMITTED
隔离级别,两个或更多用户可以在used = False
时读取同一行,并尝试将其设置为used = True
}。
我认为如果将数据库布局重构为两个表会更好:一个具有所有可能的名称,另一个具有使用的名称,并且名称列具有唯一约束。对于每个新用户,您都会在使用的名称表中插入一个新行。如果您尝试插入具有相同名称的多个用户,则会出现违反唯一约束的错误,并且可以使用其他名称重试。
答案 1 :(得分:2)
数据库上的全局锁定非常糟糕。他们会大大放慢一切。相反,有表锁(要避免),行锁(这些都很好)和事务。
使用transaction。这可以将您和他们的更改与您的更改隔离开来。它还允许您丢弃所有更改,回滚,如果有问题,所以您不要在中途完成更改。除非你有充分的理由,否则你应该总是在交易中。
MySQL支持SELECT FOR UPDATE,它告诉数据库您要更新此事务中的选定行,以便这些行被锁定。
使用伪代码示例...
begin transaction
select * from Accounts where used = "False" for update
username = (first/random row of returned set)
update Accounts set used = "True" where name = username
commit
return username
基本上,事务使一组SQL语句“原子化”,这意味着它们从并发使用的角度出现在单个操作中。
其他要点......您应该使用Accounts
的主键进行更新,以避免使用非唯一字段。也许主键是用户名,也许不是。
其次,没有订单的选择可以按任何顺序返回。如果您正在通过一个帐户队列,您应该指定某种顺序,以确保最先完成(或您决定业务逻辑将是什么)。即使order by rand()
也会比依赖默认的表排序做得更好。
最后,如果您只想获取一行,请添加limit 1
,这样数据库就不会做太多额外的工作。
答案 2 :(得分:0)
首先,我要在表格中添加一个新字段,让它称之为会话ID。连接到该端点的每个客户端都应具有唯一的会话ID,sg将其与其他客户端区分开来。
我首先更新单个记录并将其会话ID字段设置为客户端的会话ID,然后检索会话ID上的记录,而不是进行选择,然后进行更新:
function aksi_login(){
$username = $this->input->post('username');
$password = $this->input->post('password');
$where = array(
'username' => $username,
'password' => md5($password)
);
$result = $this->m_login->cek_login("admin",$where);
$cek = $this->m_login->cek_login("admin",$where)->num_rows();
if($cek > 0){
$cek = $result->row_array();//no your records are in array format having matched row
$data_session = array(
'username' => $cek['username'],
'nama' => $cek['nama']
'status' => "login"
);
$this->session->set_userdata($data_session);
redirect(base_url("admin"));
}else{
echo "Username dan password salah !";
}
}
这样就可以避免锁定