服务开发中的临时锁示例
1 简介
临时表锁的设计模式,临时表(Temporary Table)是一种特殊的表,它仅在当前会话(Session)中可见,且在会话结束时会自动删除。MySQL 采用临时表锁(Temporary Table Locks)来保证并发环境下临时表的安全访问。
(1) 临时表锁的作用
防止并发修改
由于 MySQL 的临时表是会话级的,每个连接的临时表是独立的,不会被其他连接访问。但在某些情况下(如 CREATE TEMPORARY TABLE … SELECT),MySQL 需要防止多个线程竞争相同的临时表,故使用临时表锁来保证一致性。
避免死锁
临时表的锁定可以防止多个事务同时修改相同的数据,从而避免死锁情况。
提高查询性能
MySQL 在某些复杂查询中可能会自动创建临时表(如 GROUP BY、ORDER BY、UNION 等),临时表锁确保这些查询的完整性,防止部分数据被多个线程并发修改。
(2) 临时表锁的类型
MySQL 主要通过**表级锁(Table Lock)**来保护临时表:
临时表互斥锁(Temporary Table Mutex):
在 CREATE TEMPORARY TABLE 期间,MySQL 会持有表级互斥锁,以防止并发创建相同名称的临时表。
读写锁(Read/Write Lock):
读取临时表时,加共享锁(S 锁),多个会话可以同时读取。
写入临时表时,加排他锁(X 锁),以保证数据的一致性。
2. 临时表锁的使用场景
大数据查询优化
复杂查询可能会使用临时表,如 GROUP BY、ORDER BY。
事务过程中可以使用临时表存储计算中间结果,提高查询效率。
会话级数据存储
某些数据仅在当前用户会话中有效,可使用临时表存储,如临时购物车数据。
批量数据处理
需要在短时间内存储大量数据并进行计算时,可使用临时表。
3. MySQL 临时表锁示例
(1) 创建临时表并加锁
START TRANSACTION;
CREATE TEMPORARY TABLE temp_orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
amount DECIMAL(10,2)
);
LOCK TABLE temp_orders WRITE;
INSERT INTO temp_orders (user_id, amount) VALUES (1, 100.50);
SELECT * FROM temp_orders;
UNLOCK TABLES;
COMMIT;
这里 LOCK TABLE temp_orders WRITE; 确保只有当前事务可以写入 temp_orders,防止并发访问。
UNLOCK TABLES; 释放表锁,使其他线程可以访问临时表。
(2) 多个事务并发访问
假设有两个事务:
– 事务 1
START TRANSACTION;
CREATE TEMPORARY TABLE temp_users (id INT, name VARCHAR(100));
LOCK TABLE temp_users WRITE;
INSERT INTO temp_users VALUES (1, 'Alice');
-- 此时事务 2 需要等待
COMMIT;
– 事务 2
START TRANSACTION;
CREATE TEMPORARY TABLE temp_users (id INT, name VARCHAR(100));
-- 由于事务 1 持有 WRITE 锁,事务 2 需要等待
INSERT INTO temp_users VALUES (2, 'Bob');
COMMIT;
事务 1 持有写锁,事务 2 只能等待事务 1 提交后再创建临时表。
4. 在web服务中使用临时表
Gin 是 Go 语言 Web 框架,通常用于构建 RESTful API。我们可以在 Gin 中结合 MySQL 的临时表锁,提高数据库操作的效率和并发安全性。
示例:批量查询优化
场景:前端请求多个 order_id 的订单详情,我们使用临时表存储 ID,再进行关联查询,提高查询效率。
(1) MySQL 方案
CREATE TEMPORARY TABLE temp_order_ids (
order_id INT PRIMARY KEY
);
INSERT INTO temp_order_ids VALUES (101), (102), (103);
SELECT o.* FROM orders o
JOIN temp_order_ids t ON o.id = t.order_id;
DROP TEMPORARY TABLE temp_order_ids;
这里使用临时表 temp_order_ids 存储 order_id,然后进行 JOIN 操作,优化查询。
(2) 服务中业务实现临时表锁
var db *sql.DB
func init() {
var err error
dsn := "root:password@tcp(127.0.0.1:3306)/testdb?parseTime=true"
db, err = sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(5)
}
// 订单查询
func getOrders(c *gin.Context) {
orderIDs := c.Query("order_ids") // 订单 ID,逗号分隔
ids := strings.Split(orderIDs, ",")
tx, err := db.Begin()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "事务开启失败"})
return
}
// 创建临时表
_, err = tx.Exec("CREATE TEMPORARY TABLE temp_order_ids (order_id INT PRIMARY KEY)")
if err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建临时表失败"})
return
}
// 插入数据
for _, id := range ids {
_, err = tx.Exec("INSERT INTO temp_order_ids VALUES (?)", id)
if err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"error": "插入数据失败"})
return
}
}
// 查询订单
rows, err := tx.Query(`
SELECT o.* FROM orders o
JOIN temp_order_ids t ON o.id = t.order_id`)
if err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"error": "查询订单失败"})
return
}
var results []map[string]interface{}
for rows.Next() {
var id int
var userID int
var amount float64
rows.Scan(&id, &userID, &amount)
results = append(results, gin.H{"id": id, "user_id": userID, "amount": amount})
}
// 释放资源
_, _ = tx.Exec("DROP TEMPORARY TABLE temp_order_ids")
tx.Commit()
c.JSON(http.StatusOK, results)
}
func main() {
r := gin.Default()
r.GET("/orders", getOrders)
r.Run(":8080")
}
5. 结论
临时表锁确保事务安全,防止并发访问带来的数据不一致问题。
MySQL 自动管理临时表,但在高并发场景下,可以手动加 LOCK TABLE 进一步控制访问。
服务中使用临时表能优化批量查询,减少 SQL 复杂度,提高查询性能。
事务管理 需要注意回滚(ROLLBACK)和资源释放(DROP TEMPORARY TABLE)。
这种方式能有效地提升并发查询的性能,并避免锁冲突问题。
- 点赞
- 收藏
- 关注作者
评论(0)