**php可用的clickhouse包**
地址:https://packagist.org/explore/?query=clickhouse<br><br>
**ClickHouse在Laravel9框架的应用**
项目使用的是bavix/laravel-clickhouse
php: ^7.2|^8.0
laravel/framework: ^6.0|^7.0|^8.0|^9.0|^10.0
lumen无法使用
Join方式与laravel不同
例:
![](https://wvj4gqernlg.feishu.cn/space/api/box/stream/download/asynccode/?code=NTMzNjI2MjRmOTQ5NDU0NGRkMTNiNjEyMWUyMzRiNjlfR3h0WGlFbHFCaE04dUNqMXJHRkcxSE1vU0N5UlJ6NTVfVG9rZW46SDhVaWJZU0Zyb3JkWmZ4ZWozOWM3Z3gwbkVlXzE3MTQwMzYyOTA6MTcxNDAzOTg5MF9WNA)
连接方式
![](https://wvj4gqernlg.feishu.cn/space/api/box/stream/download/asynccode/?code=NTFlZjBmNzg1ZDdlYTY5YTJlYmIzNjY1NTAwODNhMGFfZ3NidE9nbUN6d0hqYWxxNVgzUGdiTkpZenBiY2NJZ0hfVG9rZW46QUgzZmJUM1R1bzR2WFB4Wkk4TWNUeXJXblFiXzE3MTQwMzYzMDM6MTcxNDAzOTkwM19WNA)
![](https://wvj4gqernlg.feishu.cn/space/api/box/stream/download/asynccode/?code=Yjg0OTNiMzUwYTlhM2ZkNmRhOGUzNGFhMmM3MGE5OTJfcldRdHQ3SDQ3ZGdPNW1EWkVGeVJsaGVtenhxYlNDSHdfVG9rZW46RGI0cmJEeWZibzV6cTh4dGVDRmNtRmpkblpjXzE3MTQwMzYzMDM6MTcxNDAzOTkwM19WNA)
查询方式如下
![](https://wvj4gqernlg.feishu.cn/space/api/box/stream/download/asynccode/?code=OWJhZGY3NDVkNjQ5MzU1Y2JhOGM5YTk5MjM0OGY5NjBfdk5VYzJWdUlzRzVnZ0xZTE9vM0hqcmk1bVZKM1NnTE5fVG9rZW46UXViUGI4c2pLbzYzZFZ4RVBBSGM2Q2l4blRkXzE3MTQwMzYzNDI6MTcxNDAzOTk0Ml9WNA)
目前遇到的与mysql写法不一致的地方
mysql写法
![](https://wvj4gqernlg.feishu.cn/space/api/box/stream/download/asynccode/?code=MjVjZDk5YjJiYTI3OGVhZmJmNTAyMmIxMzA3ZTE3NWVfY1g1Q0l1UlV5MHo2ZHBBUEU0cFN0b2hBYnU4c3lLRmVfVG9rZW46UzhRdWJodVRVb1N5YUd4anpuN2NYejdrbjRlXzE3MTQwMzYzNDI6MTcxNDAzOTk0Ml9WNA)
clickhouse对一个字段的处理需要分两次,否则会报错
![](https://img.kancloud.cn/5a/7a/5a7adb57b181a02e58ea99dda2012971_1280x911.png)
相同数据mysql与clickhouse查询耗时对比,本地环境,本地数据库,数据相同,全表扫
```
SELECT
link_book_id,
COUNT(DISTINCT CASE WHEN link_amount > 0 THEN user_id END ) as pay_uv,
COUNT(DISTINCT CASE WHEN read_chapter_num > 0 THEN user_id END ) as read_uv,
COUNT(DISTINCT CASE WHEN is_into_read =1 THEN user_id END ) as into_read_uv,
COUNT(DISTINCT CASE WHEN is_paywall =1 THEN user_id END ) as paywall_uv
FROM fq_link_user_behavior_records
group by link_book_id
```
**clickhouse的同步**
1:手动脚本同步,因为clickhouse的主键与mysql完全不同且不存在自增,所以脚本同步的过程中需要将mysql的主键id同步,根据主键之间的差异进行数据同步
2:binlog同步,目前使用的是Bifrox,文档地址https://www.xbifrost.com/
自动建表的话数据库会新增
**`bifrost_data_version`** **Int64**,
**`binlog_event_type`** **String**
字段,且insert,update,delete都会数据库新增一条记录,自动创建的表引擎是ReplacingMergeTree,属于MergeTree引擎
自动建表同样需要加这两个字段,增删改会对原数据进行操作,不会出现多余数据,该字段用于异步删除
3:使用mysql引擎,建表时需要在数据库上写入mysql的连接方式并将引擎修改为mysql,不推荐
4:csv导入,navicate导出的csv文件日期格式为YYYY/MM/DD,导入clickhouse的DateTime类型时格式会出错
Laravel脚本同步流程:
1:composer.json使用的包:bavix/laravel-clickhouse
![](https://img.kancloud.cn/cb/9e/cb9e9e1319b3ece3b120c22c11e75f8c_1227x792.png)
2:以下为同步代码片段
```
<?php
namespace App\Console\Commands\Clickhouse;
use App\Models\Clickhouse\ClickHouseRechargeMoneyAndNumsStatistic;
use App\Models\Recharge\RechargeMoneyAndNumsStatistic;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class SyncClickhouseRechargeMoneyNumsCommand extends Command
{
/**
* The name and signature of the console command.
* php artisan SyncClickhouseRechargeMoneyNumsCommand
* @var string
*/
protected $signature = 'SyncClickhouseRechargeMoneyNumsCommand';
/**
* The console command description.
*
* @var string
*/
protected $description = '同步fq_user_recharge_money_and_nums表到clickhouse';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
echo "开始执行同步\n";
$this->syncData();
echo "完成同步\n";
}
public function syncData()
{
// 同步fq_user_recharge_money_and_nums表到clickhouse
$maxMoneyAndNumsId = 0;
if (empty($isAll)) {
// 查询最大id
$maxMoneyAndNumsId = ClickHouseRechargeMoneyAndNumsStatistic::query()->select("id")->orderByDesc("id")->first()->id ?? 0;
}
RechargeMoneyAndNumsStatistic::query()
->when(!empty($maxLinkUserBehaviorId), function ($q) use ($maxMoneyAndNumsId) {
$q->where("id", ">", $maxMoneyAndNumsId);
})->chunkById(1000, function ($result) {
$connection = DB::connection('bavix::clickhouse')->table('fq_user_recharge_money_and_nums');
$connection->getConnection()->unsetEventDispatcher();
$connection->insert($result->toArray());
});
}
}
```
![](https://img.kancloud.cn/03/a0/03a09c4f8609b1114fab60361d1c6430_1717x978.png)
同步代码片段2:
```
<?php
namespace App\Console\Commands\Clickhouse;
use App\Models\Book;
use App\Models\Chapter;
use App\Models\ChapterRackDistinct;
use App\Models\Clickhouse\ClickhouseBook;
use App\Models\Clickhouse\ClickhouseChapterRackDistinct;
use App\Models\Clickhouse\ClickhouseChapters;
use App\Models\Clickhouse\ClickhouseExpenses;
use App\Models\Clickhouse\ClickhouseOrder;
use App\Models\Clickhouse\ClickhouseUser;
use App\Models\Clickhouse\ClickhouseUserBookReadTime;
use App\Models\Clickhouse\ClickhouseUserSearchLog;
use App\Models\Clickhouse\ClickhouseUserWelfares;
use App\Models\Expense;
use App\Models\Orders;
use App\Models\User\Users;
use App\Models\User\UserWelfare;
use App\Models\User\UserWelfareConfig;
use App\Models\UserBookReadTime;
use App\Models\UserSearchLog;
use Illuminate\Console\Command;
use App\Models\Clickhouse\ClickhousePageVisit;
use App\Models\PageVisit;
use Illuminate\Support\Facades\DB;
use Tinderbox\Clickhouse\Server as TinderboxServer;
use Tinderbox\Clickhouse\ServerProvider;
class SyncClickhousePageVisitCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'SyncClickhousePageVisitCommand';
/**
* The console command description.
*
* @var string
*/
protected $description = '同步page_visit表到clickhouse';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$this->syncData();
}
public function syncData()
{
$date = date('Ymd',time());
// 同步book表数据
// 查询page_visit当日最后一条数据id
$bookId = Book::query()->where("created_at","<=",date('Y-m-d 00:00:00',time()))->orderByDesc("id")->first()->id ?? 0;
$clickhouseBookId = ClickhouseBook::query()->select("id")->orderByDesc("id")->first()->id ?? 0;
$bookConnection = DB::connection('bavix::clickhouse')->table('fq_books');
Book::query()
->where("id",">",$clickhouseBookId)
->where("id","<=",$bookId)
->chunkById(1000,function ($result) use ($bookConnection) {
$bookConnection->getConnection()->unsetEventDispatcher();
$bookConnection->insert($result->toArray());
});
// user_welf_config需先截断表
$table = 'fq_user_welfare_configs';
// 连接到 ClickHouse 数据库
$server = new TinderboxServer(
env("CLICKHOUSE_DB_HOST"),
env("CLICKHOUSE_DB_PORT"),
env("CLICKHOUSE_DB_DATABASE"),
env("CLICKHOUSE_DB_USERNAME"),
env("CLICKHOUSE_DB_PASSWORD"));
$serverProvider = (new ServerProvider())->addServer($server);
$client = new \Tinderbox\Clickhouse\Client($serverProvider);
$client->writeOne("TRUNCATE TABLE $table");
$welfareConfigConnection = DB::connection('bavix::clickhouse')->table('fq_user_welfare_configs');
UserWelfareConfig::query()
->chunkById(1000,function ($result) use ($welfareConfigConnection) {
$welfareConfigConnection->getConnection()->unsetEventDispatcher();
$welfareConfigConnection->insert($result->toArray());
});
}
}
```
ClickHouse使用的模型代码片段如下:
```
<?php
namespace App\Models\Clickhouse;
use Bavix\LaravelClickHouse\Database\Eloquent\Model;
/**
* Class ClickHouseRechargeMoneyAndNumsStatistic
* @package App\Models\Clickhouse
*/
class ClickHouseRechargeMoneyAndNumsStatistic extends Model
{
protected $table = 'fq_user_recharge_money_and_nums';
protected $guarded = ['id'];
}
```