用自增ID生成另一个长串数字的方法

17 篇文章 0 订阅
3 篇文章 0 订阅

        实际应用场景中经常会遇到用什么方式来生成userId,常用的方式是直接使用Mysql的自增主键作为userId,优点是简单方便,插入时无须考虑userId重复的问题,缺点是位数不一致,如果userId还需要暴露给用户或者第三方,容易让他人估计出用户总数量,同时伪造userId也变得更方便。所以需要一个更好的方式来产生userId来代替自增主键。

        以下算法可以通过一个数字产生另一个位数相对固定的数字,同时还是可逆的。

$seeds = [
    '7893401256',
    '7296853041',
    '9832107654',
    '8952346701',
    '6035892471',
    '5293076418',
    '8721643095',
    '4532980167',
];

function contactID2userID(int $contactID, array $seeds): int {
    $letters = count($seeds);
    $kinds_ucode = 10;

    // 替换最后 8 位数字
    $rv = []; // 替换前的ID为数组
    $nv = [];
    $rv[0] = $contactID % $kinds_ucode;
    $nv[0] = ($contactID - $rv[0]) / $kinds_ucode;
    for ($i = 1; $i < $letters; $i++) {
        $j = $i - 1;
        $rv[$i] = $nv[$j] % $kinds_ucode;
        $nv[$i] = ($nv[$j] - $rv[$i]) / $kinds_ucode;
    }
    $rv[$letters] = $nv[$letters - 1];//在第 9 位之后取 9,10,11,...

    $iv = [];
    $iv[0] = $rv[0];
    for ($i = 1; $i < $letters; $i++) {
        $iv[$i] = ($rv[$i] + $rv[0]) % $kinds_ucode;
    }

    $xv = []; // 替换后的 ID 排列成数组
    for ($i = 0; $i < $letters; $i++) {
        $xv[$i] = substr($seeds[$i], $iv[$i], 1); // 替换为 $seeds[$i]的数字字符串中的$idxth数字
    }

    // 第9位是数字+1的值(这样就不会变成0)
    $xv[$letters] = $rv[$letters] + 1;

    $result = 0;
    for ($i = 0; $i <= $letters; $i++) {
        $result += (10 ** $i) * $xv[$i];
    }

    return $result;
}

function userID2contactID(int $userID, array $seeds): int {
    $letters = count($seeds);
    $kinds_ucode = 10;

    $xv = [];
    $nv = [];
    $xv[0] = $userID % $kinds_ucode;
    $nv[0] = ($userID - $xv[0]) / $kinds_ucode;
    for ($i = 1; $i < $letters; $i++) {
        $j = $i - 1;
        $xv[$i] = $nv[$j] % $kinds_ucode;
        $nv[$i] = ($nv[$j] - $xv[$i]) / $kinds_ucode;
    }
    $xv[$letters] = $nv[$letters - 1];  // 在第 9 位之后取 9,10,11,...

    $iv = [];
    for ($i = 0; $i < $letters; $i++) {
        $iv[$i] = strpos($seeds[$i], (string) $xv[$i]); // 用 $xv[$i] 在 $seed[$i] 中搜索
    }

    $rv = [];
    $rv[0] = $iv[0];
    for ($i = 1; $i < $letters; $i++) {
        $rv[$i] = ($iv[$i] - $rv[0] + $kinds_ucode) % $kinds_ucode;
    }

    // 在第9位,数字值增加了1,所以恢复它
    $rv[$letters] = $xv[$letters] - 1;

    $result = 0;
    for ($i = 0; $i <= $letters; $i++) {
        $result += (10 ** $i) * $rv[$i];
    }

    return $result;
}

echo contactID2userID(1, $seeds);   // 157209828
echo userID2contactID(157209828, $seeds);   // 1

        如何更好的获取自增ID?

        方式一:利用Mysql的自增主键,但是需要先insert一条记录再生成userId后update回去,这样效率是很低的

        方式二:利用Redis的INCRBY,可以达到预期效果,只需要将Redis里的自增值同时记录到DB中即可,方便Redis丢失时可以还原最大自增值。

        方式三:在Redis Cluster集群模式下,由于集群内会存在多台Redis实例,那么我们可以考虑利用多个RedisKey的自增来提高自增的吞吐量,比如设置16个自增key,名字依次是incr_key_[1-16],那么这16个key算出来的hash slot必定不同,很可能就分布在多台实例上,那么计算自增值的方式

$suffix = mt_rand(1, 16);
$key = "incr_key_" . $suffix;
$uniqueID = $redis->incrby($key);
$contactID = $suffix + $uniqueID * 16;
$userID = contactID2userID($contactID);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值