数据库表mytable结构
id id_card
INT VARCHAR(32)
其中id是自增主键
如何优雅的实现if id_card 不存在就插入,当然要线程安全不能重复了。
我的实现方式是从代码层面加一个全局锁:
# 全局锁相关代码,这里没有加threading.lock,因为真实生产环境是多进程单线程模型。
if os.name != 'nt':
import fcntl
class GlobalLock(object):
def __init__(self, lock_file):
self.lock_file = "/tmp/" + lock_file
def acquire(self):
if os.name != 'nt':
self.fn = open(self.lock_file, "w")
fcntl.flock(self.fn.fileno(), fcntl.LOCK_EX)
def release(self):
if os.name != 'nt':
self.fn.close()
__gl__ = GlobalLock("__xmx__")
def global_lock(fp):
def _d(*args, **kw):
try:
__gl__.acquire()
r = fp(*args, **kw)
finally:
__gl__.release()
return r
return _d
数据库操作代码:
from sqlalchemy import func
from models.models import MyTable # 自定义的数据库表Model类
def is_id_card_exists(id_card):
try:
# 注意这里一定要用scope_session,否则会由数据缓存,从而结果不准确
s = db.create_scoped_session()
result = s.query(MyTable).filter(func.upper(MyTable.id_card) == func.upper(id_card)).first()
s.remove()
return True if result is not None else False
except Exception as e:
logging.error("_app.dbhelper.py is_id_card_exists exception:" + str(e))
return True
# 返回新mytable对象
@global_lock
def add_record(id_card):
try:
# 检查是否已存在
if is_id_card_exists(id_card) is True:
return None
s = MyTable(name)
db.session.add(s)
db.session.commit()
return s
except Exception as e:
logging.error("_app.dbhelper.py add_record exception:" + str(e))
return None
我尝试过用MySQL直接锁表的方式,但是由于db.session缓存问题也是没能解决,有没有优雅的解决方案?当然可以把id_card字段设置为主键,但我认为这样不是解决问题,而是绕过问题。
数据库上不加唯一键很容易出重复值的问题. 因为如果有一天你的代码是集群布署的,通过文件锁的方式是不太容易实现的,还涉及锁文件的管理问题(如突然中断后,锁文件没有清掉,会造成再也无法写入数据). 建议采用数据库的方式,如: