6.1.?多租户模型-广告分析

6.1.1. 准备表和数据
6.1.2. 简单的应用
6.1.3. 租户共享数据
6.1.4. 租户不共享数据

该模型记录多个公司(companies),每个公司都有广告系列(campaigns),每个活动里有多个广告(ads),每个广告都有相关的点击量(clicks)和显示次数(impressions)。

CREATE TABLE companies (
  id bigserial PRIMARY KEY,
  name text NOT NULL,
  image_url text,
  created_at timestamp without time zone NOT NULL,
  updated_at timestamp without time zone NOT NULL
);

CREATE TABLE campaigns (
  id bigserial PRIMARY KEY,
  company_id bigint REFERENCES companies (id),
  name text NOT NULL,
  cost_model text NOT NULL,
  state text NOT NULL,
  monthly_budget bigint,
  blacklisted_site_urls text[],
  created_at timestamp without time zone NOT NULL,
  updated_at timestamp without time zone NOT NULL
);

CREATE TABLE ads (
  id bigserial PRIMARY KEY,
  campaign_id bigint REFERENCES campaigns (id),
  name text NOT NULL,
  image_url text,
  target_url text,
  impressions_count bigint DEFAULT 0,
  clicks_count bigint DEFAULT 0,
  created_at timestamp without time zone NOT NULL,
  updated_at timestamp without time zone NOT NULL
);

CREATE TABLE clicks (
  id bigserial PRIMARY KEY,
  ad_id bigint REFERENCES ads (id),
  clicked_at timestamp without time zone NOT NULL,
  site_url text NOT NULL,
  cost_per_click_usd numeric(20,10),
  user_ip inet NOT NULL,
  user_data jsonb NOT NULL
);

CREATE TABLE impressions (
  id bigserial PRIMARY KEY,
  ad_id bigint REFERENCES ads (id),
  seen_at timestamp without time zone NOT NULL,
  site_url text NOT NULL,
  cost_per_impression_usd numeric(20,10),
  user_ip inet NOT NULL,
  user_data jsonb NOT NULL
);

澳门游戏平台注册网站可以对这个模型进行一些修改,让其在uxmpp中可以更高效的运行。

关系数据模型非常适合应用程序。它保护数据完整性,允许灵活的查询,并适应不断变化的数据。但是,关系数据库不能满足大型SaaS应用程序所需的工作负载。必须通过NoSQL数据库才能达到这个规模。

使用uxmpp,可以保留数据模型并使其扩展。uxmpp将应用程序看作是单个uxdb数据库,但它在内部将查询路由到可调数量的物理服务器(节点),这些服务器可以并行处理请求。

多租户应用程序有一个很好的属性:查询通常总是一次请求一个租户的信息,而不是租户的混合。例如,当销售人员在搜索潜在客户信息时,搜索结果特定于其客户,其他企业的潜在客户和信息不会包括在内。

由于应用程序查询仅限于单个租户(例如商店或公司),因此快速进行多租户应用程序查询的一种方法是将给定租户的所有数据存储在同一节点上。这可以最大限度地减少节点之间的网络开销,并允许uxmpp有效地支持所有应用程序的连接,关键约束和事务。有了这个,您可以跨多个节点进行扩展,而无需完全重写或重新构建应用程序。

澳门游戏平台注册网站需要确保每个表都有一列来清楚地标记哪个租户拥有哪些行,从而在uxmpp中执行此操作。在广告分析应用程序中,租户是公司,因此澳门游戏平台注册网站必须确保所有表都有company_id列。

当行标记为同一公司时,澳门游戏平台注册网站可以使用此列将对应的行读取和写入行到同一节点。

6.1.1.?准备表和数据

澳门游戏平台注册网站为多租户应用程序确定了正确的分发列:company_id。

澳门游戏平台注册网站创建的模式使用单独的id列作为每个表的主键。uxmpp要求主键和外键约束包括分发列。这里澳门游戏平台注册网站可以通过包含company_id来生成主键和外键组合。这与多租户案例兼容,因为澳门游戏平台注册网站真正需要的是确保每个租户的唯一性。

接下来对这些表进行改造:

CREATE TABLE companies (
  id bigserial PRIMARY KEY,
  name text NOT NULL,
  image_url text,
  created_at timestamp without time zone NOT NULL,
  updated_at timestamp without time zone NOT NULL
);

CREATE TABLE campaigns (
  id bigserial,       --  PRIMARY KEY
  company_id bigint REFERENCES companies (id),
  name text NOT NULL,
  cost_model text NOT NULL,
  state text NOT NULL,
  monthly_budget bigint,
  blacklisted_site_urls text[],
  created_at timestamp without time zone NOT NULL,
  updated_at timestamp without time zone NOT NULL,
  PRIMARY KEY (company_id, id) -- added
);

CREATE TABLE ads (
  id bigserial,       -- PRIMARY KEY
  company_id bigint,  -- added
  campaign_id bigint, -- REFERENCES campaigns (id)
  name text NOT NULL,
  image_url text,
  target_url text,
  impressions_count bigint DEFAULT 0,
  clicks_count bigint DEFAULT 0,
  created_at timestamp without time zone NOT NULL,
  updated_at timestamp without time zone NOT NULL,
  PRIMARY KEY (company_id, id),         -- added
  FOREIGN KEY (company_id, campaign_id) -- added
    REFERENCES campaigns (company_id, id)
);

CREATE TABLE clicks (
  id bigserial,        -- was: PRIMARY KEY
  company_id bigint,   -- added
  ad_id bigint,        -- was: REFERENCES ads (id),
  clicked_at timestamp without time zone NOT NULL,
  site_url text NOT NULL,
  cost_per_click_usd numeric(20,10),
  user_ip inet NOT NULL,
  user_data jsonb NOT NULL,
  PRIMARY KEY (company_id, id),      -- added
  FOREIGN KEY (company_id, ad_id)    -- added
    REFERENCES ads (company_id, id)
);

CREATE TABLE impressions (
  id bigserial,         -- PRIMARY KEY
  company_id bigint,    -- added
  ad_id bigint,         -- REFERENCES ads (id),
  seen_at timestamp without time zone NOT NULL,
  site_url text NOT NULL,
  cost_per_impression_usd numeric(20,10),
  user_ip inet NOT NULL,
  user_data jsonb NOT NULL,
  PRIMARY KEY (company_id, id),       -- added
  FOREIGN KEY (company_id, ad_id)     -- added
    REFERENCES ads (company_id, id)
);

--分发表
SELECT create_distributed_table('companies',   'id');
SELECT create_distributed_table('campaigns',   'company_id');
SELECT create_distributed_table('ads',         'company_id');
SELECT create_distributed_table('clicks',      'company_id');
SELECT create_distributed_table('impressions', 'company_id');

--使用COPY命令进行批量加载数据(COPY时指定正确的数据路径)
\copy companies from 'companies.csv' with csv
\copy campaigns from 'campaigns.csv' with csv
\copy ads from 'ads.csv' with csv
\copy clicks from 'clicks.csv' with csv
\copy impressions from 'impressions.csv' with csv

6.1.2.?简单的应用

对单租户进行一个简单的查询和更新。

-- 某公司预算最高的广告系列
SELECT name, cost_model, state, monthly_budget
  FROM campaigns
 WHERE company_id = 5
 ORDER BY monthly_budget DESC
 LIMIT 10;

-- 将其预算翻倍
UPDATE campaigns
   SET monthly_budget = monthly_budget*2
 WHERE company_id = 5;

对于使用NoSQL数据库扩展应用程序的用户来说,一个常见的问题是缺少事务和连接。然而,在uxmpp中,事务可以正常的使用:

-- 更改广告系列的预算资金
BEGIN;
UPDATE campaigns
   SET monthly_budget = monthly_budget + 1000
 WHERE company_id = 5
   AND id = 40;

UPDATE campaigns
   SET monthly_budget = monthly_budget - 1000
 WHERE company_id = 5
   AND id = 41;
COMMIT;

澳门游戏平台注册网站再来演示一个包含聚合和窗口函数的查询,它在uxmpp中的工作方式与在uxdb中的工作方式相同。根据显示次数(impressions)对每个广告系列中的广告进行排名。

SELECT a.campaign_id,
       RANK() OVER (
         PARTITION BY a.campaign_id
         ORDER BY a.campaign_id, count(*) desc
       ), count(*) as n_impressions, a.id
  FROM ads as a,
       impressions as i
 WHERE a.company_id = 5
   AND i.company_id = a.company_id
   AND i.ad_id      = a.id
GROUP BY a.campaign_id, a.id
ORDER BY a.campaign_id, n_impressions desc;

简而言之,当查询范围限定为租户时,插入,更新,删除,复杂SQL和事务都可以正常使用。

6.1.3.?租户共享数据

到目前为止,所有表都已经分发company_id,但有时候租户需要共享数据,并且特是“不属于”任何租户的数据。例如,使用此示例广告平台的所有公司可能希望根据IP地址获取其受众的地理信息。在单个机器数据库中,这可以通过geo-ip的查找表来完成,如下所示。(实际的表可能会使用GIS。)

CREATE TABLE geo_ips (
  addrs cidr NOT NULL PRIMARY KEY,
  latlon point NOT NULL
    CHECK (-90  <= latlon[0] AND latlon[0] <= 90 AND
           -180 <= latlon[1] AND latlon[1] <= 180)
);
CREATE INDEX ON geo_ips USING gist (addrs inet_ops);

要在分布式设置中有效地使用此表,澳门游戏平台注册网站需要找到一种方法来共同定位geo_ips表。这样,在查询时不需要产生网络流量。澳门游戏平台注册网站通过指定geo_ips作为参考表。

--为所有工人制作geo_ips的同步副本
SELECT create_reference_table('geo_ips');

参考表在所有工作节点上复制,uxmpp会自动保持同步。请注意,这里澳门游戏平台注册网站调用create_reference_table函数。

--加载数据
\copy geo_ips from 'geo_ips.csv' with csv
--查询。
SELECT c.id, clicked_at, latlon
  FROM geo_ips, clicks c
 WHERE addrs >> c.user_ip
   AND c.company_id = 5
   AND c.ad_id = 290;

6.1.4.?租户不共享数据

例如,使用澳门游戏平台注册网站的广告数据库的一个租户应用程序可能希望通过单击存储跟踪cookie信息,而另一个租户可能关心浏览器代理。传统上,对于多租户,使用共享模式方法的数据库只能创建固定数量的预先分配的“自定义”列,或者使用外部的“扩展表”。一种更简单非结构化列类型,JSONB格式。

请注意,表clicks已经调用了一个JSONB字段user_data。每个租户都可以使用它进行灵活存储。

假设公司包含用户是否在移动设备上的字段信息。该公司可以查询移动设备的点击次数:

SELECT
  user_data->>'is_mobile' AS is_mobile,
  count(*) AS count
FROM clicks
WHERE company_id = 5
GROUP BY user_data->>'is_mobile'
ORDER BY count DESC;

数据库管理员甚至可以创建部分索引来提高单个租户查询模式的速度。如通过用户在移动设备上的点击对公司5的过滤:

CREATE INDEX click_user_data_is_mobile
ON clicks ((user_data->>'is_mobile'))
WHERE company_id = 5;

此外,uxdb支持JSONB上的GIN索引。在JSONB列上创建GIN索引将在JSON文档中的每个键和值上创建索引。这将加速一些JSONB操作符。

CREATE INDEX click_user_data
ON clicks USING gin (user_data);

-- 这加快了查询,例如“哪些点击有在user_data中存在is_mobile密钥?”
SELECT id
  FROM clicks
 WHERE user_data ? 'is_mobile'
   AND company_id = 5;

XML 地图 | Sitemap 地图