面试题答案
一键面试1. 触发器架构设计
- 触发器类型选择:使用
AFTER INSERT
触发器,因为我们需要在新交易记录插入transactions
表之后,再去更新accounts
表。 - 事务处理:SQLite 本身支持自动事务,每个 SQL 语句默认在一个事务中执行。但为了明确和确保数据一致性,我们可以在触发器中手动控制事务。
- 减少锁争用:
- 尽量缩短事务的持续时间。在更新
accounts
表时,尽可能快速地完成操作并提交事务。 - 按照固定顺序访问资源,例如总是先访问
transactions
表,再访问accounts
表,以避免死锁。
- 尽量缩短事务的持续时间。在更新
2. 性能优化
- 索引优化:
- 在
transactions
表的account_id
列上创建索引,这样在根据account_id
查找相关记录时可以提高查询性能。 - 在
accounts
表的account_id
列上创建唯一索引,确保account_id
的唯一性,并且在更新余额时能够快速定位记录。
CREATE INDEX idx_transactions_account_id ON transactions (account_id); CREATE UNIQUE INDEX idx_accounts_account_id ON accounts (account_id);
- 在
- 批量操作:如果可能,尽量批量插入交易记录,减少触发器触发次数。
3. 事务回滚和并发冲突处理策略
- 事务回滚:如果在更新
accounts
表余额时发生错误(例如余额不足等情况),需要回滚整个事务。在 SQLite 中,当触发器内的 SQL 语句执行失败时,整个事务会自动回滚。 - 并发冲突处理:SQLite 使用
PRAGMA
设置来处理并发冲突。默认情况下,SQLite 使用ROLLBACK
模式处理冲突,即当发生冲突时,回滚整个事务。可以根据具体需求调整为ABORT
、FAIL
、IGNORE
或REPLACE
模式。例如,使用PRAGMA conflict_handling = ABORT;
来在发生冲突时立即终止事务并返回错误。
4. 关键 SQLite 代码示例
-- 创建触发器
CREATE TRIGGER update_account_balance
AFTER INSERT ON transactions
BEGIN
-- 根据交易类型更新账户余额
UPDATE accounts
SET balance = CASE
WHEN NEW.transaction_type = 'DEPOSIT' THEN balance + NEW.amount
WHEN NEW.transaction_type = 'WITHDRAWAL' THEN balance - NEW.amount
ELSE balance
END
WHERE account_id = NEW.account_id;
END;
上述代码创建了一个 AFTER INSERT
触发器,在每次向 transactions
表插入新记录时,根据 transaction_type
更新 accounts
表中的账户余额。同时,结合索引优化、事务自动回滚以及通过 PRAGMA
设置处理并发冲突等策略,确保在高并发读写场景下的数据一致性和性能。