引言
在mongodb中,自动增长的功能主要通过使用数据库的objectid或自定义的序列来实现。objectid是mongodb默认的主键类型,它是唯一的并且具有一定的排序特性。然而,在某些场景下,可能需要使用自定义的自动增长id,例如在某些遗留系统中或者为了更好的用户体验。
基本语法和命令
使用objectid
objectid是mongodb默认的主键类型,它由12字节组成,包括时间戳、机器标识符、进程id和计数器。每次插入新文档时,mongodb会自动生成一个新的objectid。
插入新文档时,_id字段会自动生成:
db.collection.insertone({name: "example"})
自定义自动增长id
如果需要自定义自动增长id,可以使用以下方法:
创建计数器集合:
创建一个专门的集合来存储序列计数器。
db.createcollection("counters")
db.counters.insertone({_id: "productid", seq: 0})
定义获取下一个序列值的函数:
使用findandmodify原子操作来获取并增加序列值。
function getnextsequence(name) {
var ret = db.counters.findandmodify({
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
});
return ret.seq;
}
插入新文档并使用自定义id:
在插入新文档时,调用该函数以生成新的id。
db.products.insertone({
_id: getnextsequence("productid"),
name: "example"
})
示例
以下是完整的示例代码:
- 创建计数器集合并插入初始值:
db.counters.insertone({_id: "userid", seq: 0})
- 定义获取下一个序列值的函数:
function getnextsequence(name) {
var ret = db.counters.findandmodify({
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
});
return ret.seq;
}
- 插入新文档并使用自定义id:
db.users.insertone({
_id: getnextsequence("userid"),
name: "john doe"
})
应用场景
1. 遗留系统迁移
详解:
在许多企业中,遗留系统使用关系数据库(如mysql、postgresql等),并依赖于自增id作为主键。如果计划将这些系统迁移到mongodb中,直接使用mongodb的objectid可能会导致兼容性问题或破坏现有业务逻辑。因此,自定义自动增长id可以简化迁移过程,保留原有系统的id生成机制。
示例场景:
假设一家电子商务公司决定将其产品数据库从mysql迁移到mongodb。原系统中的产品id是自增的整数。
// mysql中的产品表
create table products (
id int auto_increment primary key,
name varchar(255),
price decimal(10, 2)
);
// 原有数据
insert into products (name, price) values ('laptop', 999.99), ('smartphone', 699.99);
在迁移到mongodb时,需要保留这些自增的id。
// 在mongodb中创建计数器集合
db.counters.insertone({_id: "productid", seq: 2}) // 假设mysql中已有两个产品
// 定义获取下一个序列值的函数
function getnextsequence(name) {
var ret = db.counters.findandmodify({
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
});
return ret.seq;
}
// 插入新产品时使用自定义id
db.products.insertone({
_id: getnextsequence("productid"),
name: "tablet",
price: 499.99
});
2. 用户友好id
详解:
对于前端用户,使用连续的、自增的数字id比使用objectid更友好、更容易记忆。特别是在需要用户手动输入或引用id的场景中,自增id会更简洁、易读。
示例场景:
一个博客平台希望用户能够通过短链接直接访问文章。使用自增id可以生成短链接,提升用户体验。
// 创建计数器集合
db.counters.insertone({_id: "postid", seq: 0})
// 定义获取下一个序列值的函数
function getnextsequence(name) {
var ret = db.counters.findandmodify({
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
});
return ret.seq;
}
// 插入新文章时使用自定义id
db.posts.insertone({
_id: getnextsequence("postid"),
title: "how to use mongodb",
content: "mongodb is a nosql database..."
});
// 生成的短链接
var postid = getnextsequence("postid");
var shortlink = `http://blogplatform.com/post/${postid}`; // http://blogplatform.com/post/1
3. 特定业务需求
详解:
某些业务逻辑需要使用连续的、自增的数字id。例如,订单管理系统可能需要连续的订单号,以便于财务对账和客户查询。
示例场景:
一家在线零售商需要为每个订单生成连续的订单号,以便于物流跟踪和客户服务。
// 创建计数器集合
db.counters.insertone({_id: "orderid", seq: 1000}) // 假设订单号从1001开始
// 定义获取下一个序列值的函数
function getnextsequence(name) {
var ret = db.counters.findandmodify({
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
});
return ret.seq;
}
// 插入新订单时使用自定义id
db.orders.insertone({
_id: getnextsequence("orderid"),
customername: "alice",
items: [
{productid: 1, quantity: 2},
{productid: 2, quantity: 1}
],
total: 1699.97
});
// 新生成的订单号
var orderid = getnextsequence("orderid");
console.log("new order id: " + orderid); // new order id: 1001
注意事项
1. 并发问题
详解:
在高并发环境中,多个请求同时访问计数器集合时,必须确保findandmodify操作是原子的,以避免生成重复id。mongodb的findandmodify操作是原子的,它可以保证在高并发环境下每次操作都是唯一的,从而避免重复id的生成。
示例代码:
假设有一个计数器集合counters,我们使用以下代码来确保原子性:
// 获取下一个自增id的函数
function getnextsequencevalue(sequencename) {
var sequencedocument = db.counters.findandmodify({
query: { _id: sequencename },
update: { $inc: { sequence_value: 1 } },
new: true,
upsert: true
});
return sequencedocument.sequence_value;
}
// 使用示例
var nextuserid = getnextsequencevalue('user_id');
db.users.insert({ _id: nextuserid, name: "john doe" });
2. 性能影响
详解:
频繁更新计数器集合可能会成为性能瓶颈,尤其是在高并发环境中。每次获取新的id都需要对计数器集合进行读写操作。这种频繁的读写操作可能会影响数据库的整体性能。为了解决这个问题,可以考虑使用分布式id生成算法,如twitter的snowflake,它生成的id不仅是唯一的,而且是分布式的,不需要频繁访问数据库。
示例代码:
使用javascript实现简单版的snowflake算法:
class snowflake {
constructor(workerid, datacenterid, sequence = 0) {
this.workerid = workerid;
this.datacenterid = datacenterid;
this.sequence = sequence;
this.twepoch = 1288834974657n;
this.workeridbits = 5n;
this.datacenteridbits = 5n;
this.maxworkerid = -1n ^ (-1n << this.workeridbits);
this.maxdatacenterid = -1n ^ (-1n << this.datacenteridbits);
this.sequencebits = 12n;
this.workeridshift = this.sequencebits;
this.datacenteridshift = this.sequencebits + this.workeridbits;
this.timestampleftshift = this.sequencebits + this.workeridbits + this.datacenteridbits;
this.sequencemask = -1n ^ (-1n << this.sequencebits);
this.lasttimestamp = -1n;
}
tilnextmillis(lasttimestamp) {
let timestamp = this.timegen();
while (timestamp <= lasttimestamp) {
timestamp = this.timegen();
}
return timestamp;
}
timegen() {
return bigint(date.now());
}
nextid() {
let timestamp = this.timegen();
if (timestamp < this.lasttimestamp) {
throw new error('clock moved backwards. refusing to generate id');
}
if (this.lasttimestamp === timestamp) {
this.sequence = (this.sequence + 1n) & this.sequencemask;
if (this.sequence === 0n) {
timestamp = this.tilnextmillis(this.lasttimestamp);
}
} else {
this.sequence = 0n;
}
this.lasttimestamp = timestamp;
return ((timestamp - this.twepoch) << this.timestampleftshift) |
(this.datacenterid << this.datacenteridshift) |
(this.workerid << this.workeridshift) |
this.sequence;
}
}
// 使用示例
const snowflake = new snowflake(1n, 1n);
const id = snowflake.nextid();
console.log(id.tostring()); // 生成唯一id
3. 唯一性保证
详解:
在分布式环境中,确保id的唯一性是一个挑战。即使在多个节点上生成id,也必须保证每个id是唯一的。通过使用分布式id生成算法(如上所述的snowflake),可以在多个节点上生成唯一的id,而不需要依赖单一的数据库计数器。
示例代码:
继续使用上面的snowflake示例,在多个服务节点上生成唯一id:
// 服务节点1 const snowflake1 = new snowflake(1n, 1n); const id1 = snowflake1.nextid(); console.log(id1.tostring()); // 唯一id // 服务节点2 const snowflake2 = new snowflake(2n, 1n); const id2 = snowflake2.nextid(); console.log(id2.tostring()); // 唯一id
通过以上示例,在不同的服务节点上生成的id仍然是唯一的,确保了分布式环境中的id唯一性。
总结
在mongodb中,objectid提供了一种简单且有效的唯一标识符生成方式,但在某些情况下,自定义的自动增长id可能更适合。通过创建计数器集合和使用findandmodify操作,可以实现自定义的自动增长id。需要注意的是,在实现自定义自动增长id时,必须处理好并发和性能问题,以确保id的唯一性和生成效率。
以上就是mongodb中自动增长id详解(实现、应用及优化)的详细内容,更多关于mongodb自动增长id的资料请关注代码网其它相关文章!
发表评论