2018年9月

五位的大概有3000万种可能 七位有几百亿可能,虽然重复概率很低,大流量商用建议继续优化

<?php
/**
 * 短链接类
 */
class urlManager {

    //前缀
    public static $prefix = 'weiyixi';

    //后缀
    public static $suffix = 'ixiyiwei';

    //
    public static $index = 0 ;

    //32个基础字符
    public static $base_key = array (
        "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" , "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", "-", "_");

    /**
     * 创建短链接 - 5 位
     *
    网上摘录方法:

    1)将长网址md5生成32位签名串,分为4段, 每段8个字节;

       2)对这四段循环处理, 取8个字节, 将他看成16进制串与0x3fffffff(30位1)与操作, 即超过30位的忽略处理;

       3)这30位分成6段, 每5位的数字作为字母表的索引取得特定字符, 依次进行获得6位字符串;

       4)总的md5串可以获得4个6位串; 取里面的任意一个就可作为这个长url的短url地址;
     *
     */
    public static function create5($long_url,$k=0){
        $out='';
        if($k>3) return;

        $hex = md5(self::$prefix.$long_url.self::$suffix);
        $subhex = substr($hex, $k * 8, 8);

        $int = hexdec($subhex);

        for($i=0; $i<5; $i++)
        {
            $index = $int & 0x3f;
            //最后一位如果是“-”, 则替换为a,避免部分手机上链接显示错误的问题
            if($i == 4 && $index == 62)
                $index = 0;
            $out .= self::$base_key[$index];
            $int = $int >> 6;
        }

        return $out;
    }

    /**
     * 创建短链接 - 7 位
     *
    网上摘录方法:

    1)将长网址md5生成32位签名串,分为3段, 每段42位;

       2)这42位分成7段, 每6位的数字作为字母表的索引取得特定字符, 依次进行获得7位字符串;

     *
     */
    public static function create7($long_url,$k=0){
        $out='';
        $hex = md5(self::$prefix.$long_url.self::$suffix);
        switch($k)
        {
            case 0:
                $subhex = substr($hex, 21, 11); //最后42位
                break;

            case 1:
                $subhex = substr($hex, 11, 11);    //中间42位
                break;

            case 2:
                $subhex = substr($hex, 0, 11);    //最前面的42位,最前2位抛弃
                break;

            default:
                return;
        }

        $int = hexdec($subhex);
        if($k==1)
            $int = $int >> 2; //此处最后2位已被$k=0时所用,故抛弃

        for($i=0; $i<7; $i++)
        {
            $index = $int & 0x3f;
            //最后一位如果是“-”, 则替换为a,避免部分手机上链接显示错误的问题
            if($i == 6 && $index == 62)
                $index = 0;
            $out .= self::$base_key[$index];
            $int = $int >> 6;
        }

        return $out;
    }
}

JSON作为以键值对为主的存储形式,符合很多业务场景,尤其对于python中dict字典数据类型,能直接相互转换,更加适应了python的某些使用情况。
此外,JSON作为不限定固定结构的键值对,能方便灵活的使用在数据变动性比较大的场景,如对于网络数据的爬取收集等,当然其中也存在一些问题,下文再分析。
这里本文先列举MySQL中对一些JSON数据类型的常用语句和函数(参考MySQL官方文档,建议大家有疑惑的直接去查阅官方文档)。
说明:json除了键值对的形式以外,还包括Array数列形式,这在mysql5.7及以上也是支持的,对Array中元素的操作相当于直接对键值对中的值操作,少了“键”的定位这一步,这里不单独展示,所有的函数都都同时支持,大同小异

1. insert(插入)操作

先用insert给出操作面向的数据对象

insert into jwc(st_num,info) value('131141','{"数学":[95,4],"英语":{"英语上":[87,3],"英语下":[82,3]},"体育":"不及格"}')

2. select(投影)操作

对于JSON数据的选择有一个重要的函数——json_extract()

//选择“数学”对应的值
select json_extract(info,"$.数学") from jwc where id='131141'
也可以用info->”$.数学”代替json_extract()函数
//选择“数学”对应的值
select info->"$.数学" from jwc where id='131141'
用json_keys()函数选择所有的键值
select json_keys(info) from jwc
json中的path路径 

.a.b:表示键a下的键b.a[1]:表示键a对应的值(如果是列表的话)的第二个元素

3. where选择操作

json_contains(column,elem,key)函数判断包含带有elem元素的key的column属性是否存在

//注意中间elem(95)一定要用字符串形式表示
select * from jwc where json_contains(info,'95','$.数学')
json_search()函数:直接对“值”(且值必须是字符串,数列或者键值对都不行)或数列中的元素操作
//json_search的第二个参数one表示找一个,all表示找到所有
select * from jwc where json_search(info,'one',"%及格") is not null;
直接利用键值进行选择
select * from jwc where info->'$.数学[0]'>90;

4. update更新操作

json_array_append()函数:直接在定位元素的后面添加

//对于非列表的值直接扩展成列表
 update personal_info set info=json_array_append(info,'$."数学"',4) where id=6;
json_array_insert()函数:在指定位置插入元素
 update personal_info set info=json_array_insert(info,'$."数学"[0]',4) where id=6;
json_set():直接设定某值
update personal_info set info=json_set(info,'$."数学"',4) where id=6;
json_merge(column,key-value)函数:向json类型字段中添加元素
update personal_info set info=json_merge(info,'{"提示":"this is a test"}') where id=6;
json_replace()函数
update personal_info set info=json_replace(info,'$."提示"','this is another test') where id=6;

缺陷与思考

上述 是关于mysql对json数据类型的操作介绍,下面分析一下json数据类型使用的一些问题。
所有的函数在选择时候的依据都是“值”,不能依靠“键”,如json_search,json_contain,这样对于键的选择就必须要指明,这在一些结构不固定,“键”不明确的情况不太方便。
没有发现对“键”的模糊查找
很多情况下,对值的修改都限定为字符串,不能改为数列或者键值对形式。如下将数学的值直接改为数列是不能执行的:

update personal_info set info=json_replace(info,'$."提示"','this is another test','$."数学"',[94,3]) where id=6;

也就说,对数列和字典的支持还不够。
以上提出的这些缺陷或许是因为我对mysql了解不够深入,或许mysql有相应的解决办法,这里暂时存疑,希望知道的朋友能告知,以上。

CURL


/**
 * curl例子  
 * @param $query
 * [
 *  'url'=>'www.baidu.com', //链接
 *  'timeput'=>30,          //超时时间 秒
 *  'headers'=>''          //请求头信息
 *  'postData'=>''         //请求数据
 *  'proxy'=>'127.0.0.1:8888' //代理
 * ]
 */
function curlSetting($query)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $query['url']); // 设置url
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回请求结果,不直接输出
    curl_setopt($ch, CURLOPT_HEADER, true); // 请求结果中也要包含响应的heade0r
    // 设置超时
    $timeout = isset($query['timeout']) ?: 30;
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 允许跳转
    curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024);

//        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
//        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

    // 设置请求HEADER
    if (isset($query['headers'])) {
        if (!isAssocArray($query['headers'])) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $query['headers']);
        } else {
            $headers = [];
            foreach ($query['headers'] as $k => $v) $headers[] = "{$k}: {$v}";
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }
    }

    // 设置post及其数据
    if (isset($query['postData']) && !is_null($query['postData'])) {
        curl_setopt($ch, CURLOPT_POST, true);
        if (is_string($query['postData'])) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $query['postData']);
        } elseif (is_array($query['postData'])) {
            $queryString = http_build_query($query['postData']);
            // 有可能出现为应对 hobbies[]=swimming&hobbies[]=football 这种情况而出现的 hobbies[0]=swimming&hobbies[1]=football
            $queryString = preg_replace('/%5B\d+%5D/simU', '%5B%5D', $queryString);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $queryString);
        }
    }

    // 设置代理
    !empty($query['proxy']) && curl_setopt($ch, CURLOPT_PROXY, $query['proxy']);
    $response = curl_exec($ch);
    $curlInfo = curl_getinfo($ch);
    var_dump($curlInfo);

}

curlMulti

// 多线程抓取
class CurlMulti
{
    protected static $mh = null;

    // task中需要包含url header postdata proxy timeout
    public static function send(array $tasks)
    {
        // 添加任务到并行句柄中
        foreach ($tasks as $task) {
            $ch = self::taskSetting($task['url'], $task['headers'], $task['post_data'], $task['proxy'], $task['timeout']);
            curl_multi_add_handle(self::getMh(), $ch);
        }

        $result = [];
        do {
            curl_multi_exec(self::getMh(), $running);

            if (($info = curl_multi_info_read(self::getMh())) !== false) {
                if ($info['result'] === CURLE_OK) {
                    $curlInfo     = curl_getinfo($info['handle']);
                    $response     = curl_multi_getcontent($info['handle']);
                    $body         = substr($response, - $curlInfo['size_download']);
                    $header     = substr($response, 0, strlen($response) - $curlInfo['size_download']);
                    $taskCur    = $tasks[array_search($curlInfo['url'], array_column($tasks, 'url'))];
                    if (!empty($taskCur['callback'])) $body = call_user_func_array($taskCur['callback'], [$body]);
                    $result[]     = [
                        'code'         => $curlInfo['http_code'],
                        'header'    => $header,
                        'body'        => empty($taskCur['callback']) ? $body : call_user_func_array($taskCur['callback'], [$body]),
                        'curlInfo'     => $curlInfo,
                    ];
                }
                curl_close($info['handle']);
                curl_multi_remove_handle(self::getMh(), $info['handle']);
            }

        } while ($running > 0);

        return $result;
    }

    protected static function taskSetting($url, $headers = [], $postData = null, $proxy = '', $timeout = 30)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        !empty($headers) && curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        if (!is_null($postData)) {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        }
        if (!empty($proxy)) curl_setopt($ch, CURLOPT_PROXY, $proxy);
        return $ch;
    }

    protected static function getMh()
    {
        if (is_null(self::$mh)) {
            self::$mh = curl_multi_init();
        }
        return self::$mh;
    }
}