mm_mysql 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (7) hide show
  1. package/README.md +100 -34
  2. package/db.js +144 -170
  3. package/index.js +393 -156
  4. package/package.json +2 -2
  5. package/sql.js +190 -349
  6. package/test.js +218 -128
  7. package/test_create_table.js +0 -193
package/index.js CHANGED
@@ -1,20 +1,20 @@
1
1
  const mysql = require('mysql2/promise');
2
- const { BaseService } = require('mm_base_service');
2
+ const { Base } = require('mm_expand');
3
3
  const { DB } = require('./db');
4
4
 
5
5
  /**
6
6
  * 优化版MySQL数据库操作类
7
7
  * 保持必要功能,简化过度封装,直接使用mysql2模块
8
8
  * @class Mysql
9
- * @extends BaseService
9
+ * @extends Base
10
10
  */
11
- class Mysql extends BaseService {
11
+ class Mysql extends Base {
12
12
  /**
13
13
  * 默认配置
14
14
  * MySQL2支持的连接池配置参数:
15
15
  * - host, port, user, password, database
16
- * - charset, timezone, connectTimeout
17
- * - connectionLimit, acquireTimeout, waitForConnections
16
+ * - charset, timezone, connect_timeout
17
+ * - connection_limit, acquire_timeout, wait_for_connections
18
18
  * - queueLimit参数在MySQL2中可能不被支持,已移除
19
19
  */
20
20
  static default_config = {
@@ -25,9 +25,10 @@ class Mysql extends BaseService {
25
25
  database: '',
26
26
  charset: 'utf8mb4',
27
27
  timezone: '+08:00',
28
- connectTimeout: 20000,
29
- connectionLimit: 10
30
- // queueLimit: 0 // MySQL2不支持此参数,已移除以避免警告
28
+ connect_timeout: 20000,
29
+ connection_limit: 10,
30
+ acquire_timeout: 20000,
31
+ wait_for_connections: true
31
32
  };
32
33
 
33
34
  /**
@@ -35,64 +36,106 @@ class Mysql extends BaseService {
35
36
  * @param {Object} config - 配置对象
36
37
  */
37
38
  constructor(config = {}) {
38
- const mergedConfig = Object.assign({}, Mysql.default_config, config);
39
- super(mergedConfig);
40
-
41
- this.config = mergedConfig;
39
+ super(Object.assign({}, Mysql.default_config, config));
42
40
  this._pool = null;
43
41
  this._connection = null;
44
- this._usePool = this.config.connectionLimit > 1;
42
+ this._usePool = this.config.connection_limit > 1;
43
+ this._pool_stats = {
44
+ total_connections: 0,
45
+ active_connections: 0,
46
+ idle_connections: 0,
47
+ connection_errors: 0,
48
+ last_health_check: null
49
+ };
45
50
  }
46
51
  }
47
52
 
48
53
  /**
49
54
  * 获取MySQL2支持的配置参数
50
- * 过滤掉MySQL2不支持的参数,避免警告
55
+ * 根据连接类型(连接池或单连接)过滤参数,避免警告
56
+ * @param {boolean} is_pool - 是否为连接池配置
51
57
  * @returns {Object} 过滤后的配置对象
52
58
  */
53
- Mysql.prototype._getValidConfig = function() {
54
- // MySQL2支持的连接配置参数
55
- const validKeys = [
56
- 'host', 'port', 'user', 'password', 'database',
57
- 'charset', 'timezone', 'connectTimeout',
58
- 'connectionLimit', 'acquireTimeout', 'waitForConnections'
59
- ];
60
-
61
- const filteredConfig = {};
62
- for (const key of validKeys) {
63
- if (key in this.config && this.config[key] !== undefined) {
64
- filteredConfig[key] = this.config[key];
59
+ Mysql.prototype._getValidConfig = function (is_pool = false) {
60
+ // 基础连接参数(适用于所有连接类型)
61
+ const base_map = {
62
+ 'host': 'host',
63
+ 'port': 'port',
64
+ 'user': 'user',
65
+ 'password': 'password',
66
+ 'database': 'database',
67
+ 'charset': 'charset',
68
+ 'timezone': 'timezone',
69
+ 'connect_timeout': 'connectTimeout'
70
+ };
71
+
72
+ // 连接池专用参数
73
+ const pool_map = {
74
+ 'connection_limit': 'connectionLimit',
75
+ 'wait_for_connections': 'waitForConnections',
76
+ 'idle_timeout': 'idleTimeout',
77
+ 'max_idle': 'maxIdle'
78
+ };
79
+
80
+ const config = {};
81
+
82
+ // 添加基础参数
83
+ for (const key in base_map) {
84
+ if (this.config[key] !== undefined) {
85
+ config[base_map[key]] = this.config[key];
65
86
  }
66
87
  }
67
-
68
- return filteredConfig;
88
+
89
+ // 如果是连接池,添加连接池专用参数
90
+ if (is_pool) {
91
+ for (const key in pool_map) {
92
+ if (this.config[key] !== undefined) {
93
+ config[pool_map[key]] = this.config[key];
94
+ }
95
+ }
96
+ }
97
+ // 如果是单连接,确保不包含任何连接池专用参数
98
+ else {
99
+ // 移除所有连接池专用参数,避免mysql2内部警告
100
+ for (const key in pool_map) {
101
+ if (pool_map[key] in config) {
102
+ delete config[pool_map[key]];
103
+ }
104
+ }
105
+ }
106
+
107
+ return config;
69
108
  };
70
109
 
71
110
  /**
72
111
  * 打开数据库连接
73
112
  * @returns {Promise<boolean>}
113
+ * @throws {TypeError} 当配置参数无效时
74
114
  */
75
- Mysql.prototype.open = async function() {
115
+ Mysql.prototype.open = async function () {
116
+ // 参数校验
117
+ if (!this.config || typeof this.config !== 'object') {
118
+ throw new TypeError('config must be object');
119
+ }
120
+
76
121
  try {
77
- // 使用过滤后的配置参数,避免传递无效参数
78
- const validConfig = this._getValidConfig();
79
-
80
122
  if (this._usePool) {
81
- this._pool = mysql.createPool(validConfig);
123
+ // 根据连接类型获取对应的配置参数
124
+ const valid_config = this._getValidConfig(this._usePool);
125
+ this._pool = mysql.createPool(valid_config);
82
126
  // 测试连接池连接
83
127
  const conn = await this._pool.getConnection();
84
128
  conn.release();
85
129
  } else {
86
- this._connection = await mysql.createConnection(validConfig);
130
+ const valid_config = this._getValidConfig(this._usePool);
131
+ this._connection = await mysql.createConnection(valid_config);
87
132
  }
88
-
89
- $.log.info(`[${this.constructor.name}] [open] 数据库连接成功`);
133
+
134
+ this.logger('info', '数据库连接成功');
90
135
  return true;
91
136
  } catch (error) {
92
- $.log.error(`[${this.constructor.name}] [open] 数据库连接失败`, {
93
- error: error.message
94
- });
95
- throw error;
137
+ this.logger('error', '数据库连接失败', error);
138
+ return false;
96
139
  }
97
140
  };
98
141
 
@@ -100,7 +143,7 @@ Mysql.prototype.open = async function() {
100
143
  * 关闭数据库连接
101
144
  * @returns {Promise<boolean>}
102
145
  */
103
- Mysql.prototype.close = async function() {
146
+ Mysql.prototype.close = async function () {
104
147
  try {
105
148
  if (this._pool) {
106
149
  await this._pool.end();
@@ -110,196 +153,301 @@ Mysql.prototype.close = async function() {
110
153
  await this._connection.end();
111
154
  this._connection = null;
112
155
  }
113
-
114
- $.log.info(`[${this.constructor.name}] [close] 数据库连接已关闭`);
156
+
157
+ this.logger('info', '数据库连接已关闭');
115
158
  return true;
116
159
  } catch (error) {
117
- $.log.error(`[${this.constructor.name}] [close] 关闭连接失败`, {
118
- error: error.message
119
- });
120
- throw error;
160
+ this.logger('error', '关闭连接失败', error);
161
+ return false;
121
162
  }
122
163
  };
123
164
 
124
165
  /**
125
- * 获取数据库连接(保持兼容性)
126
- * @param {Number} timeout - 超时时间(毫秒)
166
+ * 安全释放数据库连接
167
+ * @param {Object} conn - 数据库连接对象
168
+ * @param {boolean} is_pool_conn - 是否为连接池连接
169
+ * @returns {Promise<void>}
170
+ */
171
+ Mysql.prototype._safeReleaseConnection = async function (conn, is_pool_conn) {
172
+ if (!conn) return;
173
+
174
+ try {
175
+ if (is_pool_conn && typeof conn.release === 'function') {
176
+ await conn.release();
177
+ // 更新统计信息
178
+ if (this._pool_stats.active_connections > 0) {
179
+ this._pool_stats.active_connections--;
180
+ }
181
+ this._pool_stats.idle_connections++;
182
+ }
183
+ } catch (release_err) {
184
+ this.logger('error', '释放连接失败', release_err);
185
+ this._pool_stats.connection_errors++;
186
+ }
187
+ };
188
+
189
+ /**
190
+ * 获取数据库连接(改进版)
127
191
  * @returns {Promise<Object>}
192
+ * @throws {TypeError} 当连接池或连接不存在时
128
193
  */
129
- Mysql.prototype.getConn = async function(timeout = null) {
194
+ Mysql.prototype.getConn = async function () {
195
+ // 参数校验
196
+ if (!this._pool && !this._connection) {
197
+ throw new Error('数据库连接未初始化');
198
+ }
199
+
130
200
  try {
131
201
  if (this._usePool) {
132
- return await this._pool.getConnection();
202
+ const conn = await this._pool.getConnection();
203
+ // 更新统计信息
204
+ this._pool_stats.total_connections++;
205
+ this._pool_stats.active_connections++;
206
+ if (this._pool_stats.idle_connections > 0) {
207
+ this._pool_stats.idle_connections--;
208
+ }
209
+ return conn;
133
210
  } else {
134
211
  return this._connection;
135
212
  }
136
213
  } catch (error) {
137
- $.log.error(`[${this.constructor.name}] [getConn] 获取连接失败`, {
138
- error: error.message
139
- });
140
- throw error;
214
+ this.logger('error', '获取连接失败', error);
215
+ this._pool_stats.connection_errors++;
141
216
  }
217
+ return null;
142
218
  };
143
219
 
144
220
  /**
145
- * 执行SQL查询(保持兼容性)
221
+ * 执行SQL查询(改进版)
146
222
  * @param {String} sql - SQL语句
147
223
  * @param {Array} params - 参数数组
148
- * @param {Number} timeout - 超时时间(毫秒)
149
224
  * @returns {Promise<Object>}
225
+ * @throws {TypeError} 当sql参数无效时
150
226
  */
151
- Mysql.prototype.run = async function(sql, params = [], timeout = null) {
227
+ Mysql.prototype.run = async function (sql, params = []) {
228
+ // 参数校验
229
+ if (typeof sql !== 'string' || sql.trim() === '') {
230
+ throw new TypeError('sql must be non-empty string');
231
+ }
232
+ if (!Array.isArray(params)) {
233
+ throw new TypeError('params must be array');
234
+ }
235
+
236
+ this.error = null;
152
237
  let conn = null;
153
- let isPoolConn = false;
154
-
238
+ let is_pool_conn = false;
239
+ let connection_released = false;
240
+
155
241
  try {
156
242
  // 获取连接
157
243
  conn = await this.getConn();
158
- isPoolConn = this._usePool;
159
-
244
+ is_pool_conn = this._usePool;
245
+
160
246
  // 直接使用mysql2的query方法
161
247
  const [rows] = await conn.query(sql, params);
162
248
  return rows;
163
- } catch (error) {
164
- $.log.error(`[${this.constructor.name}] [run] SQL执行失败`, {
165
- error: error.message,
166
- sql: typeof sql === 'string' ? sql.substring(0, 200) : sql,
167
- params: params
168
- });
169
- throw error;
249
+ } catch (err) {
250
+ this.logger('error', 'SQL执行失败', err);
251
+ this.sql = err.sql;
252
+ this.error = {
253
+ code: err.errno,
254
+ message: err.sqlMessage
255
+ };
256
+ // 在错误情况下也要确保连接释放
257
+ try {
258
+ await this._safeReleaseConnection(conn, is_pool_conn);
259
+ } catch (release_err) {
260
+ this.logger('error', '释放连接失败', release_err);
261
+ }
262
+ connection_released = true;
170
263
  } finally {
171
- // 释放连接
172
- if (conn && isPoolConn) {
264
+ // 确保连接被释放,避免资源泄漏
265
+ if (!connection_released) {
173
266
  try {
174
- conn.release();
175
- } catch (releaseErr) {
176
- $.log.error('释放连接失败', {
177
- error: releaseErr.message
178
- });
267
+ await this._safeReleaseConnection(conn, is_pool_conn);
268
+ } catch (release_err) {
269
+ this.logger('error', '释放连接失败', release_err);
179
270
  }
180
271
  }
181
272
  }
273
+ return [];
182
274
  };
183
275
 
184
276
  /**
185
- * 执行SQL语句(保持兼容性)
277
+ * 执行SQL语句(改进版)
186
278
  * @param {String} sql - SQL语句
187
279
  * @param {Array} params - 参数数组
188
- * @param {Number} timeout - 超时时间(毫秒)
189
280
  * @returns {Promise<Object>}
281
+ * @throws {TypeError} 当sql参数无效时
190
282
  */
191
- Mysql.prototype.exec = async function(sql, params = [], timeout = null) {
283
+ Mysql.prototype.exec = async function (sql, params = []) {
284
+ // 参数校验
285
+ if (typeof sql !== 'string' || sql.trim() === '') {
286
+ throw new TypeError('sql must be non-empty string');
287
+ }
288
+ if (!Array.isArray(params)) {
289
+ throw new TypeError('params must be array');
290
+ }
291
+ this.error = null;
192
292
  let conn = null;
193
- let isPoolConn = false;
194
-
293
+ let is_pool_conn = false;
294
+ let connection_released = false;
295
+
195
296
  try {
196
297
  // 获取连接
197
298
  conn = await this.getConn();
198
- isPoolConn = this._usePool;
199
-
299
+ is_pool_conn = this._usePool;
300
+
200
301
  // 直接使用mysql2的execute方法
201
302
  const [result] = await conn.execute(sql, params);
202
-
203
- // 返回与mm_sqlite兼容的格式
204
- return {
205
- affectedRows: result.affectedRows || 0,
206
- insertId: result.insertId || 0,
207
- changedRows: result.changedRows || 0
208
- };
303
+ return result.insertId || result.affectedRows || result.changedRows || 1;
209
304
  } catch (error) {
210
- $.log.error(`[${this.constructor.name}] [exec] SQL执行失败`, {
211
- error: error.message,
212
- sql: sql.substring(0, 200),
213
- params: params
214
- });
215
- throw error;
305
+ this.logger('error', 'SQL执行失败', error);
306
+ this.sql = error.sql;
307
+ this.error = {
308
+ code: error.errno,
309
+ message: error.sqlMessage
310
+ };
311
+ // 在错误情况下也要确保连接释放
312
+ try {
313
+ await this._safeReleaseConnection(conn, is_pool_conn);
314
+ } catch (release_err) {
315
+ this.logger('error', '释放连接失败', release_err);
316
+ }
317
+ connection_released = true;
216
318
  } finally {
217
- // 释放连接
218
- if (conn && isPoolConn) {
319
+ // 确保连接被释放,避免资源泄漏
320
+ if (!connection_released) {
219
321
  try {
220
- conn.release();
221
- } catch (releaseErr) {
222
- $.log.error('释放连接失败', {
223
- error: releaseErr.message
224
- });
322
+ await this._safeReleaseConnection(conn, is_pool_conn);
323
+ } catch (release_err) {
324
+ this.logger('error', '释放连接失败', release_err);
225
325
  }
226
326
  }
227
327
  }
328
+ return 0;
228
329
  };
229
330
 
230
331
  /**
231
- * 开始事务(保持兼容性)
332
+ * 开始事务(改进版)
232
333
  * @returns {Promise<Object>} 事务连接对象
233
334
  */
234
- Mysql.prototype.beginTransaction = async function() {
335
+ Mysql.prototype.beginTransaction = async function () {
336
+ let conn = null;
337
+ let transaction_committed = false;
338
+ let transaction_rolled_back = false;
339
+
235
340
  try {
236
- const conn = await this.getConn();
341
+ conn = await this.getConn();
237
342
  await conn.beginTransaction();
238
-
343
+
239
344
  return {
240
345
  connection: conn,
241
346
  commit: async () => {
347
+ if (transaction_committed || transaction_rolled_back) {
348
+ this.logger('warn', '事务已结束,无需重复提交');
349
+ return;
350
+ }
242
351
  await conn.commit();
243
- if (this._usePool) {
244
- conn.release();
352
+ transaction_committed = true;
353
+ try {
354
+ await this._safeReleaseConnection(conn, this._usePool);
355
+ } catch (release_err) {
356
+ this.logger('error', '提交时释放连接失败', release_err);
245
357
  }
246
358
  },
247
359
  rollback: async () => {
360
+ if (transaction_committed || transaction_rolled_back) {
361
+ this.logger('warn', '事务已结束,无需重复回滚');
362
+ return;
363
+ }
248
364
  await conn.rollback();
249
- if (this._usePool) {
250
- conn.release();
365
+ transaction_rolled_back = true;
366
+ try {
367
+ await this._safeReleaseConnection(conn, this._usePool);
368
+ } catch (release_err) {
369
+ this.logger('error', '回滚时释放连接失败', release_err);
251
370
  }
252
- }
371
+ },
372
+ _is_ended: () => transaction_committed || transaction_rolled_back
253
373
  };
254
374
  } catch (error) {
255
- $.log.error(`[${this.constructor.name}] [beginTransaction] 事务开始失败`, {
256
- error: error.message
257
- });
375
+ // 如果事务开始失败,确保连接被释放
376
+ try {
377
+ await this._safeReleaseConnection(conn, this._usePool);
378
+ } catch (release_err) {
379
+ this.logger('error', '释放连接失败', release_err);
380
+ }
381
+ this.logger('error', '事务开始失败', error);
258
382
  throw error;
259
383
  }
260
384
  };
261
385
 
262
386
  /**
263
- * 在事务中执行多个操作(保持兼容性)
387
+ * 在事务中执行多个操作(改进版)
264
388
  * @param {Function} callback - 包含事务操作的回调函数
265
389
  * @returns {Promise<*>} 回调函数的返回值
390
+ * @throws {TypeError} 当callback参数无效时
266
391
  */
267
- Mysql.prototype.transaction = async function(callback) {
392
+ Mysql.prototype.transaction = async function (callback) {
393
+ // 参数校验
394
+ if (typeof callback !== 'function') {
395
+ throw new TypeError('callback must be function');
396
+ }
397
+
268
398
  let transaction = null;
269
-
399
+ let transaction_executed = false;
400
+
270
401
  try {
271
402
  transaction = await this.beginTransaction();
272
403
  const result = await callback(transaction);
273
- await transaction.commit();
404
+
405
+ // 检查事务是否已由用户手动提交/回滚
406
+ if (transaction._is_ended && !transaction._is_ended()) {
407
+ await transaction.commit();
408
+ }
409
+
410
+ transaction_executed = true;
274
411
  return result;
275
412
  } catch (error) {
276
- if (transaction) {
277
- await transaction.rollback().catch(err => {
278
- $.log.error(`[${this.constructor.name}] [transaction] 事务回滚失败`, {
279
- error: err.message
413
+ if (transaction && !transaction_executed) {
414
+ // 只有在事务未成功执行的情况下才回滚
415
+ if (transaction._is_ended && !transaction._is_ended()) {
416
+ await transaction.rollback().catch(err => {
417
+ this.logger('error', '事务回滚失败', err);
280
418
  });
281
- });
419
+ }
282
420
  }
283
-
284
- $.log.error(`[${this.constructor.name}] [transaction] 事务执行失败`, {
285
- error: error.message
286
- });
287
- throw error;
421
+
422
+ this.logger('error', '事务执行失败', error);
423
+ throw error; // 重新抛出错误,让调用方知道事务失败
288
424
  }
289
425
  };
290
426
 
291
427
  /**
292
- * 读取整张表的数据(保持兼容性)
428
+ * 读取整张表的数据(改进版)
293
429
  * @param {String} table - 表名
294
430
  * @param {Object} condition - 查询条件
295
431
  * @param {Object} options - 选项(orderBy, limit, offset等)
296
432
  * @returns {Promise<Array>}
433
+ * @throws {TypeError} 当table参数无效时
297
434
  */
298
- Mysql.prototype.read = async function(table, condition = {}, options = {}) {
435
+ Mysql.prototype.read = async function (table, condition = {}, options = {}) {
436
+ // 参数校验
437
+ if (typeof table !== 'string' || table.trim() === '') {
438
+ throw new TypeError('table must be non-empty string');
439
+ }
440
+ if (condition && typeof condition !== 'object') {
441
+ throw new TypeError('condition must be object');
442
+ }
443
+ if (options && typeof options !== 'object') {
444
+ throw new TypeError('options must be object');
445
+ }
446
+
299
447
  try {
300
448
  let sql = `SELECT * FROM ${table}`;
301
449
  const params = [];
302
-
450
+
303
451
  // 处理条件
304
452
  if (Object.keys(condition).length > 0) {
305
453
  const whereClauses = [];
@@ -309,30 +457,85 @@ Mysql.prototype.read = async function(table, condition = {}, options = {}) {
309
457
  }
310
458
  sql += ` WHERE ${whereClauses.join(' AND ')}`;
311
459
  }
312
-
460
+
313
461
  // 处理排序
314
462
  if (options.orderBy) {
315
463
  sql += ` ORDER BY ${options.orderBy}`;
316
464
  }
317
-
465
+
318
466
  // 处理分页
319
467
  if (options.limit) {
320
468
  sql += ` LIMIT ${options.limit}`;
321
469
  }
322
-
470
+
323
471
  if (options.offset) {
324
472
  sql += ` OFFSET ${options.offset}`;
325
473
  }
326
-
474
+
327
475
  // 执行查询
328
476
  return await this.run(sql, params);
329
477
  } catch (error) {
330
- $.log.error(`[${this.constructor.name}] [read] 查询失败`, {
331
- error: error.message,
332
- table: table,
333
- condition: condition
334
- });
335
- throw error;
478
+ this.logger('error', '查询失败', error);
479
+ throw error; // 重新抛出错误,让调用方知道查询失败
480
+ }
481
+ };
482
+
483
+ /**
484
+ * 获取连接池状态信息
485
+ * @returns {Object} 连接池状态对象
486
+ */
487
+ Mysql.prototype.getPoolStats = function () {
488
+ if (!this._pool) {
489
+ return {
490
+ status: 'not_initialized',
491
+ message: '连接池未初始化'
492
+ };
493
+ }
494
+
495
+ // 更新统计信息
496
+ this._pool_stats.last_health_check = new Date();
497
+
498
+ return {
499
+ status: 'healthy',
500
+ ...this._pool_stats,
501
+ last_health_check: this._pool_stats.last_health_check
502
+ };
503
+ };
504
+
505
+ /**
506
+ * 健康检查
507
+ * @returns {Promise<Object>} 健康检查结果
508
+ */
509
+ Mysql.prototype.healthCheck = async function () {
510
+ try {
511
+ if (!this._pool && !this._connection) {
512
+ return {
513
+ status: 'error',
514
+ message: '数据库连接未初始化'
515
+ };
516
+ }
517
+
518
+ // 执行简单的查询来检查连接状态
519
+ const result = await this.run('SELECT 1 as health_check');
520
+
521
+ if (result && result.length > 0 && result[0].health_check === 1) {
522
+ return {
523
+ status: 'healthy',
524
+ message: '数据库连接正常',
525
+ ...this.getPoolStats()
526
+ };
527
+ } else {
528
+ return {
529
+ status: 'error',
530
+ message: '健康检查查询失败'
531
+ };
532
+ }
533
+ } catch (error) {
534
+ this.logger('error', '健康检查失败', error);
535
+ return {
536
+ status: 'error',
537
+ message: `健康检查失败: ${error.message}`
538
+ };
336
539
  }
337
540
  };
338
541
 
@@ -340,7 +543,7 @@ Mysql.prototype.read = async function(table, condition = {}, options = {}) {
340
543
  * 获取数据库管理器(保持兼容性)
341
544
  * @returns {Object} DB实例
342
545
  */
343
- Mysql.prototype.db = function() {
546
+ Mysql.prototype.db = function () {
344
547
  return new DB(this);
345
548
  };
346
549
 
@@ -348,7 +551,7 @@ Mysql.prototype.db = function() {
348
551
  * 初始化MySQL服务(保持兼容性)
349
552
  * @returns {Promise<void>}
350
553
  */
351
- Mysql.prototype.init = async function() {
554
+ Mysql.prototype.init = async function () {
352
555
  await this.open();
353
556
  };
354
557
 
@@ -361,28 +564,62 @@ exports.Mysql = Mysql;
361
564
  * 确保连接池对象存在
362
565
  */
363
566
  if (!$.pool) {
364
- $.pool = {};
567
+ $.pool = {};
365
568
  }
366
569
  if (!$.pool.mysql) {
367
- $.pool.mysql = {};
570
+ $.pool.mysql = {};
571
+ }
572
+
573
+ /**
574
+ * 全局锁对象,用于线程安全
575
+ */
576
+ if (!$.pool.mysql._locks) {
577
+ $.pool.mysql._locks = {};
368
578
  }
369
579
 
370
580
  /**
371
- * @description Mysql管理器,用于创建缓存
581
+ * 获取锁
582
+ * @param {string} key - 锁的键名
583
+ * @returns {Promise<Function>} 释放锁的函数
584
+ */
585
+ function _acquireLock(key) {
586
+ if (!$.pool.mysql._locks[key]) {
587
+ $.pool.mysql._locks[key] = Promise.resolve();
588
+ }
589
+
590
+ let release;
591
+ const lockPromise = $.pool.mysql._locks[key].then(() => {
592
+ return new Promise(resolve => {
593
+ release = resolve;
594
+ });
595
+ });
596
+
597
+ $.pool.mysql._locks[key] = lockPromise;
598
+ return lockPromise.then(() => release);
599
+ }
600
+
601
+ /**
602
+ * @description Mysql管理器,用于创建缓存(线程安全版)
372
603
  * @param {String} scope 作用域
373
604
  * @param {Object} config 配置参数
374
605
  * @return {Object} 返回一个Mysql类实例
375
606
  */
376
607
  function mysqlAdmin(scope, config) {
377
- if (!scope) {
378
- scope = 'sys';
379
- }
380
- var obj = $.pool.mysql[scope];
381
- if (!obj) {
382
- $.pool.mysql[scope] = new Mysql(config);
383
- obj = $.pool.mysql[scope];
384
- }
385
- return obj;
608
+ if (!scope) {
609
+ scope = 'sys';
610
+ }
611
+
612
+ // 检查是否已存在实例
613
+ if ($.pool.mysql[scope]) {
614
+ return $.pool.mysql[scope];
615
+ }
616
+
617
+ // 同步方式创建实例(无需锁,因为Node.js是单线程的)
618
+ if (!$.pool.mysql[scope]) {
619
+ $.pool.mysql[scope] = new Mysql(config);
620
+ }
621
+
622
+ return $.pool.mysql[scope];
386
623
  }
387
624
 
388
625
  /**