面试题答案
一键面试代码实现
import mysql.connector
def transfer_funds(from_account_id, to_account_id, amount):
cnx = mysql.connector.connect(user='your_user', password='your_password',
host='127.0.0.1',
database='your_database')
cursor = cnx.cursor()
try:
# 开启事务
cnx.start_transaction()
# 检查转出账户余额是否足够
check_query = "SELECT balance FROM accounts WHERE account_id = %s"
cursor.execute(check_query, (from_account_id,))
result = cursor.fetchone()
if result is None or result[0] < amount:
raise ValueError("Insufficient funds")
# 更新转出账户余额
update_from_query = "UPDATE accounts SET balance = balance - %s WHERE account_id = %s"
cursor.execute(update_from_query, (amount, from_account_id))
# 更新转入账户余额
update_to_query = "UPDATE accounts SET balance = balance + %s WHERE account_id = %s"
cursor.execute(update_to_query, (amount, to_account_id))
# 提交事务
cnx.commit()
print("Transfer successful")
except mysql.connector.Error as err:
# 发生错误时回滚事务
cnx.rollback()
print(f"Error: {err}")
finally:
cursor.close()
cnx.close()
代码解释
- 数据库连接:使用
mysql.connector.connect
方法连接到MySQL数据库。 - 开启事务:通过
cnx.start_transaction()
开启一个事务,确保转账操作的原子性。 - 检查余额:使用绑定变量
%s
查询转出账户的余额,检查余额是否足够。如果余额不足,抛出ValueError
异常。 - 更新账户余额:使用绑定变量
%s
分别更新转出账户和转入账户的余额。绑定变量可以防止SQL注入攻击,同时在事务处理中有助于确保数据一致性。 - 提交事务:如果所有操作都成功,通过
cnx.commit()
提交事务,使更改持久化到数据库。 - 错误处理:如果在事务执行过程中发生错误,通过
cnx.rollback()
回滚事务,撤销所有未提交的更改。 - 关闭连接:使用
cursor.close()
和cnx.close()
关闭游标和数据库连接。
处理并发访问问题
- 幻读:幻读是指在一个事务中多次查询,在两次查询之间另一个事务插入了新的数据,导致第二次查询结果中出现了第一次查询时不存在的数据。可以通过设置事务隔离级别为
SERIALIZABLE
来避免幻读。在MySQL中,可以在连接数据库后设置事务隔离级别:
cnx = mysql.connector.connect(user='your_user', password='your_password',
host='127.0.0.1',
database='your_database')
cnx.start_transaction(isolation_level='SERIALIZABLE')
在SERIALIZABLE
隔离级别下,事务执行时会对整个数据集加锁,其他事务无法在该事务执行期间插入新数据,从而避免幻读。
2. 丢失更新:丢失更新是指两个事务同时读取同一数据并进行更新,后提交的事务覆盖了先提交的事务的更新结果。可以通过设置事务隔离级别为REPEATABLE READ
来避免丢失更新。在REPEATABLE READ
隔离级别下,事务在读取数据时会对数据加锁,直到事务结束,其他事务无法修改该数据,从而避免丢失更新。同样可以在连接数据库后设置事务隔离级别:
cnx = mysql.connector.connect(user='your_user', password='your_password',
host='127.0.0.1',
database='your_database')
cnx.start_transaction(isolation_level='REPEATABLE READ')
绑定变量本身并不能直接处理并发访问问题,但在事务中使用绑定变量可以确保数据的一致性,避免在并发环境下因SQL注入等问题导致的数据错误。同时,合理设置事务隔离级别,可以有效避免幻读、丢失更新等并发问题。