Flask使用SQLAlchemy添加悲观锁和乐观锁

简介: 什么是悲观锁和乐观锁?• 悲观锁:假设在访问数据时会发生冲突,因此在访问数据前,先锁住数据。在锁被释放之前,其他事务无法访问此数据。是属于数据库中的一种互斥锁机制• 乐观锁:假设在访问数据时冲突较少,因此不会锁住数据,而是在提交更新时检查是否有冲突。如果检测到冲突,则放弃更新。但是乐观锁并非真正的数据库锁。

什么是悲观锁和乐观锁?

  • 悲观锁:假设在访问数据时会发生冲突,因此在访问数据前,先锁住数据。在锁被释放之前,其他事务无法访问此数据。是属于数据库中的一种互斥锁机制
  • 乐观锁:假设在访问数据时冲突较少,因此不会锁住数据,而是在提交更新时检查是否有冲突。如果检测到冲突,则放弃更新。但是乐观锁并非真正的数据库锁。


2种锁都是数据库在应对并发操作时,防止出现资源抢夺的,基于不同人生观所实现2种解决方案。

定义数据模型

定义一个简单的Item模型,它包含一个name字段和一个version字段。version字段用于实现乐观锁。

class Item(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    version = db.Column(db.Integer, default=0)

悲观锁的实现

悲观锁通过在查询时锁住行来实现。SQLAlchemy提供了with_lockmode方法来实现这一点。在SQLite中,悲观锁的实现有些限制,所以我们通常在支持行级锁的数据库(如PostgreSQL、MySQL)中使用它。

@app.route('/update_item_pessimistic/<int:item_id>', methods=['POST'])
def update_item_pessimistic(item_id):
    item = db.session.query(Item).with_for_update().filter_by(id=item_id).first()
    if item:
        # 执行更新操作
        item.name = 'New Name'
        db.session.commit()
        return 'Item updated successfully with pessimistic lock', 200
    return 'Item not found', 404

在上述代码中,使用with_for_update方法锁住了指定的行,确保在事务提交前,其他事务无法修改这行数据。

乐观锁的实现

乐观锁通过在更新时检查版本号是否变化来实现。如果版本号发生变化,说明有其他事务在此期间修改了数据,我们应当放弃更新。

@app.route('/update_item_optimistic/<int:item_id>', methods=['POST'])
def update_item_optimistic(item_id):
    item = db.session.query(Item).filter_by(id=item_id).first()
    if item:
        original_version = item.version
        item.name = 'New Name'
        item.version += 1

        try:
            db.session.commit()
            return 'Item updated successfully with optimistic lock', 200
        except SQLAlchemyError:
            db.session.rollback()
            return 'Update failed due to concurrent modification', 409
    return 'Item not found', 404

在上述代码中,在更新前记录当前版本号,并在更新时将版本号+1。提交时,如果发现版本号不匹配,SQLAlchemy会抛出异常,我们回滚事务并返回冲突错误。

使用场景

悲观锁适用于以下场景:

  • 数据库并发写操作频繁,冲突概率高。
  • 数据修改时需要保持数据的一致性和完整性。
  • 需要避免脏读、幻读等并发问题。


乐观锁适用于以下场景:

  • 读多写少的场景,数据冲突较少。
  • 允许重试更新操作。
  • 不希望锁住数据,允许其他事务并发读取。

拓展


举例:双11活动,商城里面id=5的商品的库存num=10了,现在我们要基于乐观锁和悲观锁来解决下单过程中,出现的资源抢夺现象,避免出现超卖(商品数量不能为负数)。

乐观锁:
---> begin;  开启事务
---> 先查看库存,记录当前库存 original_num=10
---> 进行下单操作,买6件 
---> 付款
---> 扣除库存 update from goods set num=num-6 where num=original_num and id=5;  # 增加更新条件,判断库存是否还是原来
       如果执行成功,则表示没有人抢,购买成功
---> commit;
       如果执行失败,则表示已经有人先抢购
---> rollback;

悲观锁:
---> begin; 开启事务
---> 先给id=5的数据,加锁 select * from goods where id=5 for update;
---> 进行下单操作,买6件
---> 付款
---> 扣除库存 update from goods set num=num-6 where id=5;
---> 执行成功解锁
---- commit; 提交事务 """

悲观锁的问题

  1. 提前锁定数据,形成串行化,形成阻塞,不利于性能发挥,不适用高并发场景。
  2. 悲观锁只能保证数据的一致性,不能保证脏数据的出现 乐观锁的出现就是为了解决悲观锁的问题。
相关文章
|
24天前
|
SQL 数据库 开发者
Flask、SQLAlchemy
【6月更文挑战第5天】
20 3
|
11月前
|
Python
【Flask实战】sqlalchemy和Flask-SQLAlchemy使用对比
【Flask实战】sqlalchemy和Flask-SQLAlchemy使用对比
|
9月前
|
SQL 关系型数据库 MySQL
Flask数据库之SQLAlchemy--介绍--链接数据库
Flask数据库之SQLAlchemy--介绍--链接数据库
|
10月前
|
SQL 存储 关系型数据库
【flask-sqlalchemy】SQLAlchemy+PyMysql到mysql的映射
【flask-sqlalchemy】SQLAlchemy+PyMysql到mysql的映射
129 0
|
数据库 数据库管理 Python
一分钟快速实现Flask框架与SQLAlchemy框架的整合
一分钟快速实现Flask框架与SQLAlchemy框架的整合
118 0
|
数据库 Python
Flask学习与项目实战5:通过SQLAlchemy连接数据库进行ORM模型的简单增删改查
是对于数据库的ORM的框架,直接操作模型,安装命令为pip install SQLAlchemy。 SQLAlchemy是一个独立的ORMR框架,可以独立于FLASK存在,也可以在Django中使用。 Flask-SQLAlchemy是对SQLAlchemy的一个封装,更适合在flask使用。
Flask学习与项目实战5:通过SQLAlchemy连接数据库进行ORM模型的简单增删改查
|
存储 SQL 数据库
Flask Web 极简教程(三)- SqlAlchemy(Part A)
Flask Web 极简教程(三)- SqlAlchemy(Part A)
Flask Web 极简教程(三)- SqlAlchemy(Part A)
|
数据库 Python
小白学Flask第十二天| flask-sqlalchemy数据库扩展包(二)
小白学Flask第十二天| flask-sqlalchemy数据库扩展包(二)
186 0
小白学Flask第十二天| flask-sqlalchemy数据库扩展包(二)
|
SQL 存储 NoSQL
小白学Flask第十一天| flask-sqlalchemy数据库扩展包(一)
小白学Flask第十一天| flask-sqlalchemy数据库扩展包(一)
264 0
小白学Flask第十一天| flask-sqlalchemy数据库扩展包(一)
|
数据库 Python
Python Flask SQLAlchemy上下文介绍
如果您打算仅使用一个应用程序(app),则可以在很大程度上跳过本文。只要将您的应用程序传递给SQLAlchemy 构造函数,就可以设置好了。但是,如果要使用多个应用程序,或者要在要读取的功能中动态创建该应用程序。
223 0