Пессимистичная: блокируем строки при чтении (SELECT FOR UPDATE), другие ждут. В Go: tx.QueryRowContext(ctx, "SELECT * FROM orders WHERE id = $1 FOR UPDATE", id). Оптимистичная: читаем без блокировки, при обновлении проверяем версию/значения (version column или WHERE с старыми значениями); при конфликте - retry. В Go реализуют через UPDATE ... WHERE id = $1 AND version = $2 и проверку RowsAffected.
result, _ := tx.ExecContext(ctx, "UPDATE orders SET total = $1, version = version+1 WHERE id = $2 AND version = $3", total, id, version)
if n, _ := result.RowsAffected(); n == 0 { return ErrConflict }