MySQL频繁更新热点数据高并发场景下的具体解决方案

 更新时间:2025年10月29日 08:53:39   作者:学亮编程手记  
本文详细介绍了MySQL高频更新热点数据在高并发场景下的解决方案,包括应用层缓存、数据分片、队列削峰、数据库层面优化(如CAS、调整事务隔离级别)、读写分离、请求合并、参数调优、分布式计数器等方法,需要的朋友可以参考下

热点数据问题确实是高并发场景下的典型瓶颈。以下是针对热点数据问题的具体解决方案:

1. 应用层缓存 + 批量更新

使用 Redis 计数器

// 应用层累加,定期批量更新到数据库
public class HotSpotCounter {
    private Jedis jedis;
    
    public void increment(String key) {
        // 在Redis中累加
        jedis.incr(key);
    }
    
    // 定时任务,批量同步到数据库
    @Scheduled(fixedRate = 5000) // 每5秒同步一次
    public void syncToDatabase() {
        Set<String> keys = jedis.keys("counter:*");
        for (String key : keys) {
            Long value = Long.parseLong(jedis.get(key));
            String entityId = key.substring(8); // 去掉"counter:"前缀
            
            // 批量更新数据库
            updateDatabase(entityId, value);
            
            // 清空Redis计数器
            jedis.set(key, "0");
        }
    }
}

2. 数据分片(水平拆分)

将单行热点数据拆分为多行

-- 原始热点表
CREATE TABLE page_views (
    page_id INT PRIMARY KEY,
    view_count BIGINT
);

-- 拆分为多行
CREATE TABLE page_views_sharded (
    page_id INT,
    shard_id INT,  -- 分片ID (0-15)
    view_count BIGINT,
    PRIMARY KEY (page_id, shard_id)
);

-- 更新时分散到不同分片
UPDATE page_views_sharded 
SET view_count = view_count + 1 
WHERE page_id = 1001 AND shard_id = RAND() * 16;

-- 查询时汇总
SELECT SUM(view_count) FROM page_views_sharded WHERE page_id = 1001;

3. 队列削峰填谷

使用消息队列缓冲写请求

@Component
public class ViewCountService {
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    
    public void recordView(int pageId) {
        // 发送到消息队列,异步处理
        kafkaTemplate.send("page-view-topic", String.valueOf(pageId));
    }
}

// 消费者,批量处理
@KafkaListener(topics = "page-view-topic")
public void batchUpdateViews(List<String> pageIds) {
    Map<Integer, Long> countMap = pageIds.stream()
        .collect(Collectors.groupingBy(Integer::parseInt, Collectors.counting()));
    
    // 批量更新数据库
    for (Map.Entry<Integer, Long> entry : countMap.entrySet()) {
        updatePageView(entry.getKey(), entry.getValue());
    }
}

4. 数据库层面的优化

使用 CAS (Compare-And-Set) 更新

-- 基于当前值的更新,减少锁竞争
UPDATE products 
SET stock = stock - 1 
WHERE id = 1001 AND stock > 0;

-- 或者使用版本号
UPDATE products 
SET stock = stock - 1, version = version + 1 
WHERE id = 1001 AND version = @current_version;

调整事务隔离级别(临时方案)

-- 对于计数类操作,使用READ-COMMITTED + 短事务
SET SESSION transaction_isolation = 'READ-COMMITTED';
BEGIN;
UPDATE counters SET value = value + 1 WHERE name = 'page_views';
COMMIT;

5. 读写分离

写主库,读从库

-- 写操作指向主库
UPDATE hot_table SET count = count + 1 WHERE id = 1; -- 主库

-- 读操作指向从库  
SELECT count FROM hot_table WHERE id = 1; -- 从库

6. 应用层合并请求

请求合并窗口

public class RequestMerger {
    private Map<Integer, AtomicLong> counterMap = new ConcurrentHashMap<>();
    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    
    public RequestMerger() {
        // 每100ms批量处理一次
        scheduler.scheduleAtFixedRate(this::flush, 100, 100, TimeUnit.MILLISECONDS);
    }
    
    public void increment(int id) {
        counterMap.computeIfAbsent(id, k -> new AtomicLong(0)).incrementAndGet();
    }
    
    private void flush() {
        Map<Integer, Long> snapshot = new HashMap<>();
        counterMap.forEach((id, atomic) -> {
            long value = atomic.getAndSet(0);
            if (value > 0) {
                snapshot.put(id, value);
            }
        });
        
        // 批量更新数据库
        batchUpdate(snapshot);
    }
}

7. 数据库参数调优

优化 InnoDB 参数

-- 增加锁相关内存
SET GLOBAL innodb_buffer_pool_size = 8G;  -- 根据内存调整
SET GLOBAL innodb_log_file_size = 2G;     -- 增大日志文件
SET GLOBAL innodb_lock_wait_timeout = 10; -- 减少锁等待时间

-- 调整线程并发数
SET GLOBAL innodb_thread_concurrency = 0; -- 0表示不限制

8. 架构层面的解决方案

使用分布式计数器

// 使用 Redis Cluster 分散热点
public class DistributedCounter {
    public void increment(String key) {
        // 使用CRC32分片到不同的Redis节点
        int slot = CRC32.hash(key) % 16384;
        String redisNode = getNodeBySlot(slot);
        redisTemplate(redisNode).opsForValue().increment(key);
    }
}

实际案例:电商库存热点

问题场景

-- 热点商品库存更新,秒杀时大量并发
UPDATE products SET stock = stock - 1 WHERE id = 1001 AND stock > 0;

解决方案

-- 1. 库存分片
CREATE TABLE product_stock_shard (
    product_id INT,
    shard_id TINYINT, -- 0-9 10个分片
    stock INT,
    PRIMARY KEY (product_id, shard_id)
);

-- 2. 更新时随机选择分片
UPDATE product_stock_shard 
SET stock = stock - 1 
WHERE product_id = 1001 AND shard_id = FLOOR(RAND() * 10) AND stock > 0;

-- 3. 检查是否成功,如果失败重试其他分片

监控和诊断

监控热点行锁

-- 查看行锁等待
SELECT * FROM information_schema.INNODB_LOCKS 
WHERE lock_table = 'your_hot_table';

-- 查看锁等待关系
SELECT * FROM information_schema.INNODB_LOCK_WAITS;

监控数据库状态

-- 查看InnoDB状态
SHOW ENGINE INNODB STATUS;

-- 查看当前运行的事务
SELECT * FROM information_schema.INNODB_TRX 
ORDER BY trx_started DESC 
LIMIT 10;

选择策略的建议

  • 轻度热点:应用层缓存 + 批量更新
  • 中度热点:数据分片 + 队列削峰
  • 重度热点:分布式计数器 + 读写分离
  • 秒杀场景:预扣库存 + 异步最终一致性

关键原则:将串行更新改为并行更新,将实时更新改为批量更新,将单点压力分散到多个节点。

以上就是MySQL频繁更新热点数据高并发场景下的具体解决方案的详细内容,更多关于MySQL频繁更新热点数据的资料请关注脚本之家其它相关文章!

相关文章

  • mysql数据校验过程中的字符集问题处理

    mysql数据校验过程中的字符集问题处理

    在日常应用中,我们经常会遇到在不同的字符集的数据库直接进行数据的导入导出操作,针对这个问题,我们来进行讨论下
    2014-05-05
  • mysql -参数thread_cache_size优化方法 小结

    mysql -参数thread_cache_size优化方法 小结

    以下是某门户网站的mysql状态实例及分析过程,绝对的第一手数据资料,很生动的体现了参数thread_cache_size优化的效果及优化该参数的必要性,希望对各位系统管理员能有帮助。
    2011-03-03
  • 在MySQL中使用子查询和标量子查询的基本操作教程

    在MySQL中使用子查询和标量子查询的基本操作教程

    这篇文章主要介绍了在MySQL中使用子查询和标量子查询的基本操作教程,子查询的使用时MySQL入门学习中的基础知识,需要的朋友可以参考下
    2015-12-12
  • Mysql定时数据库备份实现的保姆级教程

    Mysql定时数据库备份实现的保姆级教程

    数据备份本身主要是为了预防一些意外,例如服务器或者个人电脑的硬件故障、人为的错误操作等情况,这篇文章主要给大家介绍了Mysql定时数据库备份实现的保姆级教程,需要的朋友可以参考下
    2024-12-12
  • MYSQL造数据占用临时表空间的解决方法

    MYSQL造数据占用临时表空间的解决方法

    在MySQL中,临时表空间并不是一个可以直接删除的文件或目录,因为临时表空间通常是由MySQL服务器在运行时根据需要自动创建和管理的,这篇文章主要介绍了MYSQL造数据占用临时表空间,需要的朋友可以参考下
    2024-05-05
  • Mysql写入数据十几秒后被自动删除了如何解决

    Mysql写入数据十几秒后被自动删除了如何解决

    这篇文章主要介绍了Mysql写入数据十几秒后被自动删除了如何解决,文章通过围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09
  • Linux手动部署远程的mysql数据库的方法详解

    Linux手动部署远程的mysql数据库的方法详解

    这篇文章主要介绍了Linux手动部署远程的mysql数据库的方法详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • MySQL多表查询、事务与索引的实践与应用操作

    MySQL多表查询、事务与索引的实践与应用操作

    本文围绕MySQL数据库操作展开,通过构建部门与员工管理、餐饮业务相关的数据库表,并填充测试数据,系统地阐述了多表查询的多种方式,包括内连接、外连接和不同类型的子查询,同时介绍了事务的处理以及索引的创建、查询和删除操作,感兴趣的朋友一起看看吧
    2025-04-04
  • MySQL临时表的使用方法举例详解

    MySQL临时表的使用方法举例详解

    MySQL临时表在我们需要保存一些临时数据时是非常有用的,这篇文章主要介绍了MySQL临时表的使用方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-06-06
  • Linux下Mysql5.6 二进制安装过程

    Linux下Mysql5.6 二进制安装过程

    这篇文章主要介绍了Linux下Mysql5.6 二进制安装过程,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-06-06

最新评论