本地事务不支持跨库操作
方案1
将有关联的表放在一个数据库中
1. 同一个数据库操作 可以使用一个事务
2. eg:用户表& 用户频道表 ,文章基本信息表 & 文章内容表
方案2
Mysql从5.6开始支持分布式事务
核心是二阶段提交协议(简称 2PC协议 / XA协议)
分布式事务会提供一个 事务管理器 来对 各数据库的本地事务进行统一管理, 只有各本地事务都向管理器 预提交 成功后, 事务管理器才会统一执行提交处理, 否则统一进行回滚处理
注意: 在Flask框架中,sqlalchemy 也支持分布式事务
我们 只需要在创建 SQLAlchemy对象时, 设置参数 session_options={‘twophase’: True}即可
设置后, 整个session的所有操作会被放入到一个分布式事务中, 并在整个分布式事务范围内保证原子性
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# 设置多个数据库地址
app.config['SQLALCHEMY_BINDS'] = {
'db1': 'mysql://root:mysql@192.168.105.140:3306/db1',
'db2': 'mysql://root:mysql@192.168.105.140:3306/db2'
}
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_ECHO'] = True
# 创建组件对象 设置二阶段提交
db = SQLAlchemy(app, session_options={'twophase': True})
# 用户表
class User(db.Model):
__tablename__ = 't_user'
__bind_key__ = 'db1' # 设置数据库db1
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20))
# 地址表
class Address(db.Model):
__tablename__ = 't_adr'
__bind_key__ = 'db2' # 设置数据库db2
id = db.Column(db.Integer, primary_key=True)
detail = db.Column(db.String(20), unique=True)
user_id = db.Column(db.Integer)
@app.route('/')
def index():
"""添加数据"""
user1 = User(name='张三')
db.session.add(user1)
db.session.flush()
adr1 = Address(detail='中关村3号', user_id=user1.id)
adr2 = Address(detail='华强北5号', user_id=user1.id)
db.session.add_all([adr1, adr2])
db.session.flush()
db.session.commit() # 由于采用了分布式事务, 整个session的操作会被放入到一个分布式事务中, 并实现事务的原子性
return "index"
@app.route('/demo1')
def demo1():
"""查询多表数据 需求: 查询姓名为"张三"的所有地址信息"""
# 先根据姓名查找用户主键
user1 = User.query.filter_by(name='张三').first()
# 再根据主键到从表查询关联地址
adrs = Address.query.filter_by(user_id=user1.id).all()
for adr in adrs:
print(adr.detail)
return 'demo1'
if __name__ == '__main__':
# 删除所有继承自db.Model的表
db.drop_all()
# 创建所有继承自db.Model的表
db.create_all()
app.run(debug=True)
注意点:
分布式事务要在所有事务都"提交成功"的情况下才会正式提交, 如果参与的部分节点卡顿, 会影响整个事务的性能
方案3
基于状态/消息的最终一致性方案
1.对于 包含多个子系统的大型项目, 需要保证子系统之间的数据一致性
2.单个子系统往往不会操作所有数据库, 但是 每个子系统可以通过定义字段来记录操作的状态, 每完成一个阶段则更新相应的状态
3.如我们在学习过程中经常使用的下单-付款流程中, 应用A的下单事务完成后更新订单状态为已下单, 应用B付款事务完成后, 再通过 支付回调接口 通知应用A 更新订单状态,应用B还需要提供一个 支付查询接口, 以便在用户查询或者订单超时的情况下, 让应用A可以查询订单的支付情况ebay提出的方案, 理论叫做BASE