Nodejs在做mysql判重的时候遇到的问题

Nodejs在做mysql判重的时候遇到的问题

如果,同时接收到N条数据,将N条数据存入数据库,此时需对N条数据进行数据库 某些字段 判重,如果数据库已有该数据,则不插入。

遇到的问题就是,假如1和2是相同数据,在做1和2查询数据库相同记录时,都返回没有,因此1和2就都插入数据库了。这不是想要的状态

像JAVA的话多线程,可以在查询数据库相同记录时做同步操作,而nodejs是单线程,没有加锁的概念,而且数据库已经有唯一性约束了,不能通过唯一

性约束来做。。

想问各位大神,有遇到这种情况是怎么解决的吗?


6 回复

在Node.js中处理MySQL中的数据重复问题确实是一个常见的挑战,尤其是在高并发情况下。以下是一些可能的解决方案以及相应的示例代码。

解决方案

  1. 使用事务(Transaction):事务可以确保一组操作要么全部成功,要么全部失败。这可以避免在并发情况下出现重复数据的问题。
  2. 乐观锁(Optimistic Locking):通过版本号或时间戳来实现,但这种方法在MySQL中通常需要额外的列来存储版本信息。
  3. 悲观锁(Pessimistic Locking):通过SELECT ... FOR UPDATE语句锁定记录,防止其他会话在当前事务完成前修改这些记录。

示例代码

假设我们有一个表 users,其中包含字段 email,并且我们希望在插入新用户之前检查是否已存在相同的 email

使用事务

const mysql = require('mysql');
const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'testdb'
});

connection.connect();

async function insertUser(user) {
  const email = user.email;

  try {
    await new Promise((resolve, reject) => {
      connection.beginTransaction(function (err) {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      });
    });

    const checkQuery = "SELECT * FROM users WHERE email = ?";
    const [rows] = await new Promise((resolve, reject) => {
      connection.query(checkQuery, [email], (err, results) => {
        if (err) {
          reject(err);
        } else {
          resolve(results);
        }
      });
    });

    if (rows.length > 0) {
      console.log("Email already exists");
      connection.rollback(() => {
        connection.end();
      });
      return;
    }

    const insertQuery = "INSERT INTO users (email, name) VALUES (?, ?)";
    await new Promise((resolve, reject) => {
      connection.query(insertQuery, [email, user.name], (err, results) => {
        if (err) {
          reject(err);
        } else {
          resolve(results);
        }
      });
    });

    connection.commit((err) => {
      if (err) {
        console.error("Error during transaction:", err.message);
        connection.rollback(() => {
          connection.end();
        });
      } else {
        console.log("User inserted successfully");
        connection.end();
      }
    });
  } catch (error) {
    console.error("Transaction failed:", error.message);
    connection.rollback(() => {
      connection.end();
    });
  }
}

// 测试插入用户
insertUser({ email: 'test@example.com', name: 'Test User' });

解释

  • 事务:通过beginTransaction开始一个事务,commit提交事务,rollback回滚事务。这样可以确保如果检查到重复的数据,不会执行插入操作,并且可以安全地回滚事务。
  • 查询重复数据:在事务中先查询是否有重复的 email,如果没有则插入新用户。
  • 错误处理:使用try...catch捕获并处理可能出现的错误。

这种方法可以有效地解决并发情况下的数据重复问题。


为什么不是mysql设置 字段 unique

A+B unique , A+B+C unique, 能推到出 C unique 么?

推导不严密~ 有问题,不能通过拆分索引解决。。。

a1+b1, c1
a2+b1, c1

你这个问题原本就有问题,mongodb不敢说,因为不太懂。。 A+B+C unique = A(unique) && A,B(unique) && A,B,C(unique) 在你的应用场景里完全浪费索引空间,标题 + 内容 + 发布时间这样的结构"发布时间"还要做唯一不是蛋痛没事做吗,还不如做A+B unique。

在Node.js中处理MySQL判重问题时,可以采用以下几种方法来避免重复插入数据:

方法一:使用事务

你可以使用数据库事务来确保操作的原子性,即要么所有操作都成功,要么都不执行。事务可以保证在并发情况下,数据的一致性。

const mysql = require('mysql');
const connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'testdb'
});

connection.connect();

function insertData(data, callback) {
    const queryCheck = `SELECT * FROM your_table WHERE field = ?`;
    const queryInsert = `INSERT INTO your_table (field, other_field) VALUES (?, ?)`;

    connection.beginTransaction(function (err) {
        if (err) {
            return callback(err);
        }

        data.forEach(async (item) => {
            try {
                let [rows] = await new Promise((resolve, reject) => {
                    connection.query(queryCheck, [item.field], (err, rows) => {
                        if (err) {
                            reject(err);
                        } else {
                            resolve(rows);
                        }
                    });
                });

                if (!rows.length) {
                    await new Promise((resolve, reject) => {
                        connection.query(queryInsert, [item.field, item.other_field], (err) => {
                            if (err) {
                                reject(err);
                            } else {
                                resolve();
                            }
                        });
                    });
                }
            } catch (err) {
                connection.rollback(() => {
                    console.error('Error occurred:', err.message);
                });
            }
        });

        connection.commit(function (err) {
            if (err) {
                return callback(err);
            }
            callback(null, 'All data inserted successfully');
        });
    });
}

// 使用示例
insertData([
    { field: 'value1', other_field: 'other_value1' },
    { field: 'value2', other_field: 'other_value2' }
], (err, result) => {
    if (err) {
        console.error('Error:', err);
    } else {
        console.log(result);
    }
});

方法二:批量检查

另一种方法是在插入前先批量检查数据库中是否存在相同的记录,然后只插入不存在的记录。

async function batchInsert(data) {
    const queryCheck = `SELECT field FROM your_table WHERE field IN (?)`;
    const queryInsert = `INSERT INTO your_table (field, other_field) VALUES ?`;

    // 获取已存在的字段值
    let existingFields = await new Promise((resolve, reject) => {
        connection.query(queryCheck, [data.map(item => item.field)], (err, rows) => {
            if (err) {
                reject(err);
            } else {
                resolve(rows.map(row => row.field));
            }
        });
    });

    // 过滤出需要插入的数据
    let newData = data.filter(item => !existingFields.includes(item.field));

    if (newData.length > 0) {
        let values = newData.map(item => [item.field, item.other_field]);
        await new Promise((resolve, reject) => {
            connection.query(queryInsert, [values], (err) => {
                if (err) {
                    reject(err);
                } else {
                    resolve();
                }
            });
        });
    }

    console.log('Batch insert completed.');
}

// 使用示例
batchInsert([
    { field: 'value1', other_field: 'other_value1' },
    { field: 'value2', other_field: 'other_value2' }
]);

以上两种方法都可以有效解决并发插入时的重复数据问题。

回到顶部