美文网首页技术分享云顶公开课
基于MySQL数据库和Koa2框架解决抢票遇到的高并发问题

基于MySQL数据库和Koa2框架解决抢票遇到的高并发问题

作者: 好一只帅卤蛋 | 来源:发表于2019-07-19 19:58 被阅读0次

基于MySQL数据库和Koa2框架解决抢票遇到的高并发问题

学了这么久nodejs,一直说NodeJS适合运用在高并发、I/O密集、少量业务逻辑的场景,今天终于派上了用场,现在面临的问题是如何处理新生们听我们宣讲会抢票的问题。

一开始的思路

因为抢票肯定要控制票的数量,所以,我一开始的思路是每次抢票之后,获取数据库中票的总数量,然后跟后台写的票的常量做对比,如果数据库中存的票的数量小于自定义票的常量,那就返回抢票结果和(数据库中票的数量+1)就是抢票者抢到的票编号,同时将票和编号和用户id存入数据库。

下面是封装的数据库的操作

/* 高并发接口测试 */
const config = require('../config/Database');
// sql查询表中数据总条:SELECT COUNT(*) FROM 表名称。
// 返回大于 20 岁的人数:
// SELECT COUNT(*) FROM Personsinfo WHERE Age>20

// 增
exports.add = function (addSqlParams) {

    let addSql = 'INSERT INTO briefing(brief_session,brief_time,user_cloudId,brief_sign,brief_number) VALUES(?,?,?,?,?)';

    return new Promise(function (resolve, reject) {

        config.query(addSql, addSqlParams, function (err, result) {

            resolve(result);

            if (err) {
                console.log(err);
                reject(err.message);
            }

            console.log('INSERT ID:', result);
          
        });
    });
}

// 获得数据库中的总票数
exports.get_ticket = function (addSqlParams) {

    let addSql = 'SELECT count(*) AS count FROM briefing WHERE brief_session=' + "'" + addSqlParams + "'";

    return new Promise(function (resolve, reject) {

        config.query(addSql, addSqlParams, function (err, result) {

            resolve(result[0]);

            if (err) {
                console.log(err);
                reject(err.message);
            }

            console.log('INSERT ID:', result);

        });
    });
}

下面是业务逻辑层的操作

/* 高并发接口测试 */
const DB = require("../dbhelper/db_high");
const uuid = require('uuid-v4');

exports.high = async (ctx, next) => {

    // 这是票的个数,从数据库里面获取
    let ticket_count = 30;
    // 前两个参数要从数据库中获取
    let brief_session = "第三场"
    let brief_time = "2019-07-15 15:16:45"
    // 后三个参数,第一个是前端传回来的,后两个是自己定的
    let user_cloudId = uuid().replace(/-/g, '')
    let brief_sign = 0;

    // 获取数据库中现在有的票数
    let result1 = await DB.get_ticket(brief_session);

    console.log(result1.count)
    if (result1.count < ticket_count) {

        result1.count = result1.count + 1;

        let addparams = [brief_session, brief_time, user_cloudId, brief_sign, result1.count];
        console.log(result1.count)
        let result = await DB.add(addparams);
        console.log(result);
        ctx.response.body = {
            brief_number: result1.count,
            state: '抢票成功',
            code: '1'
        }


    } else {
        ctx.response.body = {
            state: '抢票失败',
            code: '0'
        }
    }

}


// 关闭抢票接口的同时 将brief_number清零


我用网页进行单独测试的时候,能够一张一张的返回正确结果


网页测试

可是当用高并发测试工具时候


高并发测试
高并发测试结果

就会发现,数据库中会出现重复的数据
一张票被好多个人同时抢上,我突然意识到了自己的问题,当用户抢票时直接对数据库进行数量的读取操作,不仅耗费了时间,而且,会出现逻辑上来不及判断,导致一张票被好多人抢的问题

正确的思路

正确的思路应该是设置一个全局变量brief_number 来控制票的编号

/* 高并发接口测试 */
const DB = require("../dbhelper/db_high");
const uuid = require('uuid-v4');
let brief_number = 0;

exports.high = async (ctx, next) => {

    // 这是票的个数,从数据库里面获取
    let ticket_count = 10;
    // 前两个参数要从数据库中获取
    let brief_session = "第三场"
    let brief_time = "2019-07-15 15:16:45"
    // 后三个参数,第一个是前端传回来的,后两个是自己定的
    let user_cloudId = uuid().replace(/-/g, '')
    let brief_sign = 0;

    // 获取数据库中现在有的票数
    // let result1 = await DB.get_ticket(brief_session);

    // console.log(result1.count)
    if (brief_number < ticket_count) {

        brief_number = brief_number + 1;

        let addparams = [brief_session, brief_time, user_cloudId, brief_sign, brief_number];
        console.log(brief_number)
        let result = await DB.add(addparams);
        console.log(result);
        ctx.response.body = {
            brief_number: brief_number,
            state: '抢票成功',
            code: '1'
        }

    } else {
        ctx.response.body = {
            state: '抢票失败',
            code: '0'
        }
    }

}


// 关闭抢票接口的同时 将brief_number清零

如图所示,当处理5000个人的并发抢100张票时候可以将响应时间控制在两秒左右,四秒以下


高并发测试

正宗的解决思路

上面那个用全局变量的方法虽然快,但是有一个问题,如果并发量到达一定程度的话,服务器崩掉重启之后,会将原来的抢票信息清空,不过一般配一个性能好一点的服务器不会出现这样的问题。为了保险起见,还是使用redis数据库。

解决思路:将票的总数存入redis数据库,每次用户抢票比较redis数据库中的数据总量,并将抢票信息存入数据库。速度上虽然比不上内存,但是一定比存入MySQL数据库比较强多了。

相关文章

  • 基于MySQL数据库和Koa2框架解决抢票遇到的高并发问题

    基于MySQL数据库和Koa2框架解决抢票遇到的高并发问题 学了这么久nodejs,一直说NodeJS适合运用在高...

  • 数据库锁机制

    本文所有内容基于MySQL/InnoDB引擎。 并发事务解决方案 脏读、不可重复读和幻读都是数据库读一致性问题,需...

  • 写在学习golang一个月后

    遇到的问题 连接池。由于PHP没有连接池,当高并发时就会有大量的数据库连接直接冲击到MySQL上,最终导致数据库挂...

  • 写在学习golang一个月后

    遇到的问题 连接池。由于PHP没有连接池,当高并发时就会有大量的数据库连接直接冲击到MySQL上,最终导致数据库挂...

  • 【阅读笔记002】之基础知识摘抄

    【NoSQL数据库简介】 常见的关系型数据库如:MySQL、Oracle、RDBMS等存在的问题: ①.高并发时的...

  • Mysql之脏读、不可重复读、幻读的区别

    我用的Mysql版本是5.7.19。 事物高并发的三种异常问题 数据库在在高并发时,事物会出现三种异常问题。 脏读...

  • NoSql

    什么是NoSql 为了解决高并发、高可扩展、高可用、大数据存储问题而产生的数据库解决方案,就是NoSql数据库。 ...

  • php+redis消息队列实现抢购功能

    实现功能: 基于redis队列,防止高并发的超卖 基于mysql的事务加排它锁,防止高并发的超卖基于redis队列...

  • Mac Mysql数据库中文乱码问题解决

    Mac 下MySQL数据库中文乱码解决方案:当我们用框架进行数据库的存储操作时,经常会遇到中文乱码的问题。如:在使...

  • 关于秒杀抢库存相关的一点理解

    秒杀场景通常需要解决两个问题:1、高并发2、库存超卖 对于高并发问题,我们可以使用缓存系统来解决,避免直接对数据库...

网友评论

    本文标题:基于MySQL数据库和Koa2框架解决抢票遇到的高并发问题

    本文链接:https://www.haomeiwen.com/subject/vofelctx.html