string转url_长链接 转短链接URL的设计思路

正确的原理就是通过发号策略,给每一个过来的长地址,发一个号即可,小型系统直接用mysql的自增索引就搞定了。如果是大型应用,可以考虑各种分布式key-value系统做发号器。不停的自增就行了。第一个使用这个服务的人得到的短地址是 http://xx.xx/0 第二个是 http://xx.xx/1 第11个是 http://xx.xx/a 第依次往后,相当于实现了一个62进制的自增字段即可。

1. 62进制如何用数据库或者KV存储来做?

其实我们并不需要在存储中用62进制,用10进制就好了。比如第10000个长地址,我们给它的短地址对应的编号是9999,我们通过存储自增拿到9999后,再做一个10进制到62进制的转换,转成62进制数即可。这个10~62进制转换,你完全都可以自己实现。

2. 如何保证同一个长地址,每次转出来都是一样的短地址

上面的发号原理中,是不判断长地址是否已经转过的。也就是说用拿着百度首页地址来转,我给一个http://xx.xx/abc 过一段时间你再来转,我还会给你一个 http://xx.xx/xyz。这看起来挺不好的,但是不好在哪里呢?不好在不是一一对应,而一长对多短。这与我们完美主义的基因不符合,那么除此以外还有什么不对的地方?

有人说它浪费空间,这是对的。同一个长地址,产生多条短地址记录,这明显是浪费空间的。那么我们如何避免空间浪费,有人非常迅速的回答我,建立一个长对短的KV存储即可。嗯,听起来有理,但是。。。这个KV存储本身就是浪费大量空间。所以我们是在用空间换空间,而且貌似是在用大空间换小空间。真的划算吗?这个问题要考虑一下。当然,也不是没有办法解决,我们做不到真正的一一对应,那么打个折扣是不是可以搞定?

这个问题的答案太多种,各有各招。这个方案最简单的是建立一个长对短的hashtable,这样相当于用空间来换空间,同时换取一个设计上的优雅(真正的一对一)。实际情况是有很多性价比高的打折方案可以用,这个方案设计因人而异了。那我就说一下我的方案吧。

我的方案是:用key-value存储,保存“最近”生成的长对短的一个对应关系。注意是“最近”,也就是说,我并不保存全量的长对短的关系,而只保存最近的。比如采用一小时过期的机制来实现LRU淘汰。

这样的话,长转短的流程变成这样:

  • 在这个“最近”表中查看一下,看长地址有没有对应的短地址

  • 有就直接返回,并且将这个key-value对的过期时间再延长成一小时

  • 如果没有,就通过发号器生成一个短地址,并且将这个“最近”表中,过期时间为1小时

所以当一个地址被频繁使用,那么它会一直在这个key-value表中,总能返回当初生成那个短地址,不会出现重复的问题。如果它使用并不频繁,那么长对短的key会过期,LRU机制自动就会淘汰掉它。

当然,这不能保证100%的同一个长地址一定能转出同一个短地址,比如你拿一个生僻的url,每间隔1小时来转一次,你会得到不同的短地址。但是这真的有关系吗?

3. 如何保证发号器的大并发高可用

上面设计看起来有一个单点,那就是发号器。如果做成分布式的,那么多节点要保持同步加1,多点同时写入,这个嘛,以CAP理论看,是不可能真正做到的。其实这个问题的解决非常简单,我们可以退一步考虑,我们是否可以实现两个发号器,一个发单号,一个发双号,这样就变单点为多点了?依次类推,我们可以实现1000个逻辑发号器,分别发尾号为0到999的号。每发一个号,每个发号器加1000,而不是加1。这些发号器独立工作,互不干扰即可。而且在实现上,也可以先是逻辑的,真的压力变大了,再拆分成独立的物理机器单元。1000个节点,估计对人类来说应该够用了。如果你真的还想更多,理论上也是可以的。

4. 具体存储如何选择

这个问题就不展开说了,各有各道,主要考察一下对存储的理解。对缓存原理的理解,和对市面上DB、Cache系统可用性,并发能力,一致性等方面的理解。

5. 跳转用301还是302

这也是一个有意思的话题。首先当然考察一个候选人对301和302的理解。浏览器缓存机制的理解。然后是考察他的业务经验。301是永久重定向,302是临时重定向。短地址一经生成就不会变化,所以用301是符合http语义的。同时对服务器压力也会有一定减少。

但是如果使用了301,我们就无法统计到短地址被点击的次数了。而这个点击次数是一个非常有意思的大数据分析数据源。能够分析出的东西非常非常多。所以选择302虽然会增加服务器压力,但是我想是一个更好的选择。

<?php class Tool{    /**     * 64进制转十进制     * @date 2017-11-11     * @param $b64 64进制参数     * @return bool|int 返回10进制数值     */    public static function b64dec($b64){        $map = array(            '0'=>0,'1'=>1,'2'=>2,'3'=>3,'4'=>4,'5'=>5,'6'=>6,'7'=>7,'8'=>8,'9'=>9,            'A'=>10,'B'=>11,'C'=>12,'D'=>13,'E'=>14,'F'=>15,'G'=>16,'H'=>17,'I'=>18,'J'=>19,            'K'=>20,'L'=>21,'M'=>22,'N'=>23,'O'=>24,'P'=>25,'Q'=>26,'R'=>27,'S'=>28,'T'=>29,            'U'=>30,'V'=>31,'W'=>32,'X'=>33,'Y'=>34,'Z'=>35,'a'=>36,'b'=>37,'c'=>38,'d'=>39,            'e'=>40,'f'=>41,'g'=>42,'h'=>43,'i'=>44,'j'=>45,'k'=>46,'l'=>47,'m'=>48,'n'=>49,            'o'=>50,'p'=>51,'q'=>52,'r'=>53,'s'=>54,'t'=>55,'u'=>56,'v'=>57,'w'=>58,'x'=>59,            'y'=>60,'z'=>61,'_'=>62,'='=>63        );        $dec = 0;        $len = strlen($b64);        for ($i = 0; $i < $len; $i++) {            $b = $map[$b64{$i}];            if ($b === null) {                return false;            }            $j = $len - $i - 1;            $dec += ($j == 0 ? $b : (2 << (6 * $j - 1)) * $b);        }        return $dec;    }    /**     * 十进制转64进制     * @date 2017-11-24     * @param $dec     * @return bool|string 返回64进制     */    public static function decb64($dec){        if ($dec < 0) {            return FALSE;        }        $map = array(            0=>'0',1=>'1',2=>'2',3=>'3',4=>'4',5=>'5',6=>'6',7=>'7',8=>'8',9=>'9',            10=>'A',11=>'B',12=>'C',13=>'D',14=>'E',15=>'F',16=>'G',17=>'H',18=>'I',19=>'J',            20=>'K',21=>'L',22=>'M',23=>'N',24=>'O',25=>'P',26=>'Q',27=>'R',28=>'S',29=>'T',            30=>'U',31=>'V',32=>'W',33=>'X',34=>'Y',35=>'Z',36=>'a',37=>'b',38=>'c',39=>'d',            40=>'e',41=>'f',42=>'g',43=>'h',44=>'i',45=>'j',46=>'k',47=>'l',48=>'m',49=>'n',            50=>'o',51=>'p',52=>'q',53=>'r',54=>'s',55=>'t',56=>'u',57=>'v',58=>'w',59=>'x',            60=>'y',61=>'z',62=>'_',63=>'=',        );        $b64 = '';        do {            $b64 = $map[($dec % 64)] . $b64;            $dec /= 64;        } while ($dec >= 1);        return $b64;    }}

2b1947ba0c6812ae5328426e4f892032.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一些思路,具体实现可能需要结合您的具体需求来进行编写。 首先,您需要在JAVA中连接MySQL数据库。这可以通过JDBC来实现。您需要下载MySQL的JDBC驱动程序,然后在JAVA中使用以下代码来连接数据库: ``` import java.sql.*; public class DatabaseConnection { Connection conn = null; public Connection connect() { try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/your_database_name"; conn = DriverManager.getConnection(url, "root", "your_password"); System.out.println("Connection successful!"); } catch (Exception e) { System.out.println("Connection failed!"); e.printStackTrace(); } return conn; } } ``` 在上面的代码中,您需要将“your_database_name”和“your_password”替换为您的数据库名称和密码。 接下来,您需要设计超市收银系统的数据表结构。您需要在MySQL中创建一个数据库,然后创建一个或多个表来存储商品信息、订单信息等数据。例如,您可以创建一个名为“product”的表来存储商品信息,包括商品编号、商品名称、商品单价等等。 然后,您需要在JAVA中编写代码来实现添加商品、查询商品、结算订单等功能。这些功能的实现需要使用SQL语句来操作数据库。 以下是一个简单的添加商品的示例代码: ``` public void addProduct(Product product) { try { Connection conn = new DatabaseConnection().connect(); PreparedStatement pstmt = conn.prepareStatement("insert into product (product_id, product_name, product_price) values (?, ?, ?)"); pstmt.setString(1, product.getId()); pstmt.setString(2, product.getName()); pstmt.setDouble(3, product.getPrice()); pstmt.executeUpdate(); System.out.println("Product added successfully!"); } catch (Exception e) { System.out.println("Product add failed!"); e.printStackTrace(); } } ``` 在这个示例代码中,我们使用了PreparedStatement对象来执行SQL语句,并使用占位符“?”来代替实际的值。在执行SQL语句之前,我们需要先调用setString()和setDouble()方法来设置占位符的值。 类似地,您可以编写代码来查询商品信息、计算订单总价等等。 希望这些思路可以帮助您完成超市收银系统的开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值