XPages应用:并发保存时生成无重复用户编号的高性能方案咨询
Hey there! That duplicate sequence number issue during concurrent saves is a classic pain point in XPages apps—glad you're moving away from FTSearch and UpdateIndex, since those are total performance killers for this use case. Let's dive into some solid, efficient alternatives that'll keep your app snappy while guaranteeing unique, sequential IDs.
1. Database Counter Document with Locking
This is the most reliable and straightforward approach for sequential IDs. The idea is to use a single dedicated document in your database to track the current highest sequence number. When a user saves a new record, you lock this counter document, increment the value, assign it to your new document, then unlock and save the counter.
Example Java Code (XPages-friendly):
// Get the current database and counter document Database db = ExtLibUtil.getCurrentDatabase(); Document counterDoc = db.getDocumentByKey("UserSequenceCounter", true); // Initialize counter if it doesn't exist if (counterDoc == null) { counterDoc = db.createDocument(); counterDoc.replaceItemValue("Form", "Counter"); counterDoc.replaceItemValue("CounterID", "UserSequenceCounter"); counterDoc.replaceItemValue("CurrentMax", 0); counterDoc.save(); } // Lock the counter to prevent concurrent writes counterDoc.lock(); try { // Increment the sequence int currentVal = counterDoc.getItemValueInteger("CurrentMax"); int newSequence = currentVal + 1; // Assign this new sequence to your user's document Document userDoc = db.createDocument(); userDoc.replaceItemValue("Form", "User"); userDoc.replaceItemValue("UserID", newSequence); // Add other user fields... userDoc.save(); // Update the counter counterDoc.replaceItemValue("CurrentMax", newSequence); counterDoc.save(); } finally { // Always release the lock to avoid deadlocks counterDoc.unlock(); }
Why this works:
- The
lock()method ensures only one user can modify the counter at a time, eliminating duplicates. - This uses basic document read/write operations—no full-text indexing or expensive searches, so performance stays fast even with many concurrent users.
- It’s persistent across server restarts, so you won’t lose track of the sequence.
2. UUIDs for Non-Sequential Unique IDs
If you don’t strictly need sequential numbers (just unique, user-specific identifiers), skip the sequence entirely and use UUIDs. These are guaranteed to be unique across time and space, with zero concurrency issues.
Example Code:
// Generate a UUID string String uniqueUserID = java.util.UUID.randomUUID().toString(); // Assign to your user document Document userDoc = db.createDocument(); userDoc.replaceItemValue("Form", "User"); userDoc.replaceItemValue("UserID", uniqueUserID); userDoc.save();
Why this works:
- No database locks, no counter documents—just a quick in-memory generation.
- Perfect if the ID doesn’t need to be human-readable or sequential (like internal system IDs).
- Zero performance overhead, even under heavy load.
3. Cached Counter with Database Sync (For High Concurrency)
If you’re dealing with extremely high traffic (hundreds of concurrent saves), you can optimize the counter approach by caching the current sequence value in memory to reduce database hits. Just make sure to sync the cached value back to the counter document periodically (or on server shutdown) to avoid data loss.
Quick Overview:
- Use a static
AtomicIntegerin a Java bean to track the current sequence in memory. - When the app starts, load the initial value from the counter document.
- For each new ID, increment the
AtomicInteger(thread-safe) and assign it. - Sync the cached value to the database every X minutes, or after X increments, to keep it persistent.
This cuts down on database write operations, making it even faster for high-load scenarios.
Why FTSearch/UpdateIndex Was a Bad Fit
Just to confirm your hunch: FTSearch relies on the database’s full-text index, which is slow to query for real-time sequence checks. Calling UpdateIndex forces the server to rebuild the index immediately, which eats up CPU and disk I/O—terrible for performance when multiple users are saving at once. The above methods avoid all that overhead.
Pick the approach that fits your ID requirements: sequential vs. non-sequential, and adjust based on your app’s traffic volume. All of these will keep your XPages app running smoothly without duplicate IDs.
内容的提问来源于stack exchange,提问作者Felipe

