首页 > 解决方案 > 创建用户帐户竞争条件

问题描述

我有以下程序:

CREATE OR REPLACE FUNCTION create_user(p_email character varying) RETURNS integer AS
$$
DECLARE
    v_user_id integer;
BEGIN
    SELECT user_id FROM user WHERE email = p_email INTO v_user_id;
    IF v_user_id IS NULL THEN
        INSERT
        INTO user (email, status)
        VALUES (p_email, 'active')
        RETURNING user_id INTO v_user_id;
    END IF;

    RETURN v_user_id;
END;
$$ LANGUAGE plpgsql;

但是,如果对 DB 有多个并行请求,则会导致竞争条件,该过程会被多次调用,并且由于延迟非常低,所有这些请求都试图创建一个用户,并且只有一个成功。有什么好的解决方法吗?

标签: postgresql

解决方案


有什么好的解决方法吗?

是的,在电子邮件列上创建一个唯一索引,然后使用insert on conflict

...
BEGIN
  INSERT INTO "user" (email, status)
  VALUES (p_email, 'active')
  ON CONFLICT (email) DO NOTHING 
  RETURNING user_id INTO v_user_id;

  RETURN v_user_id;
END;

但是,如果电子邮件已存在,则上述内容不会返回 user_id。如果你需要它,你将需要这样的东西:

...
BEGIN
  INSERT INTO "user" (email, status)
  VALUES (p_email, 'active')
  ON CONFLICT (email) DO NOTHING 
  RETURNING user_id INTO v_user_id;

  if v_user_id is null then 
    -- no insert happened
    select user_id 
        into v_user_id
    from "user"
    where email = p_email;
  end if;

  RETURN v_user_id;
END;

推荐阅读