美文网首页技术分享云顶公开课
基于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框架解决抢票遇到的高并发问题

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