技术分享云顶公开课

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

2019-07-19  本文已影响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数据库比较强多了。

上一篇下一篇

猜你喜欢

热点阅读