mySQL - 表锁与行锁

4

应用描述

我有一个存储地图区域id的表格。每个地图包含1000个区域。 领地是指地图上任意数量相邻的区域。用户争夺地图上不同区域的所有权。

数据库设计

目前,我拥有一个地图表、一个领地表和一个区域表。

tblMaps: MapID, MapName

tblTerritories: TerrID (unique game wide), MapID, OwnerID, Status, Modified

tblAreas: AreaID (1-1000), TerrID

目前,tblAreas仅存储地图中的占用区域 - 不管是否有人拥有它,它都不包含每个地图的1000条记录。

当用户尝试拥有某些区域时,应用程序必须连接三个表并查询该地图中所有已占用的区域。如果其中任何一个被占用,则应拒绝他的所有权尝试。如果所有区域都是空闲的,则应创建一个新领土,并在tblAreas中添加相关区域。

问题

我意识到我需要一个基于事务的系统,以便两个用户不会同时尝试“拥有”该区域。现在据我所见,我必须锁定整个Area表,查询看看区域是否空闲,插入新的领土及其区域,提交并解锁表...或者Areas表应包含每个地图的所有1000个区域,并且锁定应仅应用于该地图的行。

希望有更好的选择,因为据我所见,锁定表将意味着所有区域数据在那一秒钟内都无法访问,或者使用行锁定,表中充满了无用的未占用区域。


我有点困惑 - 什么是区域?地图我懂。领土只是地图的一个子区域,可以被拥有(我想是这样的吧?)。区域是一组领土吗?还是领土是一组区域? - Keith Randall
抱歉,我已经编辑了它。该地图由1000个区域组成。一个领土是由一个用户拥有的相邻区域集合。 - Mark
1个回答

2
如果你在tblAreas.AreaID上有一个索引,那么任何包含“WHERE tblAreas.AreaID in (...)”的事务都会锁定这些条目的索引。无论行是否存在都没有关系。该锁将防止另一个事务插入任何这些ID的条目。所以我认为你不需要采取你的任何建议。只需查询以查看您的领土是否可用即可获得您需要以原子方式插入领土的锁定。
由于您的区域ID不是游戏范围内唯一的,因此在具有不同地图中相同ID的区域之间可能会出现一些虚假序列化问题。将mapID添加到tblAreas表中可能有所帮助,以便您可以创建(mapID,areaID)索引进行查找,从而避免索引上的虚假冲突。(这将使您的模式去规范化,由于其他原因您可能不想这样做。)

我能不能只锁定tblTerritories中的mapID,以防止任何人插入新条目?非规范化是如此诱人...它会使可用区域的查询变得更加容易(只需查询tblarea..而不是将三个表连接在一起)...但我认为这样做效率会更低。你怎么看? - Mark
1
是的,我猜如果您通过mapID查询tblTerritories,那么在您的事务期间将阻止其他人插入另一个领土。 - Keith Randall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接