鍍金池/ 問答/Python  數(shù)據(jù)庫/ sqlalchemy如何優(yōu)雅的實現(xiàn)線程安全的INSERT IF NOT EXIS

sqlalchemy如何優(yōu)雅的實現(xiàn)線程安全的INSERT IF NOT EXIST?

數(shù)據(jù)庫表mytable結構
id id_card
INT VARCHAR(32)
其中id是自增主鍵

如何優(yōu)雅的實現(xiàn)if id_card 不存在就插入,當然要線程安全不能重復了。
我的實現(xiàn)方式是從代碼層面加一個全局鎖:

# 全局鎖相關代碼,這里沒有加threading.lock,因為真實生產(chǎn)環(huán)境是多進程單線程模型。
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

數(shù)據(jù)庫操作代碼:

from sqlalchemy import func
from models.models import MyTable  # 自定義的數(shù)據(jù)庫表Model類

def is_id_card_exists(id_card):
    try:
        # 注意這里一定要用scope_session,否則會由數(shù)據(jù)緩存,從而結果不準確
        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緩存問題也是沒能解決,有沒有優(yōu)雅的解決方案?當然可以把id_card字段設置為主鍵,但我認為這樣不是解決問題,而是繞過問題。

回答
編輯回答
久礙你

找到了一個解決方法, 設置id_card為unique index, 這樣如果有重復的id_card數(shù)據(jù)插入或者修改就會拋sql異常~~~ 完美解決了這個問題

2017年8月12日 21:36
編輯回答
乖乖噠

數(shù)據(jù)庫上不加唯一鍵很容易出重復值的問題. 因為如果有一天你的代碼是集群布署的,通過文件鎖的方式是不太容易實現(xiàn)的,還涉及鎖文件的管理問題(如突然中斷后,鎖文件沒有清掉,會造成再也無法寫入數(shù)據(jù)). 建議采用數(shù)據(jù)庫的方式,如:

INSERT INTO table (id_card, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name="A", age=19
2017年4月16日 04:51