Nodejs在做mysql判重的时候遇到的问题
Nodejs在做mysql判重的时候遇到的问题
如果,同时接收到N条数据,将N条数据存入数据库,此时需对N条数据进行数据库 某些字段 判重,如果数据库已有该数据,则不插入。
遇到的问题就是,假如1和2是相同数据,在做1和2查询数据库相同记录时,都返回没有,因此1和2就都插入数据库了。这不是想要的状态
像JAVA的话多线程,可以在查询数据库相同记录时做同步操作,而nodejs是单线程,没有加锁的概念,而且数据库已经有唯一性约束了,不能通过唯一
性约束来做。。
想问各位大神,有遇到这种情况是怎么解决的吗?
在Node.js中处理MySQL中的数据重复问题确实是一个常见的挑战,尤其是在高并发情况下。以下是一些可能的解决方案以及相应的示例代码。
解决方案
- 使用事务(Transaction):事务可以确保一组操作要么全部成功,要么全部失败。这可以避免在并发情况下出现重复数据的问题。
- 乐观锁(Optimistic Locking):通过版本号或时间戳来实现,但这种方法在MySQL中通常需要额外的列来存储版本信息。
- 悲观锁(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' }
]);
以上两种方法都可以有效解决并发插入时的重复数据问题。