zoukankan      html  css  js  c++  java
  • Nodejs模拟并发,尝试的两种解决方案

     一、准备数据库表

    创建商品库存表 db_stock ,插入一条数据

    DROP TABLE IF EXISTS `db_stock`;
    CREATE TABLE `db_stock`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `goods_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品id',
      `inventory_total` int(11) NULL DEFAULT NULL COMMENT '总库存',
      `inventory_remain` int(11) NULL DEFAULT NULL COMMENT '剩余库存',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '记录实时库存表' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of db_stock
    -- ----------------------------
    INSERT INTO `db_stock` VALUES (1, 'goods_01', 100, 100);
    
    SET FOREIGN_KEY_CHECKS = 1;

    创建出库信息表 db_checkout 

    DROP TABLE IF EXISTS `db_checkout`;
    CREATE TABLE `db_checkout`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `goods_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品id',
      `opt_num` int(11) NULL DEFAULT NULL COMMENT '操作数量',
      `inventory_remain` int(11) NULL DEFAULT NULL COMMENT '剩余库存',
      `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '出库记录表' ROW_FORMAT = Dynamic;
    
    SET FOREIGN_KEY_CHECKS = 1;

    查看数据库

    mysql> select * from db_stock;
    +----+----------+-----------------+------------------+
    | id | goods_id | inventory_total | inventory_remain |
    +----+----------+-----------------+------------------+
    |  1 | goods_01 |             100 |              100 |
    +----+----------+-----------------+------------------+
    1 row in set (0.00 sec)
    
    mysql> select * from db_checkout;
    Empty set (0.00 sec)
    
    mysql>

    二、准备Nodejs接口(基于Koa2)

      接口说明:

      url    dm/testConcurrentOrder

      method   get

      data   initDB //首次初始化数据

           goods_id //商品id

           opt_num //出库数量



    router.js

    const router = require('koa-router')();
    const dm_model = require("../models/dm_model");

    router.preffix("/dm");
    /** * 测试并发 * testConcurrentOrder */ router.get('/testConcurrentOrder', async function (ctx) { let reqParams = ctx.reqParams; try { ctx.body = await dm_model.testConcurrentOrder(reqParams); } catch (e) { ctx.body = { success: false, msg: `出库异常: ${e.toString()}`, } } }); module.exports = router;

     model.js

    let db = require("../utils/mysql_util").db;
    let moment = require("moment"); let model = {}; /** * 测试并发 * @param reqParams * @returns {Promise<{msg: string, success: boolean}>} */ model.testConcurrentOrder = async function (reqParams) { let initDB = !!reqParams.initDB || 0;// 是否初始化db let goods_id = reqParams.goods_id || "goods_01";// 商品id let opt_num = reqParams.opt_num || 1;// 出库数量 if (initDB) { // 清除数据 await db.query(`TRUNCATE db_stock`); await db.query(`TRUNCATE db_checkout`); // 插入库存数据 await db.query(`INSERT INTO db_stock(goods_id, inventory_total, inventory_remain) VALUES ('${goods_id}', 100, 100)`); return { success: true, msg: '初始化DB成功', } } else { let tran = await db.beginTransaction();
         // 查询剩余库存
            let querySql = `SELECT
        t.inventory_total,
        t.inventory_remain 
    FROM
        db_stock t 
    WHERE
        1 = 1 
        AND t.goods_id = ?`;
    
            try {
                let result = {};
    
                let queryResult = await tran.queryOne(querySql, [goods_id]);
                if (!queryResult) {
                    result = {
                        success: false,
                        msg: `无法匹配商品id:${goods_id}`
                    }
                } else {
                    let inventory_remain = queryResult.inventory_remain - 1;
    
                    // 新增出库记录
                    let checkoutSql = `INSERT INTO db_checkout(goods_id, opt_num, inventory_remain, create_time) VALUES (?, ?, ?, ?)`;
                    await tran.query(checkoutSql, [goods_id, opt_num, inventory_remain, new Date()]);
    
                    // 更新库存信息
                    let updateStockSql = `UPDATE db_stock t SET inventory_remain = ? WHERE t.goods_id = ?`;
                    // 更新库存
                    await tran.query(updateStockSql, [inventory_remain, goods_id]);
                    result = {
                        success: true,
                        msg: '出库成功',
                    }
                }
                await tran.commit();
                return result;
            } catch (e) {
                await tran.rollback();
                throw e;
            }
        }
    
    };
    
    module.exports = model;

     三、并发测试

    总共请求20次,20个并发同时请求

    C:WINDOWSsystem32>ab.exe -n 20 -c 20  http://localhost:3000/dm/testConcurrentOrder
    This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/
    
    Benchmarking localhost (be patient).....done
    
    
    Server Software:
    Server Hostname:        localhost
    Server Port:            3000
    
    Document Path:          /dm/testConcurrentOrder
    Document Length:        228 bytes
    
    Concurrency Level:      20
    Time taken for tests:   24.284 seconds
    Complete requests:      20
    Failed requests:        0
    Total transferred:      7420 bytes
    HTML transferred:       4560 bytes
    Requests per second:    0.82 [#/sec] (mean)
    Time per request:       24284.077 [ms] (mean)
    Time per request:       1214.204 [ms] (mean, across all concurrent requests)
    Transfer rate:          0.30 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    0   0.4      0       1
    Processing:  1590 2674 4696.9   1638   22629
    Waiting:     1589 2673 4696.2   1637   22625
    Total:       1590 2674 4696.9   1638   22629
    
    Percentage of the requests served within a certain time (ms)
      50%   1638
      66%   1644
      75%   1648
      80%   1651
      90%   1655
      95%  22629
      98%  22629
      99%  22629
     100%  22629 (longest request)
    
    C:WINDOWSsystem32>

    查看并发请求后,数据库表的信息

    mysql> select * from db_stock;
    +----+----------+-----------------+------------------+
    | id | goods_id | inventory_total | inventory_remain |
    +----+----------+-----------------+------------------+
    |  1 | goods_01  |             100 |               97 |
    +----+----------+-----------------+------------------+
    1 row in set (0.00 sec)
    
    mysql> select * from db_checkout;
    +----+----------+---------+------------------+---------------------+
    | id | goods_id | opt_num | inventory_remain | create_time         |
    +----+----------+---------+------------------+---------------------+
    | 61 | goods_01  |       1 |               99 | 2019-10-09 17:31:01 |
    | 62 | goods_01  |       1 |               98 | 2019-10-09 17:31:01 |
    | 63 | goods_01  |       1 |               98 | 2019-10-09 17:31:01 |
    | 64 | goods_01  |       1 |               98 | 2019-10-09 17:31:01 |
    | 65 | goods_01  |       1 |               98 | 2019-10-09 17:31:01 |
    | 66 | goods_01  |       1 |               98 | 2019-10-09 17:31:01 |
    | 67 | goods_01  |       1 |               98 | 2019-10-09 17:31:01 |
    | 68 | goods_01  |       1 |               98 | 2019-10-09 17:31:01 |
    | 69 | goods_01  |       1 |               98 | 2019-10-09 17:31:01 |
    | 70 | goods_01  |       1 |               98 | 2019-10-09 17:31:01 |
    | 71 | goods_01  |       1 |               98 | 2019-10-09 17:31:01 |
    | 72 | goods_01  |       1 |               97 | 2019-10-09 17:31:01 |
    | 73 | goods_01  |       1 |               97 | 2019-10-09 17:31:01 |
    | 74 | goods_01  |       1 |               97 | 2019-10-09 17:31:01 |
    | 75 | goods_01  |       1 |               97 | 2019-10-09 17:31:01 |
    | 76 | goods_01  |       1 |               97 | 2019-10-09 17:31:01 |
    | 77 | goods_01  |       1 |               97 | 2019-10-09 17:31:01 |
    | 78 | goods_01  |       1 |               97 | 2019-10-09 17:31:01 |
    | 79 | goods_01  |       1 |               97 | 2019-10-09 17:31:01 |
    | 80 | goods_01  |       1 |               97 | 2019-10-09 17:31:01 |
    +----+----------+---------+------------------+---------------------+
    20 rows in set (0.00 sec)
    
    mysql>

     结果:并发请求20次,由于代码和数据库没有控制并发,导致数据错误。

    四:解决方案

    1、加行锁

    在查询剩余库存的时候调整sql为
    // 查询剩余库存
    let querySql = `SELECT
        t.inventory_total,
        t.inventory_remain 
    FROM
        db_stock t 
    WHERE
        1 = 1 
        AND t.goods_id = ? for update`;

     这个是基于数据库的行锁实现,对于单节点Mysql实现没有难度,测试结果就不放了。

    2、使用redis分布式锁(基于Redlock的实现)

    调整model.js

    let db = require("../utils/mysql_util").db;
    let moment = require("moment");
    let {async_redis_client, redlock} = require("../utils/redis_util"); let model = {};

    /** * 测试并发 * @param reqParams * @returns {Promise<{msg: string, success: boolean}>} */ model.testConcurrentOrder = async function (reqParams) { // 之前本人有个误区,设置资源锁的key为 `goods_${inventory_remain}`,但是仍然有写入错误的数据。 // 思考了一下,觉得是很多请求同时进来,从加锁->释放锁相当于进入队列,前一个操作完成后释放了锁,后一个才要开始加锁。 // 假如有100个请求同时进来,其中50个请求在加锁之前查询到的库存都是100,提交事务的出库信息都是剩余库存99,那么有49条数据都会形成脏数据。 // 所以这里必须设置资源锁的key一致,只要有一个资源还没有释放锁,其他资源就不允许进行查询库存的操作,问题解决。 // 上锁的资源 let resource_lock_key = `redislock_goods`; // 上锁资源5s,操作完成或者到时间后自动释放 let resource_ttl = 5 * 1000; // 开始加锁 let lock = await redlock.lock(resource_lock_key, resource_ttl); console.log(resource_lock_key, lock.value); console.log(moment().format('YYYY-MM-DD HH:mm:ss:SSS')); let initDB = !!reqParams.initDB || 0;// 是否初始化db let goods_id = reqParams.goods_id || "goods_01";// 商品id let opt_num = reqParams.opt_num || 1;// 出库数量 let result = {}; if (initDB) { // 清除数据 await db.query(`TRUNCATE db_stock`); await db.query(`TRUNCATE db_checkout`); // 插入库存数据 await db.query(`INSERT INTO db_stock(goods_id, inventory_total, inventory_remain) VALUES ('${goods_id}', 100, 100)`); result = { success: true, msg: '初始化DB成功', }; } else { let querySql = `SELECT t.inventory_total, t.inventory_remain FROM db_stock t WHERE 1 = 1 AND t.goods_id = ?`; let queryResult = await db.queryOne(querySql, [goods_id]); if (!queryResult) { result = { success: false, msg: `无法匹配商品id:${goods_id}` } } else { // 当前库存 let inventory_remain = queryResult.inventory_remain;
    // 等待设置的新库存 let inventory_remain_new
    = inventory_remain - 1; let tran = await db.beginTransaction(); try { // 新增出库记录 let checkoutSql = `INSERT INTO db_checkout(goods_id, opt_num, inventory_remain, create_time) VALUES (?, ?, ?, ?)`; await tran.query(checkoutSql, [goods_id, opt_num, inventory_remain_new, new Date()]); // 更新库存信息 let updateStockSql = `UPDATE db_stock t SET inventory_remain = ? WHERE t.goods_id = ?`; // 更新库存 await tran.query(updateStockSql, [inventory_remain_new, goods_id]); await tran.commit(); result = { success: true, msg: '出库成功', } } catch (e) { await tran.rollback(); // 抛出异常之前,及时释放资源锁 await lock.unlock(); throw e; } } } if (lock) { // 及时释放资源锁 await lock.unlock(); } return result; }; module.exports = model;

     附上redis_util.js的代码

    // Redis单节点
    let date_util = require("../utils/date_util");
    let RedLock = require("redlock");
    // redis
    let redis = require("redis");
    // async_redis
    let async_redis = require("async-redis");
    
    // redis_option
    let redis_option = {
        //redis服务器的ip地址
        host: '127.0.0.1',
        //redis服务器的端口
        port: '6379',
        // 授权密码,修改redis.windows.conf requirepass 1234,密码错误报错: NOAUTH Authentication required
        password: '1234',
        // redis select $index,默认0
        db: '0',
        // reply number => string 默认null
        string_numbers: null,
        // replay strings => buffer,默认false
        return_buffers: false,
        // 是否检测缓冲区,默认false
        detect_buffers: false,
        // 长链接,默认true
        socket_keepalive: true,
        // 长链接延迟时间,默认0
        socket_initialdelay: 0,
        // 禁用ready检查,默认false
        no_ready_check: false,
        // 启用离线命令队列,默认true
        enable_offline_queue: true,
        // // @Deprecated 重连最大延迟,默认null
        // retry_max_delay: null,
        // // @Deprecated 连接超时时间,默认60*60*1000,默认1h
        // connect_timeout: 60 * 60 * 1000,
        // // @Deprecated 最大连接次数,默认0,设置为1将阻止任何重新连接尝试
        // max_attempts: 0,
        // 默认false, 如果设置为true,则在重新建立连接后,将重试连接丢失时未执行的所有命令。如果使用状态更改命令(例如incr),请小心使用此命令。这在使用阻塞命令时特别有用。
        retry_unfulfilled_commands: false,
        retry_strategy: function (options) {
            if (options.error && options.error.code === 'ECONNREFUSED') {
                // 拒绝连接
                let errMsg = 'The server refused the connection';
                console.log(date_util.format(), 'retry_strategy', errMsg);
                return new Error(errMsg);
            }
            // 默认 60*60*1000
            if (options.total_retry_time > 60 * 60 * 1000) {
                // 超过最大重试时间
                let errMsg = 'Retry time exhausted';
                console.log(date_util.format(), 'retry_strategy', errMsg);
                return new Error(errMsg);
            }
            // 默认10
            if (options.attempt > 10) {
                // 超过最大重试次数
                let errMsg = 'maximum connection attempts exceeded';
                console.log(date_util.format(), 'retry_strategy', errMsg);
                return new Error(errMsg);
            }
            // reconnect after
            return Math.min(options.attempt * 1000, 3000);
        }
    };
    
    // redis_client
    let redis_client = redis.createClient(redis_option);
    
    // async_redis_client
    let async_redis_client = async_redis.createClient(redis_option);
    
    redis_client.on("error", function (err) {
        console.log(date_util.format(date_util.pattern().ymdhmsSSS), err.toString());
    });
    async_redis_client.on("error", function (err) {
        console.log(date_util.format(date_util.pattern().ymdhmsSSS), err.toString());
    });
    
    // NOTE: 这里只能使用redis_client
    let redlock = new RedLock([redis_client]);
    redlock.on('clientError', function (err) {
        console.error(date_util.format(date_util.pattern().ymdhmsSSS), 'A redis error has occurred:', err);
    });
    
    module.exports = {
        redis_client,
        async_redis_client,
        redlock
    };

     ab测试

    C:WINDOWSsystem32>ab.exe -n 95 -c 95  http://localhost:3000/dm/testConcurrentOrder
    This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/
    
    Benchmarking localhost (be patient).....done
    
    
    Server Software:
    Server Hostname:        localhost
    Server Port:            3000
    
    Document Path:          /dm/testConcurrentOrder
    Document Length:        46 bytes
    
    Concurrency Level:      95
    Time taken for tests:   1.867 seconds
    Complete requests:      95
    Failed requests:        0
    Total transferred:      17860 bytes
    HTML transferred:       4370 bytes
    Requests per second:    50.88 [#/sec] (mean)
    Time per request:       1867.041 [ms] (mean)
    Time per request:       19.653 [ms] (mean, across all concurrent requests)
    Transfer rate:          9.34 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    0   0.2      0       1
    Processing:    29  932 437.8    909    1840
    Waiting:       21  931 437.9    908    1839
    Total:         29  932 437.8    909    1840
    
    Percentage of the requests served within a certain time (ms)
      50%    907
      66%   1123
      75%   1257
      80%   1346
      90%   1578
      95%   1727
      98%   1783
      99%   1840
     100%   1840 (longest request)
    
    C:WINDOWSsystem32>

    Mysql查询结果:

    mysql> select * from db_stock;
    +----+----------+-----------------+------------------+
    | id | goods_id | inventory_total | inventory_remain |
    +----+----------+-----------------+------------------+
    |  1 | goods_01 |             100 |                5 |
    +----+----------+-----------------+------------------+
    1 row in set (0.00 sec)
    
    mysql> select * from db_checkout;
    +----+----------+---------+------------------+---------------------+
    | id | goods_id | opt_num | inventory_remain | create_time         |
    +----+----------+---------+------------------+---------------------+
    |  1 | goods_01 |       1 |               99 | 2019-10-10 18:36:15 |
    |  2 | goods_01 |       1 |               98 | 2019-10-10 18:36:15 |
    |  3 | goods_01 |       1 |               97 | 2019-10-10 18:36:15 |
    |  4 | goods_01 |       1 |               96 | 2019-10-10 18:36:15 |
    |  5 | goods_01 |       1 |               95 | 2019-10-10 18:36:15 |
    |  6 | goods_01 |       1 |               94 | 2019-10-10 18:36:15 |
    |  7 | goods_01 |       1 |               93 | 2019-10-10 18:36:15 |
    |  8 | goods_01 |       1 |               92 | 2019-10-10 18:36:15 |
    |  9 | goods_01 |       1 |               91 | 2019-10-10 18:36:15 |
    | 10 | goods_01 |       1 |               90 | 2019-10-10 18:36:15 |
    | 11 | goods_01 |       1 |               89 | 2019-10-10 18:36:15 |
    | 12 | goods_01 |       1 |               88 | 2019-10-10 18:36:15 |
    | 13 | goods_01 |       1 |               87 | 2019-10-10 18:36:15 |
    | 14 | goods_01 |       1 |               86 | 2019-10-10 18:36:15 |
    | 15 | goods_01 |       1 |               85 | 2019-10-10 18:36:15 |
    | 16 | goods_01 |       1 |               84 | 2019-10-10 18:36:15 |
    | 17 | goods_01 |       1 |               83 | 2019-10-10 18:36:15 |
    | 18 | goods_01 |       1 |               82 | 2019-10-10 18:36:15 |
    | 19 | goods_01 |       1 |               81 | 2019-10-10 18:36:15 |
    | 20 | goods_01 |       1 |               80 | 2019-10-10 18:36:15 |
    | 21 | goods_01 |       1 |               79 | 2019-10-10 18:36:15 |
    | 22 | goods_01 |       1 |               78 | 2019-10-10 18:36:15 |
    | 23 | goods_01 |       1 |               77 | 2019-10-10 18:36:15 |
    | 24 | goods_01 |       1 |               76 | 2019-10-10 18:36:15 |
    | 25 | goods_01 |       1 |               75 | 2019-10-10 18:36:15 |
    | 26 | goods_01 |       1 |               74 | 2019-10-10 18:36:15 |
    | 27 | goods_01 |       1 |               73 | 2019-10-10 18:36:15 |
    | 28 | goods_01 |       1 |               72 | 2019-10-10 18:36:15 |
    | 29 | goods_01 |       1 |               71 | 2019-10-10 18:36:15 |
    | 30 | goods_01 |       1 |               70 | 2019-10-10 18:36:15 |
    | 31 | goods_01 |       1 |               69 | 2019-10-10 18:36:15 |
    | 32 | goods_01 |       1 |               68 | 2019-10-10 18:36:15 |
    | 33 | goods_01 |       1 |               67 | 2019-10-10 18:36:15 |
    | 34 | goods_01 |       1 |               66 | 2019-10-10 18:36:15 |
    | 35 | goods_01 |       1 |               65 | 2019-10-10 18:36:15 |
    | 36 | goods_01 |       1 |               64 | 2019-10-10 18:36:15 |
    | 37 | goods_01 |       1 |               63 | 2019-10-10 18:36:15 |
    | 38 | goods_01 |       1 |               62 | 2019-10-10 18:36:15 |
    | 39 | goods_01 |       1 |               61 | 2019-10-10 18:36:15 |
    | 40 | goods_01 |       1 |               60 | 2019-10-10 18:36:15 |
    | 41 | goods_01 |       1 |               59 | 2019-10-10 18:36:15 |
    | 42 | goods_01 |       1 |               58 | 2019-10-10 18:36:15 |
    | 43 | goods_01 |       1 |               57 | 2019-10-10 18:36:15 |
    | 44 | goods_01 |       1 |               56 | 2019-10-10 18:36:15 |
    | 45 | goods_01 |       1 |               55 | 2019-10-10 18:36:15 |
    | 46 | goods_01 |       1 |               54 | 2019-10-10 18:36:16 |
    | 47 | goods_01 |       1 |               53 | 2019-10-10 18:36:16 |
    | 48 | goods_01 |       1 |               52 | 2019-10-10 18:36:16 |
    | 49 | goods_01 |       1 |               51 | 2019-10-10 18:36:16 |
    | 50 | goods_01 |       1 |               50 | 2019-10-10 18:36:16 |
    | 51 | goods_01 |       1 |               49 | 2019-10-10 18:36:16 |
    | 52 | goods_01 |       1 |               48 | 2019-10-10 18:36:16 |
    | 53 | goods_01 |       1 |               47 | 2019-10-10 18:36:16 |
    | 54 | goods_01 |       1 |               46 | 2019-10-10 18:36:16 |
    | 55 | goods_01 |       1 |               45 | 2019-10-10 18:36:16 |
    | 56 | goods_01 |       1 |               44 | 2019-10-10 18:36:16 |
    | 57 | goods_01 |       1 |               43 | 2019-10-10 18:36:16 |
    | 58 | goods_01 |       1 |               42 | 2019-10-10 18:36:16 |
    | 59 | goods_01 |       1 |               41 | 2019-10-10 18:36:16 |
    | 60 | goods_01 |       1 |               40 | 2019-10-10 18:36:16 |
    | 61 | goods_01 |       1 |               39 | 2019-10-10 18:36:16 |
    | 62 | goods_01 |       1 |               38 | 2019-10-10 18:36:16 |
    | 63 | goods_01 |       1 |               37 | 2019-10-10 18:36:16 |
    | 64 | goods_01 |       1 |               36 | 2019-10-10 18:36:16 |
    | 65 | goods_01 |       1 |               35 | 2019-10-10 18:36:16 |
    | 66 | goods_01 |       1 |               34 | 2019-10-10 18:36:16 |
    | 67 | goods_01 |       1 |               33 | 2019-10-10 18:36:16 |
    | 68 | goods_01 |       1 |               32 | 2019-10-10 18:36:16 |
    | 69 | goods_01 |       1 |               31 | 2019-10-10 18:36:16 |
    | 70 | goods_01 |       1 |               30 | 2019-10-10 18:36:16 |
    | 71 | goods_01 |       1 |               29 | 2019-10-10 18:36:16 |
    | 72 | goods_01 |       1 |               28 | 2019-10-10 18:36:16 |
    | 73 | goods_01 |       1 |               27 | 2019-10-10 18:36:16 |
    | 74 | goods_01 |       1 |               26 | 2019-10-10 18:36:16 |
    | 75 | goods_01 |       1 |               25 | 2019-10-10 18:36:16 |
    | 76 | goods_01 |       1 |               24 | 2019-10-10 18:36:16 |
    | 77 | goods_01 |       1 |               23 | 2019-10-10 18:36:16 |
    | 78 | goods_01 |       1 |               22 | 2019-10-10 18:36:16 |
    | 79 | goods_01 |       1 |               21 | 2019-10-10 18:36:16 |
    | 80 | goods_01 |       1 |               20 | 2019-10-10 18:36:16 |
    | 81 | goods_01 |       1 |               19 | 2019-10-10 18:36:16 |
    | 82 | goods_01 |       1 |               18 | 2019-10-10 18:36:16 |
    | 83 | goods_01 |       1 |               17 | 2019-10-10 18:36:16 |
    | 84 | goods_01 |       1 |               16 | 2019-10-10 18:36:16 |
    | 85 | goods_01 |       1 |               15 | 2019-10-10 18:36:16 |
    | 86 | goods_01 |       1 |               14 | 2019-10-10 18:36:16 |
    | 87 | goods_01 |       1 |               13 | 2019-10-10 18:36:16 |
    | 88 | goods_01 |       1 |               12 | 2019-10-10 18:36:16 |
    | 89 | goods_01 |       1 |               11 | 2019-10-10 18:36:16 |
    | 90 | goods_01 |       1 |               10 | 2019-10-10 18:36:16 |
    | 91 | goods_01 |       1 |                9 | 2019-10-10 18:36:16 |
    | 92 | goods_01 |       1 |                8 | 2019-10-10 18:36:16 |
    | 93 | goods_01 |       1 |                7 | 2019-10-10 18:36:16 |
    | 94 | goods_01 |       1 |                6 | 2019-10-10 18:36:16 |
    | 95 | goods_01 |       1 |                5 | 2019-10-10 18:36:16 |
    +----+----------+---------+------------------+---------------------+
    95 rows in set (0.00 sec)
    
    mysql>

    完美!

  • 相关阅读:
    04_特征工程
    03_特征清洗
    02_数据探索
    01_简介
    cache是什么文件?
    gulp详细入门教程
    HTML5实战与剖析之触摸事件(touchstart、touchmove和touchend)
    h4和h5的区别
    弹性盒布局
    js面向对象
  • 原文地址:https://www.cnblogs.com/wrcold520/p/11643250.html
Copyright © 2011-2022 走看看