一致性哈希分表

四个表结构一样的
ceshi_a、ceshi_b、ceshi_c、ceshi_d
`id` varchar(50) NOT NULL,//主键
`username` varchar(200) DEFAULT NULL,//取哈希值的字段
`hash_value` bigint(50) DEFAULT NULL,//哈希值

代码

定义
	public $tableList = array(); //表列表
    public $virtualPos = array(); //虚拟节点的位置
    public $virtualPosNum = 5;//每个节点对应5个虚节点
取哈希值
	/**
     * 计算哈希值
     * @param $str
     * @return int
     */
    public function cHash($str){
        $str = md5($str);
        return sprintf('%u', crc32($str));
    }
分配表
   	/**
     * 在当前的表中找到合适的表存放数据
     * @param $key 键名
     * @return mixed 表名
     */
    public function lookup($key){
        $point = $this->cHash($key);//落点的hash值
        //dump($point);
        $finalServer = current($this->virtualPos);//先取圆环上最小的一个节点当成结果
        foreach($this->virtualPos as $pos=>$server)
        {
            if($point <= $pos)
            {
                $finalServer = $server;
             //   dump($pos);
                break;
            }
        }
        //dump($finalServer);die;
        reset($this->virtualPos);//重置圆环的指针为第一个
        return $finalServer;
    }
添加表
    /**
     * 添加一个表到表列表中
     * @param $table 表名
     * @return bool
     */
    public function addTable($table)
    {
        if(!isset($this->tableList[$table]))
        {
            for($i=0; $i<$this->virtualPosNum; $i++)
            {
                $pos = $this->cHash($table . '-' . $i);
                $this->virtualPos[$pos] = $table;
                $this->tableList[$table][] = $pos;
            }
            ksort($this->virtualPos,SORT_NUMERIC);
        }
        return true;
    }

    /**
     * 添加表同步数据
     * @param $table
     */
    public function alldata($table){
        $list = [];
        for($i=0; $i<$this->virtualPosNum; $i++)
        {
            $pos = $this->cHash($table . '-' . $i);
            $list[] = (int)$pos;

        }
        $table_list = [];
        foreach ($this->virtualPos as $key=>$value){
            $table_list[] = $key;
        }
        sort($list);
        $end = end($table_list);
        //$start = reset($table_list);
        if(!$table_list){
            return true;
        }
        foreach ($list as $key=>$value){

            $default = 0;

            foreach ($table_list as $k=>$v){
                if($value>$v){
                    $default = $v;

                }else{
                    break;
                }
            }
            if($default){
                if($default==$end){
                    $where = ['hash_value'=>['>=',$value]];
                    $remove_talbe =$table_list[0];
                }else{
                    $where = ['hash_value'=>[['>=',$default],['<',$value],'and']];
                    $remove_talbe = $default;
                }

            }else{
                $where = ['hash_value'=>[['>=',$value],['<',$table_list[0]],'and']];
                $remove_talbe =$table_list[0];
            }
            $data_list = db($this->virtualPos[$remove_talbe])
                ->where($where)
                //  ->fetchSql(true)
                ->select();

            //dump($data_list);
            //continue;
            if(!$data_list){
                continue;
            }
            db()->startTrans();
            $res_data = db($table)->insertAll($data_list);
            if(!$res_data){
                db()->rollback();
                break;//正常写逻辑是要发起重试
            }
            $res_del = db($this->virtualPos[$remove_talbe])->where($where)->delete();
            if(!$res_del){
                db()->rollback();
                break;
            }
            db()->commit();
        }
    }
移除表
    /**
     * 移除一个表(循环所有的虚节点,删除值为该表的虚节点)
     * @param $key
     * @return bool
     */
    public function removeTable($key)
    {
        if(isset($this->tableList[$key]))
        {
            //删除对应虚节点
            foreach($this->tableList[$key] as $pos)
            {
                unset($this->virtualPos[$pos]);
            }
            //移除对应表
            unset($this->tableList[$key]);
        }
        return true;
    }

    /**
     * 移除表同步数据
     * @param $table
     */
    public function deldata($table){
        $tabble_data = db($table)->select();
        if(!$tabble_data){
            return true;
        }
        dump($tabble_data);
        $install_data = [];
        foreach ($tabble_data as $key=>$value){

            $install_data[$this->lookup($value['username'])][] = $value;
        }

        db()->startTrans();
        foreach ($install_data as $k=>$v){
            $res = db($k)->insertAll($v);
            if(!$res){
                db()->rollback();
                return false;//正常做重试,写入消息
            }
        }
        $res_del =db($table)->where('id','neq',1)->delete();
        if(!$res_del){
            db()->rollback();
            return false;
        }
        db()->commit();
        return true;
    }
运行
    public function ceshi(){
        $user_data = [
            'key1',
            'key2',
            'key3',
            'key4',
            'key5',
            'key6',
            'key7',
            'key8',
            'key9'
        ];
        $this->addTable('ceshi_a');
        $this->addTable('ceshi_b');
        $this->addTable('ceshi_c');

        foreach ($user_data as $key=>$value){
            db($this->lookup($value))->insert([
                'id'=>\app\common\Idcreate::createOnlyId(),
                'username'=>$value,
                'hash_value'=>$this->cHash($value)
            ]);
        }
//        dump($this->tableList);
//        dump($this->virtualPos);
        $this->alldata('ceshi_d');
        $this->addTable('ceshi_d');
        $this->removeTable('ceshi_d');
        dump($this->tableList);
        dump($this->virtualPos);
        $this->deldata('ceshi_d');
        
    }

数据没问题,增加虚拟空间数的方法和增加表的思路差不多。
注意
数据量较大的时候同步数据最好是把sql语句生成出来自己去手动运行sql语句或者写存储过程调,记得站点要暂时停止一小会避免同步数据的时候有写操作,要是想自动同步数据的话,把要执行的操作写进消息,让消息处理。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值