You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多用户Google Sheet数据录入:Lock Service失效与重复ID问题优化

解决方案

核心问题分析

  1. 锁释放时机错误:原代码在弹窗前就释放了锁,弹窗会阻塞函数执行,此时其他并发请求能直接进入临界区,导致ID重复、记录覆盖。
  2. tryLock()容错不足:超时直接放弃操作,未处理锁获取失败的场景,易丢失请求。
  3. 冗余操作拖慢并发:Utilities.sleep(3000)完全多余,增加锁持有时间,降低并发效率。

优化方案

关键优化点

  • 锁包裹全流程:将获取ID、写入记录、更新ID、弹窗提示的所有操作都放在锁的范围内,确保同一时间只有一个请求能执行临界逻辑。
  • 改用waitLock():让请求排队等待锁,避免因超时直接放弃,最多等待10秒防止无限阻塞。
  • finally块释放锁:无论操作成功或失败,确保锁一定会被释放,避免死锁。
  • 提前处理锁失败场景:锁获取失败时直接提示用户,终止后续操作。

优化后的代码

var ss = SpreadsheetApp.getActiveSpreadsheet();
var db = ss.getSheetByName("DB");

function getNewID1() {
  const lock = LockService.getDocumentLock();
  // 等待锁,最多等待10秒
  if (!lock.waitLock(10000)) {
    SpreadsheetApp.getUi().alert("系统繁忙,请稍后重试");
    return;
  }

  try {
    // 获取当前可用ID
    const nextidCell = db.getRange("K1");
    const nextID = nextidCell.getValue();

    // 获取最后一行并写入记录
    const lr = getLastRowSpecial(db, "A1:I");
    db.getRange(lr + 1, 1, 1, 1).setValue(nextID);

    // 更新ID自增
    nextidCell.setValue(nextID + 1);

    // 立即同步修改到服务器
    SpreadsheetApp.flush();

    // 弹窗提示操作完成
    SpreadsheetApp.getUi().alert("ID已分配:" + nextID);
  } finally {
    // 必须释放锁,防止死锁
    lock.releaseLock();
  }
}

function getNewID2() {
  // 与getNewID1逻辑一致,可合并为单函数减少冗余
  const lock = LockService.getDocumentLock();
  if (!lock.waitLock(10000)) {
    SpreadsheetApp.getUi().alert("系统繁忙,请稍后重试");
    return;
  }

  try {
    const nextidCell = db.getRange("K1");
    const nextID = nextidCell.getValue();

    const lr = getLastRowSpecial(db, "A1:I");
    db.getRange(lr + 1, 1, 1, 1).setValue(nextID);

    nextidCell.setValue(nextID + 1);
    SpreadsheetApp.flush();

    SpreadsheetApp.getUi().alert("ID已分配:" + nextID);
  } finally {
    lock.releaseLock();
  }
}

// 获取包含非空内容的最后一行(跳过全空行)
function getLastRowSpecial(sheetlastrow, rangeString) {
  const rng = sheetlastrow.getRange(rangeString).getValues();
  let lrIndex;

  for (let i = rng.length - 1; i >= 0; i--) {
    lrIndex = i;
    if (!rng[i].every(c => c === "")) {
      break;
    }
  }
  return lrIndex + 1;
}

额外建议

  • 合并重复函数:getNewID1和getNewID2逻辑完全相同,可通过传入参数区分提示信息或业务操作,减少代码冗余。
  • 优先使用appendRow:如果数据区域没有中间全空行,直接用db.appendRow([nextID])更高效,无需手动计算最后一行。
  • 并发测试:用多窗口同时触发函数,验证是否还存在重复ID或记录覆盖问题。

内容的提问来源于stack exchange,提问作者Gaspar Lam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 23:15:36