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:\WINDOWS\system32>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:\WINDOWS\system32>

查看並發請求后,數據庫表的信息

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:\WINDOWS\system32>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:\WINDOWS\system32>

 

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>

完美!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM