项目背景:需要完成一个广告系统提供给媒体方和广告主去投放并展示广告;

目的:与项目背景是相同的。其实只要搞清楚了广告系统能够实现的功能,也就知道了背景和目的;

选择微服务开发框架:降低服务功能之间的耦合、开发人员分配也更加简单等等;

设计思路:首先肯定是数据表怎样去设计、为什么这样设计,要说清楚分层的表结构,每张表存储什么样的数据。之后是投放系统,就是实现对表数据的增删改查;最后是检索系统,将数据表中的数据加载到系统中,构造倒排索引,实现高效检索服务。
image.png

广告系统在设计中我们需要考虑的几件事情:

  • 怎样定义投放系统的数据结构?
  • 怎样高效的处理广告请求?

本系统包含四个子系统:广告计费系统、广告投放系统、报表系统、广告检索系统。

广告系统主要知识点:

  • SpringCloud
  • 广告检索
  • 微服务架构
    • SpringCloud
      • Eureka服务治理
        • 实现服务注册与发现
      • Zuul网关
        • 实现路由转发与请求信息记录(自定义过滤器)
      • Feign
    • kafka消息队列组件
  • 解析MySQL Binlog

广告系统应该需要实现的最基本的功能

  • 广告投放系统 -> 既然是广告系统,一定得有广告数据,数据当然是由广 告主或代理商投放,那么,也就需要有个投放广告的平台,这就是广告 投放系统
  • 广告检索系统 -> 媒体方对广告系统发起请求,广告系统能够检索符合要 求的广告数据,这就是广告检索系统的核心功能

    完整的广告系统又需要包含哪些子系统

  • 曝光监测系统 -> 监测广告数据的曝光记录

  • 报表系统 -> 构建广告数据报表,比如广告A在地域B中一共曝光了多少 次,主要是 OLAP 的过程
  • 扣费系统 -> 广告的每一次曝光都是需要扣费的,且这个系统里面负责了 将广告数据置位的功能

image.png
image.pngimage.pngimage.png
image.png

编辑hosts文件

  1. vim /etc/hosts

CAP 定理的含义

网关做权限鉴定的分析

  1. ⾸先,⽤户与权限(⽤户与权限相关的肯定是在⼀起中,只有有了⽤户才会有权限,⽽权限⼜是
    应⽤于⽤户的)应该是在⼀个单独的模块中来实现,或者说它是⼀个独⽴的微服务。
    2. ⽹关是系统的统⼀⼊⼝,所以也是权限校验最合适的地⽅。如果没有相应的权限,不需要向
    下继续下发给对应的微服务了。其他的微服务也是⼀样,当需要⽤户权限信息的时候,直接调⽤独
    ⽴的微服务即可。

image.pngimage.png

image.pngimage.png

广告投放数据的核心要素

  • 用户账户 -> 最高层级,用于定义广告主或代理商,只有有了用户才会有接下来的数据投放
  • 推广计划 -> 一类品牌或产品广告投放的规划,自身并不定义太多关于广告自身的信息,它会将信息打包下放到推广单元层级
  • 推广单元 -> 一个确定的广告投放策略,描述了投放广告的规则信息
  • 推广单元维度限制 -> 广告投放会有一些限制条件,例如只投放到北京、上海地区,对一些关键字进行投放等等 (这可能是比较难理解的概念,需要大家好好的思考)
  • 广告创意 -> 展示给用户看到的数据,可以是图片、文本或者一段视频

image.png

image.png

索引

正排索引

按照自增键或唯一键生成与对象的映射关系。

倒排索引

也被称作是反向索引,是一种索引方法,在文档检索系统中最为常用的数据结构。为了存储在全文搜索下的某个单词在一个文档或一组文档中存储位置的映射。
image.png

全量索引:

检索系统在启动时一次性读取当前数据库中(注意,不能直接从数据库中直接读取?)的所有数据,建立索引。

增量索引:

系统运行过程中,监控数据库变化,即增量,实时加载更新,构建索引。

启动检索系统加载全量索引
adplan.data
检索系统运行过程中的增量索引
正向索引:id->object
ad_unit.data
ad.plan:insert,update
增删改操作
delete
ad_creative.data
检索系统
ad_creative_unit.data
adunitdistrict:insert
delete
ad_unitdistrict.data
增删操作
倒排索引
adunitit.data
ad_unitkeyword.data
某一个pos之前的数据全
部导出到文件中,从文件中
加载全量索引
image.png

广告数据索引的设计

设计索引的目的就是为了加快检索的速度,将原始数据抽象,规划出合理的字段,在内存中构建广告数据索引。记住,并不是所有的数据都需要放在索引里。

正向索引

核心思想是通过一个键找到一个对象,且这种关系是确定的,即唯一键对应到唯一的对象。

  • 推广计划
  • 推广单元
  • 创意

    倒排索引

    核心思想是通过内容去确定包含关系的对象。

  • 创意与推广单元的关联对象

  • 推广单元地域限制
  • 推广单元兴趣限制
  • 推广单元关键词限制

    广告数据索引的维护

    核心思想是保证检索服务中的索引是完整的

  • 全量索引

  • 增量索引

在内存(其实就是在你的 JVM 中)中保存索引首先需要考虑的是你的 JVM 内存能开多大,如果你机器的内存足够大,那么 JVM 的内存给多一些更好(建议不要低于 4G【注意最好限制在32GB以下,使用多机部署】)。JVM 的内存确定之后,再去考虑你当前的数据量,这个主要是靠预估,估计你的一个 Java Object 会占据多少字节。看一看当前的 JVM 是否可以存的下。这个需要根据具体的业务来做选择。但是,对于广告系统这种项目来说,可以肯定的是,你的广告数据量不会很大(这类数据比较特殊,本来就没有多少广告主,哪来的巨量数据),所以,一般数据量都在 MB 级别。完全可以存储在 JVM 中。

索引数据的存储与操作使用的是ConcurrentHashMap、ConcurrentSkipListSet,能否使用 HashMap、HashSet 替换呢 ?为什么 ?

不能, 用HashMap和HashSet在高并发的情况下会有线程安全问题,相比之下ConcurrentHashMap和ConcurrentSkipListSet是线程安全的类
更新索引的过程依赖于监听 Binlog 的库,而这种库是可以随意更换的。且更新 Binlog 的过程很多都是异步和多线程的,所以,使用 ConcurrentHashMap 利于将来的扩展、修改与维护。

DataTable.java 的功能是什么 ?

主要是在使用bean的时候,不需要繁琐的注入,正常情况下,都需要Autowired或者Resource注入。如果使用到的bean太多的话,也是个苦力活。所以说DataTable.java通过获取spring上下文,里面的of方法实现了往dataTableMap中存放相应的bean,再加上条件判断也不需要频繁的getBean(),这样方便我们使用,只需要注入dataTable,调用of方法就可以取到相应的bean.

加载全量索引的设计

其实就是对三个方面进行思考、设计与实现

  • 全量数据从哪来 ?
  • 怎样规划或定义数据之间的依赖关系 ?
  • 什么时候实现加载 ?又要怎样做呢 ?

**

加载全量索引时,为什么要将数据库表中的数据导出到文件中,而不是直接从数据库中读取 ?

多机部署时,多个服务同时读写数据库并各自建立索引在网络和计算上损耗较大,不如使用读取文件的方式减轻数据库以及网络的压力。

Binlog日志

二进制日志,记录对数据发生或潜在发生更改的sql语句,并以二进制的形式保存在磁盘中。

两个主要的作用:复制、恢复和审计。

MySQL 的 Binlog 是基于 MySQL 的 Master/Slave 协议,主从之间的数据同步就是依赖于 Binlog。而我们的 ad-search 之所以能实现监听 Binlog,则是把自己 “伪装”为 MySQL Slave。这是通过 mysql-binlog-connector-java 这个开源工具实现的。
Binlog 其实就是 MySQL 记录你对数据表的改动操作,然后记录下来形成的二进制文件。 mysql-binlog-connector-java 就是去监听这些二进制文件的变化。这个监听与被监听的实现则是 MySQL 与 mysql-binlog-connector-java 工具都需要去做的(MySQL 定好协议标准,mysql-binlog-connector-java 工具按照协议来实现对应的接口)。

image.png

格式类型 格式特征 应用
ROW 仅保存记录被修改的细节,不记录sql语句上下文相关信息 较为常用
STTATEMENT 每一条会修改数据的sql都会记录在Binlog中
MIXED 以上两种的混合使用

管理binlog相关的sql语句
image.png

image.png

image.png

MySQL Binlog

Binlog 的相关概念

什么是 Binlog

Binlog 是 MySQL Server 维护的一种二进制日志,主要是用来记录对 MySQL 数据更新或潜在发生更新的 SQL 语句,并以”事务”的形式保存在磁盘中(文件)

主要用途

  • 1. 复制:MySQL 的 Master-Slave 协议,让 Slave 可以通过监听 Binlog 实现数据复制,达到数据一致的目的
  • 2. 数据恢复:通过 mysqlbinlog 工具恢复数据
  • 3. 增量备份

    支持的格式

  • ROW

仅保存记录被修改细节,不记录 SQL 语句上下文相关信息:能非常清晰的记录下每行数据的修改细节,不需要记录上下文相关信息,因此不会发生某些特定情况下的 procedure、function、及 trigger 的调用触发无法被正确复制的问题,任何情况都可以被复制,且能加快从库重放日志的效率,保证从库数据的一致性。

  • STATEMENT

每一条会修改数据的 SQL 都会记录在 Binlog 中:只需要记录执行语句的细节和上下文环境,避免了记录每一行的变化,在一些修改记录较多的情况下相比 ROW 类型能大大减少 Binlog 日志量,节约IO,提高性能;还可以用于实时的还原;同时主从版本可以不一样,从服务器版本可以比主服务器版本高。

  • MIXED

以上两种类型的混合使用。经过前面的对比,可以发现 ROW 类型和 STATEMENT 类型各有优势,如能根据 SQL 语句取舍可能会有更好地性能和效果;MIXED 便是以上两种类型的结合。
**

开启binlog

查找配置文件响应位置

  1. mysql --help --verbose | grep my.cnf
  1. order of preference, my.cnf, $MYSQL_TCP_PORT,

/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf

修改或创建 [~/.my.cnf]

(新建[mysqld]节点)在[mysqld]节点后填入以下内容。

  1. [mysqld]
  2. # log_bin
  3. #开启binlog
  4. log-bin = mysql-bin
  5. #选择row模式
  6. binlog-format = ROW
  7. #配置mysql replication需要定义,不能和canal的slaveId重复
  8. server_id = 1
  9. expire-logs-days = 14
  10. max-binlog-size = 500M

此时重启mysql,并检查开启状态。

Binlog 的相关命令

相关变量

  1. -- Binlog 开关变量
  2. mysql> show variables like 'log_bin';
  3. +---------------+-------+
  4. | Variable_name | Value |
  5. +---------------+-------+
  6. | log_bin | ON |
  7. +---------------+-------+
  8. 1 row in set (0.30 sec)
  9. -- Binlog 日志的格式
  10. mysql> show variables like 'binlog_format';
  11. +---------------+-------+
  12. | Variable_name | Value |
  13. +---------------+-------+
  14. | binlog_format | ROW |
  15. +---------------+-------+
  16. 1 row in set (0.00 sec)

常用操作命令

SQL语句 语句含义
show master logs; 查看所有 Binlog 的日志列表
show master status; 查看最后一个 Binlog 日志的编号名称,及最后一个事件结束的位置(pos)
flush logs; 刷新 Binlog,此刻开始产生一个新编号的 Binlog 日志文件
reset master; 清空所有的 Binlog 日志
show binlog events; 查看第一个 Binlog 日志
show binlog events in ‘binlog.000030’; 查看指定的 Binlog 日志
show binlog events in ‘binlog.000030’ from 931; 从指定的位置开始,查看指定的 Binlog 日志
show binlog events in ‘binlog.000030’ from 931 limit 2; 从指定的位置开始,查看指定的 Binlog 日志,限制查询的条数
show binlog events in ‘binlog.000030’ from 931 limit 1, 2; 从指定的位置开始,带有偏移,查看指定的 Binlog 日志,限制查询的条数

Binlog 的 Event 类型

MySQL Binlog Event 类型有很多种(MySQL 官方定义了 36 种),例如:XID、TABLE_MAP、QUERY 等等。但是,我们需要了解的(也是最常用的)类型不是很多。

Event Type 事件 重要程度
QUERY_EVENT 与数据无关的操作, begin、drop table、truncate table 等 了解即可
XID_EVENT 标记事务提交 了解即可
TABLE_MAP_EVENT 记录下一个操作所对应的表信息,存储了数据库名和表名 非常重要
WRITE_ROWS_EVENT 插入数据,即 insert 操作 非常重要
UPDATE_ROWS_EVENT 更新数据,即 update 操作 非常重要
DELETE_ROWS_EVENT 删除数据,即 delete 操作 非常重要

最好的方式是在数据库中建一张测试表,然后对表进行增删改操作,再去查看下 Binlog 里面都有些什么,如下图所示
基于Spring Cloud微服务架构 广告系统设计与实现 - 图19

说明

对于 MySQL Binlog,我们可以不用过分追究 Binlog 里面到底包含了些什么,对于应用的话,我们最重要要搞清楚 Binlog 的 Event:每个 Event 包含 header 和 data 两个部分;header 提供了 Event 的创建时间,哪个服务器等信息,data 部分提供的是针对该 Event 的具体信息,如具体数据的修改。我们对 Binlog 的解析,即为对 Event 的解析。

  • Binlog 的 EventType (需要注意,不同版本的 MySQL,EventType 可能会不同)
  • Binlog 中并不会打印数据表的列名

    TABLE_MAP_EVENT 事件存在的意义

广告检索服务功能架构

image.png

kafka消息系统

image.png

image.png

Topics 主题
partition 数据偏移量
producer 生产者
consumer 消费者

image.png
image.png
image.png
image.png

消费消息的方式

①自动提交位移
②手动提交当前位移
③手动异步提交当前位移
④手动异步提交当前位移带回调
⑤混合同步与异步提交位移