введение
Статистический доход от заказов — обычная проблема для приложений электронной коммерции.日报/月报/年报
, Эти отчетные данные создают множество проблем при разработке таблиц и программ.聚合查询
Время запроса отчета будет постепенно увеличиваться по мере того, как данные отчета о прибылях и убытках становятся все больше и больше. В это время необходимо подумать о том, как разработать отчет о прибылях и убытках для более эффективного запроса? Какой дизайн может сделать статистический доход просто?
необходимость
визуализация
конкретное требование
- Типы доходов делятся на: доход от заказа на самостоятельную покупку, доход от заказа на совместное использование, доход от распределения, доход от деятельности.
- Статистика доходов за текущий день, доходов за текущий месяц
- В зависимости от времени просмотра рассчитывается доход за период времени.
считать
Идеи дизайна
Таблица заказов обязательно нужна.При написании или изменении таблицы заказов отчет о прибылях и убытках будет писаться и изменяться синхронно.В таблице заказов будут записаны только заказы на самостоятельную покупку и совместное использование, а также будут записаны только доходы от раздачи и подарков. в отчете о прибылях и убытках в специальном бизнесе. Затем используйте день в качестве измерения для создания пользователя收益日报表
.Однострочная запись записывается в доход пользователя за день.降低
Запросить объем данных ежедневной/ежемесячной/годовой статистики дохода пользователя.В качестве примера для одного пользователя разделение пользователя на месяц будет генерировать только наибольшую31
Это относится к контролируемому темпу роста.Если вы продолжите использовать отчет о прибылях и убытках, поскольку объем данных в отчете о прибылях и убытках соответствует количеству заказов, размещенных пользователями, если количество заказов, размещенных пользователями, велико, таблица будет быть очень большим. , этот метод можно использовать, чтобы избежать статистики большого объема данных, а позже, если количество пользователей увеличится, что приведет к большему количеству данных ежедневного отчета, вы можете рассмотреть подтаблицы.
видимая проблема
- Время синхронизации отчета о ежедневном доходе является проблемой.Поскольку операция исходного заказа очень сложна, необходимо синхронно записывать доход и вычислять данные о ежедневном доходе, а связывание кода слишком велико.Есть ли какой-либо способ генерировать ежедневный отчет о доходах через отчет о прибылях и убытках?
- Несмотря на то, что доход записывается в ежедневный отчет, может потребоваться несколько раз запрашивать оператор SQL, чтобы добиться эффекта, требуемого визуализацией.Есть ли способ агрегировать как можно меньше SQL, не влияя на эффективность программы?
выполнить
Подытожим приведенные выше вопросы. Я начал сбор данных. Наконец, принялcanal
+RocketMQ
как гетерогенный раствор.
стек технологий
Кратко представим эти две технические структуры:
- канал: основная цель — анализировать добавочные журналы на основе базы данных MySQL, обеспечивая подписку и потребление добавочных данных.
- RocketMQ: Распределенная система обмена сообщениями с открытым исходным кодом, основанная на технологии распределенного кластера высокой доступности, обеспечивает публикацию сообщений и услуги подписки с низкой задержкой и высокой надежностью.
注:我用的aliyun的全家桶,MQ和mysql都是阿里云的,如果是自建服务器的可能有区别,我在后面尽量标出
Процесс схемы
- Контролируйте отчет о прибылях и убытках mysql через канал при написании или изменении отчета о прибылях и убытках.бинлог журнал.
- Canal обнаруживает изменение, собирает измененное сообщение JSON и отправляет TOPIC, заранее определенный в RocketMQ.
- Программа потребляет ТЕМУ, ежедневный отчет о гетерогенном доходе.
раздел конфигурации канала
Пожалуйста, обратитесь к установке каналаофициальная документацияПосле распаковки можно получить папку canal, которая содержит три директории
- bin: хранить сценарий перезапуска при запуске
conf:存放核心配置文件
- lib: хранить основной пакет jar
Нам нужно сосредоточиться на основном файле конфигурации conf/canal.properties в папке conf и файле конфигурации отдельного узла мониторинга conf/example/instance.properties.
conf/canal.properties
# tcp, kafka, RocketMQ,这里默认是tcp读取模式,采用RocketMQ需要将其改变为RocketMQ模式
canal.serverMode = RocketMQ
# 如果是aliyun的RocketMQ需要配置以下两个KEY,ak/sk
canal.aliyun.accessKey =xxxxxxx
canal.aliyun.secretKey =xxxxxxx
# 监控的节点名称.这个默认就是example如果有多节点可以逗号隔开,如下方的例子
canal.destinations = example,sign
# 如果是aliyun的RocketMQ需要修改canal.mq.accessChannel为cloud默认为local
canal.mq.accessChannel = cloud
#MQ的地址,需要注意这里是不带http://,但是需要带端口号
canal.mq.servers =
#rocketmq实例id
canal.mq.namespace =
conf/example/instance.properties
#mysql地址
canal.instance.master.address=
#以下两个参数需要在开启数据库binlog日志后得到,在数据库查询界面输入查询语句`show master status`,canal.instance.master.journal.name对应File参数,canal.instance.master.position对应Position参数
canal.instance.master.journal.name=
canal.instance.master.position=
#数据库的账号密码
canal.instance.dbUsername=
canal.instance.dbPassword=
#需要监控变动的表
canal.instance.filter.regex=xxx.t_user_order,xxx.t_user_cash_out
#定义发送的mq生产组
canal.mq.producerGroup =
#定义发送到mq的指定主题
canal.mq.topic=
Примечание. Формат правил написания таблицы мониторинга см.Правила написания таблицы мониторинга
запускать
cd /canal/bin
./start.sh
В это время вы обнаружите, что в каталоге канала есть дополнительный файл журнала, и вы можете ввести основной файл журнала канала и пример журнала запуска узла.
canal日志中出现
the canal server is running now ......
example日志中出现
init table filter : ^tablename
xxxxxxxxx , the next step is binlog dump
Указывает, что вы преуспели в большом шаге, и мониторинг канала работает нормально.
Часть RocketMQ
Если вы используете RocketMQ от Aliyun, часть кода конфигурации может быть напрямуюСправочная документацияСамостоятельно созданный RocketMQ также может ссылаться на простой пример потребления для мониторинга соответствующей ТЕМЫ. Для использования данных, отправленных Canal, используется следующий формат:
{
"data":[
{
//单个修改后表数据,如果同一时间有多个表变动会有多个该JSON对象 }
],
"database":"监控的表所在数据库",
"es":表变动时间,
"id":canal生成的id,
"isDdl":Boolean类型,表示是否DDL语句,
"mysqlType":{
表结构
},
"old":如果是修改类型会填充修改前的值,
"pkNames":[
该表的主键,如"id"
],
"sql":"执行的SQL",
"sqlType":{
字段对应的sqlType,一般使用mysqlType即可
},
"table":"监控的表名",
"ts":canal记录发送时间,
"type":"表的修改类型,入INSERT,UPDATE,DELETE"
}
Код потребления MQ в основном использует отражение для сопоставления с соответствующей таблицей.
//这里的body就是Canal发来的数据
public Action process(String body) {
boolean result = Boolean.FALSE;
JSONObject data = JSONObject.parseObject(body);
log.info("数据库操作日志记录:data:{}",data.toString());
Class c = null;
try {
//这里监控了订单和收益表分别做订单统计和收益日报统计
c = Class.forName(getClassName(data.getString("table")));
} catch (ClassNotFoundException e) {
log.error("error {}",e);
}
if (null != c) {
JSONArray dataArray = data.getJSONArray("data");
if (dataArray != null) {
//把获取到的data部分转换为反射后的实体集合
List list = dataArray.toJavaList(c);
if (CollUtil.isNotEmpty(list)) {
//对修改和写入操作分别进行逻辑操作
String type = data.getString("type");
if ("UPDATE".equals(type)) {
result = uppHistory(list);
} else if ("INSERT".equals(type)) {
result = saveHistory(list);
}
}
}
}
return result ? Action.CommitMessage : Action.ReconsumeLater;
}
/**
* @description: 获取反射ClassName
* @author: chenyunxuan
*/
private String getClassName(String tableName) {
StringBuilder sb = new StringBuilder();
//判断是哪张表的数据
if (tableName.equals("t_user_income_detail")) {
sb.append("cn.mc.core.model.order");
} else if (tableName.equals("t_user_cash_out")) {
sb.append("cn.mc.sync.model");
}
String className = StrUtil.toCamelCase(tableName).substring(1);
return sb.append(".").append(className).toString();
}
/**
* @description: 写入对应类型的统计表
* @author: chenyunxuan
*/
private <T> Boolean saveHistory(List<T> orderList) {
boolean result = Boolean.FALSE;
Object dataType = orderList.get(0);
//用instanceof判断类型进入不同的逻辑处理代码
if (dataType instanceof TUserIncomeDetail) {
result = userOrderHistoryService.saveIncomeDaily(orderList);
} else if (dataType instanceof UserCashOut) {
result = userCashOutHistoryService.delSaveHistoryList(orderList);
}
return result;
}
Псевдокод saveIncomeDaily
public synchronized Boolean saveIncomeDaily(List orderList) {
//循环收益明细记录
.......
//通过创建时间和用户id查询收益日报表中是否有当日数据
if(不存在当日数据){
//创建当日的收益日报表记录
.....
}
//因为不存在当日记录也会立即写入当日的空数据,所以下面的流程都是走更新流程
//更新当日数据
.......
return Boolean.TRUE;
}
注:代码中应该多打一些日志,方便产生异常收益数据后的校对
постскриптум
Пока на основеcanal
+RocketMQ
Ежедневная статистическая неоднородная схема дохода завершена, и следующая статья будет посвящена второй проблеме, упомянутой в этой статье, чтобы уменьшить генерацию агрегированного SQL.Обратите внимание.