Python SQLAlchemy 中 with_for_update 行级锁用法详解
本文介绍 Python SQLAlchemy 中 with_for_update 方法的使用,替代已废弃的 with_lockmode。通过 nowait、of 等参数控制锁行为,并说明 MySQL InnoDB 引擎下明确指定主键可实现行级锁,否则可能升级为表级锁。
SQLAlchemy 行级锁实现方式
在 SQLAlchemy 中,实现行级锁主要有两种方法:with_lockmode(self, mode) 和 with_for_update(self, read=False, nowait=False, of=None)。需要注意的是,前者已在 SQLAlchemy 0.9.0 版本中被标记为废弃,官方推荐使用后者 with_for_update 来替代。因此,在实际开发中应统一使用 with_for_update。
接下来我们查看该函数的具体定义及用法。
with_for_update 函数定义与参数
@_generative()
def with_for_update(self, read=False, nowait=False, of=None):
"""return a new :class:`.Query` with the specified options for the
``FOR UPDATE`` clause.
The behavior of this method is identical to that of
:meth:`.SelectBase.with_for_update`. When called with no arguments,
the resulting ``SELECT`` statement will have a ``FOR UPDATE`` clause
appended. When additional arguments are specified, backend-specific
options such as ``FOR UPDATE NOWAIT`` or ``LOCK IN SHARE MODE``
can take effect.
E.g.::
q = sess.query(User).with_for_update(nowait=True, of=User)
The above query on a Postgresql backend will render like::
SELECT users.id AS users_id FROM users FOR UPDATE OF users NOWAIT
.. versionadded:: 0.9.0 :meth:`.Query.with_for_update` supersedes
the :meth:`.Query.with_lockmode` method.
.. seealso::
:meth:`.GenerativeSelect.with_for_update` - Core level method with
full argument and behavioral description.
"""
read
是标识加互斥锁还是共享锁. 当为 True 时, 即 for share 的语句, 是共享锁. 多个事务可以获取共享锁, 互斥锁只能一个事务获取. 有"多个地方"都希望是"这段时间我获取的数据不能被修改, 我也不会改", 那么只能使用共享锁.
nowait
其它事务碰到锁, 是否不等待直接"报错".
of
指明上锁的表, 如果不指明, 则查询中涉及的所有表(行)都会加锁.
示例代码如下: q = sess.query(User).with_for_update(nowait=True, of=User)
上述代码在支持的数据库后端(如 PostgreSQL)中,会生成对应的 SQL 语句。
生成的 SQL 如下: SELECT users.id AS users_id FROM users FOR UPDATE OF users NOWAIT

MySQL 下的锁行为差异
需要注意的是,MySQL 数据库并不支持 nowait 和 of 等特定参数。因此,在 MySQL 环境下,无论是否传递这些参数,最终转换生成的 SQL 语句均为:
SELECT users.id AS users_id FROM users FOR UPDATE
以下是一个完整的 Python 代码示例。
代码示例与输出结果
def query_city_for_update():
session = get_session()
with session.begin():
query = session.query(City).with_for_update().filter(City.ID == 8)
print 'SQL : %s' % str(query)
print_city_info(query.first())
运行上述代码后,控制台输出的结果如下。
SQL : SELECT city."ID" AS "city_ID", city."Name" AS "city_Name", city."CountryCode" AS "city_CountryCode", city."District" AS "city_District", city."Population" AS "city_Population"
FROM city
WHERE city."ID" = :ID_1 FOR UPDATE
{'city': {'population': 234323, 'district': u'Utrecht', 'id': 8, 'country_code': u'NLD', 'name': u'Utrecht'}}
使用 SELECT ... FOR UPDATE 时,锁定的数据范围是关键注意事项。由于 MySQL 的 InnoDB 引擎默认采用行级锁(Row-Level Lock),只有当查询条件中明确指定了主键时,MySQL 才会执行行级锁,即仅锁定被选取的数据行。如果未明确指定主键,MySQL 可能会执行表级锁(Table Lock),导致整个数据表单被锁定。
以上内容涵盖了 SQLAlchemy 中 with_for_update 的核心用法及 MySQL 下的锁机制差异,希望对读者有所帮助。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









