服务开发中的临时锁示例

举报
码乐 发表于 2025/02/03 08:48:08 2025/02/03
【摘要】 1 简介临时表锁的设计模式,临时表(Temporary Table)是一种特殊的表,它仅在当前会话(Session)中可见,且在会话结束时会自动删除。MySQL 采用临时表锁(Temporary Table Locks)来保证并发环境下临时表的安全访问。(1) 临时表锁的作用防止并发修改由于 MySQL 的临时表是会话级的,每个连接的临时表是独立的,不会被其他连接访问。但在某些情况下(如 ...

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)。
这种方式能有效地提升并发查询的性能,并避免锁冲突问题。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。