前语

本文是一次学习分享,主要是我从一个面试题引发的考虑,然后结合实际事务处理问题的案例。从这篇文章中,大家能够考虑怎么样才能够触类旁通,怎么样结合自己的实际情况去打破技能瓶颈,?

由于公司扩展了新的事务,咱们有了一个运营活动需求一起运行在多个App的需求,但是,比方某个活动前端只开发一次,但是需求布置在两个域名下(有一些法务上的约束),关于运营来说,假如每个活动都要让他们装备两次的话,无疑是增加了他们的工作担负,并且还可能呈现错配的问题。因此,就引入了一个短链的需求(实际上便是用一个地址依据App的UserAgent来做分发,但是为了照顾到别的事务线的运营,咱们就直接把它当做一个短链需求来完成),咱们直接把跳转的内容相关到某个短链上,直接把短链给运营进行投放。

面试题之数字转Excel单元格序号

这道面试题,我相信很多同学都做过,比方下面这个图:

由一道简略的手写题引出的方案设计——短链雪花算法
横轴上面的序号是由A-Z,第27列便是AA,其实就适当所以26进制,A代表1,27便是26*10^1+26*10^0得来的。

现在,请编写一个算法,给你一个合法的整数得到转化成Excel横轴上面的字母标识法(即26进制)的内容。

这个题的处理方法,在初等数学就现已讲过叫除K取余法,不过关于很久现已没有再看过数学教材的咱们来说,如同现已忘了这个解法的进程了。咱们就不管用什么方法,反正尽量写出这道题吧。

我的思路是,假定从最左边开端取,有多少个26,假定几百上千个,怎么去确认最高位的数字?那如同就完全没方法做。

那从右边开端算呢?由于咱们总能求得个位上的数的,所以拿这个整数对26取余,这样就得到了个位上的字母了,由于把个位上的字母现已得到了,那么用这个数减去这个个位上的数字之后,剩下的数便是26的整26倍了。

假如咱们现在把这个剩下是整26倍的数除以26,本来的从右边数的第二位不就变成了第一位了吗?如同现已把刚才的那个数字的规划减小了,并且又回到了重复的问题,就这样一向往从右往左递推,那这样就能把问题处理了。

function transform(num) {
  let result = '';
  const charMap = [
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
    'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
    'U', 'V', 'W', 'X', 'Y', 'Z'
  ];
  while (num >= 26) {
    const digit = (num - 1) % 26;
    const alphabet = charMap[digit];
    result = alphabet + result;
    num = Math.floor((num - 1) / 26);
  }
  // 处理最终一位数
  result = charMap[num - 1] + result;
  return result;
}

168. Excel表列称号 – 力扣(LeetCode)

短链雪花算法

什么是短链?

短链,一般便是一个很简洁的域名,然后后面跟上一个有含义的参数(比方,v.dy.cn/w1Uirh2,我随意写的,不必定打的开哦),但是咱们作为用户不知道它的含义,这个短链服务一般会作为公司的全栈架构进口,一般能够作为BFF中的一个功能,也能够单独开发布置。服务端能够依据用户的UserAgent的不同,把用户的恳求导向到对应的事务服务。

关于用户来说,短链比较好回忆,他实际上只是只需求记住短链码即可;关于开发者来说,短链服务是一个很好的导流进口,由于开发者能够很简略的拿到用户的UserAgent,就能够依据用户的信息吞吐用户当时设备感兴趣的内容。

比方,在我从前上任过的一家公司,关于运营来说,有一个叫做超级二维码的功能。这个二维码假如用App扫开,打开的是一个内嵌H5页面;假如是用小程序扫,能够直接打开公司的小程序,这个地址便是依据小程序的标识,返回了一个对应的scheme;而内嵌H5直接以302方法的重定向到了一个网页,其它运用类型以此类推。

生成短链码

短链服务的呼应有必要要快,在理解了短链服务的含义之后,对咱们的雪花算法功率就有必定的要求,所以问题的要害便是在于怎么生成短链码,怎么快速的解析短链码。

咱们需求用一张表来存这些短链的跳转内容,整数作为主键和UUID作为主键都有各自的优势和考虑要素,以下是运用整数主键相比运用UUID主键的一些优势:

  1. 功率和功能:整数主键一般比UUID主键更高效,由于它们在存储和比较时占用更少的空间。这能够减小磁盘和内存耗费,进步查询功能,特别是在大型数据集上;整数主键一般更简略进行索引,因此查询操作一般更快速。

  2. 数据库巨细:整数主键一般需求更少的存储空间,这在大型数据库中能够节约很多的磁盘空间。

  3. 衔接操作:在衔接不同表时,运用整数主键一般更高效,由于比较整数比较UUID更快。

  4. 数据共同性:整数主键一般更简略确保数据的共同性,由于它们不简略遭到UUID生成算法的影响。

然而,运用UUID作为主键也有其优势,特别是在分布式系统和分布式数据库中:

  1. 大局仅有性:UUID确保了全球范围内的仅有性,即便在分布式环境中也能够安全运用。这关于分布式系统和多个数据库实例之间的数据同步十分有用。

  2. 安全性:UUID不简略被猜想或计算出来,因此能够进步数据的安全性。

  3. 独立性:UUID主键不依赖于特定数据库或生成次序,因此更简略在不同数据库之间迁移和仿制数据。

  4. 随机性:UUID主键具有随机性,这能够协助减轻锁定和热点问题,特别是在高并发环境下。

比较简略想到的便是,用UUID生成一个指定长度的内容,然后取指定的长度。既然是短链,要方便用户回忆,那肯定不可能太长,假如UUID的长度取的太短,咱们不禁心里面会有个问号,真的不会呈现重复吗?

假定咱们在数据库中用自增ID,然后采用哈希算法,然后再取多少位,依然跟前面的算法存在相同的问题。

所以,在这个时分,我就想到了我在上节说到的面试题。我以26个大写字母+26个小写字母+10个数字对数据库的自增ID做进制转化,那么短链肯定是不会太长了,并且两个ID生成出来的内容必定是不会重复的。

此刻,我又进一步考虑了一个问题,假如我的短链服务就这样被他人发现了规则,那岂不是我的一切内容都能够被他人爬取完了吗?哈哈哈,,能够玩儿一个阴招,我干嘛那么厚道啊,谁告诉你0就真的是代表0啊,哈哈哈。

所以我将这62个字符乱序,得到一个排列,然后将这个序列保存下来,这个便是今后短链的秘钥,加密解密都能够用它来做。

好了,这样就能确保短链码跟数据库表记载有仅有的相关,并且短链码能够转出数据记载的ID,数据记载的ID也能转出短链码。

这个代码简略的令人发指,哈哈哈:

function getShortLinkCode(num) {
  let result = '';
  // 我的字符映射码没有乱序,实际项目中必定要乱序,并且必定要存下来
  const charMap = [
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
    'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
    'U', 'V', 'W', 'X', 'Y', 'Z', 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
    'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
    'u', 'v', 'w', 'x', 'y', 'z',
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
  ];
  // 只是只需求将本来的26进制转化为62进制即可
  while (num >= 62) {
    const digit = (num - 1) % 62;
    const alphabet = charMap[digit];
    result = alphabet + result;
    num = Math.floor((num - 1) / 62);
  }
  // 处理最终一位数
  result = charMap[num - 1] + result;
  return result;
}

另外,还需求一个依据短链码求解表记载ID的函数,这个函数相对来说要简略一些,但是必定要跟生成短链进程中所用到字符次序是共同的

function parsePkFromShortLinkCode(code) {
  // 我的字符映射码没有乱序,实际项目中必定要乱序,并且必定要存下来
  const charMap = {
    'A': 1,
    'B': 2,
    'C': 3,
    'D': 4, 
    'E': 5,
    'F': 6,
    'G': 7,
    'H': 8,
    'I': 9,
    'J': 10,
    'K': 11,
    'L': 12,
    'M': 13,
    'N': 14,
    'O': 15,
    'P': 16,
    'Q': 17,
    'R': 18,
    'S': 19,
    'T': 20,
    'U': 21,
    'V': 22,
    'W': 23,
    'X': 24,
    'Y': 25,
    'Z': 26, 
    'a': 27,
    'b': 28,
    'c': 29,
    'd': 30,
    'e': 31,
    'f': 32,
    'g': 33,
    'h': 34,
    'i': 35,
    'j': 36,
    'k': 37,
    'l': 38,
    'm': 39,
    'n': 40,
    'o': 41,
    'p': 42,
    'q': 43,
    'r': 44,
    's': 45,
    't': 46,
    'u': 47,
    'v': 48, 
    'w': 49,
    'x': 50,
    'y': 51,
    'z': 52,
    '0': 53,
    '1': 54,
    '2': 55,
    '3': 56,
    '4': 57,
    '5': 58,
    '6': 59,
    '7': 60,
    '8': 61,
    '9': 62
  };
  // 从个位一向求和到最高位,所以需求reverse一下
  return code.split('').reverse().reduce((total, char, idx) => {
      return (total += charMap[char] * 62 ** idx)
  }, 0)
}

试一下,看看转化是否正确:

由一道简略的手写题引出的方案设计——短链雪花算法

为了让短链不至于太短,咱们能够初试数据库的自增ID从1E开端。

这个雪花算法能够在确保功能的前提下,内容不至于被爬虫爬取。

结语

尽管很多同学比较反感刷题,不过从我的经历来说的话,刷题是成为高手的一个必经之路,是打破自己技能瓶颈至关重要的一个阶段。在刷题的进程中,往往咱们能够才智到很多不常见的编程手法,俗话说好记忆不如烂笔头,自己亲自写一遍,知识就简略转化成咱们自己的,形成永久回忆。

在我的这个场景中,也是经过刷题积累到的,然后在适宜的时机用对了适宜的东西就能迅速的处理问题。关于刷题也没必要怼着一类题型不停的刷刷刷,假如当你现已完全把握到了一类问题的处理方案之后,就能够跳过了,假如过一段时间你觉得可能忘了,然后再回过头来尝试做几道这个类型的题加深回忆,多重复几次,就形成了永久回忆(至少我是这样的,遇到问题的时分,脑子里下意识的反响便是,这个问题我从前处理过,哈哈哈)

关于本文论述的内容有任何疑问的同学能够在评论区留言或私信我。

假如大家喜欢我的文章,能够多多点赞收藏加重视,你们的认但是我最好的更新动力,。