表
四个表结构一样的
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语句或者写存储过程调,记得站点要暂时停止一小会避免同步数据的时候有写操作,要是想自动同步数据的话,把要执行的操作写进消息,让消息处理。