横向扩展 纵向扩展 数据库

There are lot of articles online describing database scalability patterns, but they are mostly scattered articles — just techniques that are defined haphazardly without much context. I find that they are not defined in a step by step manner, and don't discuss when to choose which scaling option, which scaling options are feasible in practise, and why.

在线上有很多文章描述数据库可伸缩性模式,但是它们大多是分散的文章,只是在没有太多上下文的情况下随意定义的技术。 我发现它们不是一步一步定义的,并且没有讨论何时选择哪种缩放选项,哪些缩放选项在实践中可行以及为什么。

Therefore, I am planing to discuss some of the techniques in detail in future articles. To start, I feel it’s better if I discuss step by step techniques with some context in my own way. This article is a high level article — I will not discuss scaling techniques in details here, but will provide an overview. So let's get started.

因此,我计划在以后的文章中详细讨论一些技术。 首先,我觉得最好以自己的方式在某些情况下逐步讨论技术。 本文是高级文章—我将不在这里详细讨论缩放技术,而是提供概述。 因此,让我们开始吧。

案例研究 (A case study)

Assume you have built a startup which offers ride sharing at a cheap cost. Initially when you start, you target a city and hardly have tens of customers after your initial advertisement.

假设您建立了一家可以廉价提供拼车服务的初创公司。 最初,当您开始时,您以城市为目标,并且在最初的广告发布后几乎没有几十个客户。

You save all customers, trips, locations, bookings data, and customer trip history in the same database or most likely in a single physical machine. There is no fancy caching or big data pipeline to solve problems since your app is very new. This is perfect for your use case at this moment since there are very few customers and your system hardly books 1 trip in 5 minutes, for example.

您可以将所有客户,行程,位置,预订数据和客户行程历史记录保存在同一数据库中,或者最有可能保存在单个物理计算机中。 由于您的应用程序很新,因此没有花哨的缓存或大数据管道可以解决问题。 目前这对您的用例来说是完美的,因为客户很少,并且您的系统几乎不会在5分钟内预订1次行程。

But as time goes on, more people start signing up in your system since you are the cheapest service in the market and thanks to your promotion and ads. You start booking, say, 10 bookings per minute, and slowly the number increases to 20, 30 bookings per minute.

但是随着时间的流逝,由于您是市场上最便宜的服务,这要归功于您的促销和广告,越来越多的人开始注册您的系统。 您开始预订,例如每分钟10个预订,然后慢慢增加到每分钟20、30个预订。

At this point of time, you realize that the system has started performing poorly: API latency has increased a lot, and some transactions deadlock or starve and eventually they fail. Your app is taking more time to respond, causing customer dissatisfaction. What can you do to solve the problem?

此时,您意识到系统开始运行不佳:API延迟增加了很多,某些事务陷入死机或饥饿,最终失败。 您的应用需要更多时间来做出响应,从而引起客户不满。 您能怎么解决这个问题?

模式1-查询优化和连接池的实现: (Pattern 1 - Query Optimization & Connection Pool implementation:)

The first solution that comes in mind is that the cache frequently uses non-dynamic data like booking history, payment history, user profiles and so on. But after this application layer caching, you can’t solve the latency problem of APIs exposing dynamic data like the current driver's location or the nearest cabs for a given customer or current trip cost at a certain moment in time after the trip starts.

首先想到的解决方案是,缓存经常使用非动态数据,例如预订历史记录,付款历史记录,用户个人资料等。 但是在应用程序层缓存之后,您将无法解决API的延迟问题,该API会在行程开始后的某个时刻公开动态数据,例如给定客户的当前驾驶员位置或最近的出租车,或者当前行程成本。

You identify that your database is probably heavily normalized, so you introduce some redundant columns (these columns frequently appear in WHERE or JOIN ON clause in queries) in highly used tables for the sake of denormalization. This reduces join queries, breaks a big query into multiple smaller queries, and adds their results up in the application layer.

您确定数据库可能已高度规范化,因此为了高度规范化,在高度使用的表中引入了一些冗余列(这些列经常出现在WHEREJOIN ON子句中)。 这样可以减少联接查询,将一个大查询分解为多个较小的查询,并将其结果添加到应用程序层中。

Another parallel optimization that you can do is tweaking around database connections. Database client libraries and external libraries are available in almost all programming languages. You can use connection pool libraries to cache database connections or can configure connection pool size in the database management system itself.

您可以执行的另一个并行优化是围绕数据库连接进行调整。 数据库客户端库和外部库几乎以所有编程语言提供。 您可以使用连接池库来缓存数据库连接,也可以在数据库管理系统本身中配置连接池的大小。

Creating any network connection is costly since it requires some back & forth communication between client & server. Pooling connections may help you to optimize the number of connections. Connection pool libraries may help you to multiplex connections — multiple application threads can use the same database connection. I shall see if I can explain connection pooling in detail in a separate article later.

建立任何网络连接的成本很高,因为它需要客户端与服务器之间进行一些来回通信。 池化连接可以帮助您优化连接数。 连接池库可以帮助您多路复用连接-多个应用程序线程可以使用同一数据库连接。 我将在以后的另一篇文章中看到是否可以详细解释连接池。

Your measure latency of your APIs & find probably 20–50% or more reduced latency. This is good optimization at this point in time.

您可以测量API的延迟,并发现延迟可能减少20–50%或更多。 目前,这是一个很好的优化。

You have now scaled your business to one more city, more customer sign up, you slowly start to do 80–100 bookings per minute. Your system is not able to handle this scale. Again you see API latency has increased, database layer has given up, but this time, no query optimization is giving you any significant performance gain. You check the system metric, you find disk space is almost full, CPU is busy 80% of the time, RAM fills up very quickly.

现在,您已经将业务扩展到了另一个城市,注册了更多的客户,然后慢慢开始每分钟预订80-100次。 您的系统无法处理此比例。 再次您看到API延迟增加了,数据库层已经放弃了,但是这一次,没有查询优化可以给您带来显着的性能提升。 您检查系统指标后,发现磁盘空间几乎已满,CPU忙于80%的时间,RAM很快就装满了。

模式2-垂直缩放或放大: (Pattern 2 - Vertical Scaling or Scale Up:)

After examining all system metrics, you know there is no other easy solution rather than upgrading the hardware of the system. You upgrade your RAM size by 2 times, upgrade disk space by, say, 3 times or more. This is called vertical scaling or scaling up your system. You inform your infrastructure team or devops team or third party data centre agents to upgrade your machine.

在检查了所有系统指标之后,您知道除了升级系统硬件之外,没有其他简单的解决方案。 您将RAM大小升级了2倍,将磁盘空间升级了3倍或更多。 这称为垂直扩展或扩展系统。 您通知基础架构团队或开发团队或第三方数据中心代理升级您的计算机。

But how do you set up machine for vertical scaling?

但是,如何设置机器进行垂直缩放?

You allocate a bigger machine. One approach is not to migrate data manually from old machine rather set the new machine as replica to the existing machine (primary)-make a temporary primary replica configuration. Let the replication happen naturally. Once the replication is done, promote the new machine to primary & take the older machine offline. Since the bigger machine is expected to serve all request, all read / write will happen on this machine.

您分配一台更大的机器。 一种方法是不从旧计算机手动迁移数据,而是将新计算机设置为replica到现有计算机( primary ),进行临时primary replica配置。 让复制自然发生。 复制完成后, 将新计算机升级为主服务器并使旧计算机脱机。 由于预期较大的计算机可以满足所有请求,因此所有读/写操作都将在此计算机上进行。

Cool. Your system is up & running again with increased performance.

凉。 您的系统重新启动并运行,从而提高了性能。

Your business is doing very well & you decide to scale to 3 more cities — you are now operational in 5 cities total. Traffic is 3x times than earlier, you are expected to do around 300 bookings per minute. Before even achieving this target booking, you again hit the performance crunch, database index size is increasing heavily in memory, it needs constant maintenance, table scanning with index is getting slower than ever. You calculate the cost of scaling up the machine further but not convinced with the cost. What do you do now?

您的业​​务做得很好,您决定扩展到另外3个城市-您现在在5个城市运营。 流量是以前的3倍,预计每分钟可以进行300笔预订。 甚至在达到此目标预定之前,您就再次遇到了性能瓶颈,数据库索引大小在内存中大量增加,需要不断维护,使用索引进行表扫描的速度比以往任何时候都要慢。 您可以计算出进一步扩大机器规模的成本,但对成本不满意。 你现在做什么?

模式3-命令查询职责隔离(CQRS): (Pattern 3 - Command Query Responsibility Segregation (CQRS):)

You identify that the big machine is not able to handle all read/write requests. Also in most of the cases, any company needs transactional capability on write but not on read operations. You are also fine with a little bit of inconsistent or delayed read operations & your business has no issue with that either. You see an opportunity where it might be a good option to separate the read & write operations physical machine wise. It will create scope for individual machines to handle more read/write operations.

您确定大型计算机无法处理所有read/write请求。 同样,在大多数情况下,任何公司都需要在write操作上具有事务处理能力,而在read操作上则不需要。 一点点不一致或延迟的read操作也可以,而且您的业务也没有问题。 你看到了机会在那里它可能是一个很好的选择,分开readwrite方式的运算物理机。 它将为单个计算机创建范围以处理更多的read/write操作。

You now take two more big machines & set them up as replica to the current machine. Database replication will take care of distributing data from primary machine to replica machines. You navigate all read queries (Query (Q) in CQRS) to the replicas — any replica can serve any read request, you navigate all write queries (Command (C) in CQRS) to the primary. There might be little lag in the replication, but according to your business use case that’s fine.

现在,您再使用两台大型计算机,并将它们设置为当前计算机的replica 。 数据库复制将负责将数据从primary计算机分发到replica计算机。 您将所有读取查询( CQRS查询( Q )) CQRS到副本-任何replicaCQRS任何读取请求,您将所有写入查询( CQRS Command( C )) CQRSprimary 。 复制中的延迟可能很少,但是根据您的业务用例,这很好。

Most of the medium scale startups which serve few hundred thousand requests everyday can survive with primary-replica set up provided that they periodically archive older data.

每天处理数十万次请求的大多数中型初创企业只要设置为定期复制较旧的数据,就可以通过设置主副本生存下来。

Now you scale to 2 more cities, you see that your primary is not able to handle all write requests. Many write requests are having latency. Moreover, the lag between primary & replica sometimes impact customers & drivers ex — when trip ends, customer pays the driver successfully, but the driver is not able to see the payment since customer’s activity is a write request that goes to the primary, while driver’s activity is a read request that goes to one of the replicas. Your overall system is so slow that driver is not able to see the payment for at least half a minute — frustrating for both driver & customer. How do you solve it?

现在,您可以扩展到另外2个城市,您会看到您的primary城市无法处理所有write请求。 许多write请求都有延迟。 此外, primaryreplica之间的时差有时会影响客户和驱动程序,例如,旅行结束后,客户成功向驱动程序付款,但是由于客户的活动是发给primarywrite请求,因此驱动程序无法看到付款。活动是对副本之一进行的read请求。 您的整个系统太慢了,以至于驾驶员至少半分钟都看不到付款,这对驾驶员和客户都感到沮丧。 您如何解决?

模式4-多主复制 (Pattern 4 - Multi Primary Replication)

You scaled really well with primary-replica configuration, but now you need more write performance. You might be ready to compromise a little bit on read request performance. Why not distribute the write request to a replica also?

您可以使用primary-replica配置很好地扩展,但是现在您需要更高的写入性能。 您可能已经准备好在read请求性能上做出一些妥协。 为什么不将写请求也分发到replica

In a multi-primary configuration, all the machines can work as both primary& replica. You can think of multi-primary as a circle of machines say A->B->C->D->A. B can replicate data from A, C can replicate data from B, D can replicate data from C, A can replicate data from D. You can write data to any node, while reading data, you can broadcast the query to all nodes, whoever replies return that. All nodes will have same database schema, same set of tables, index etc. So you have to make sure there are no collision in id across nodes in the same table, otherwise during broadcasting, multiple nodes would return different data for the same id.

multi-primary配置中,所有计算机都可以同时充当primaryreplica 。 您可以将multi-primary看作是一圈机器,例如A->B->C->D->A B可以从A复制数据, C可以从B复制数据, D可以从C复制数据, A可以从D复制数据。 您可以将数据写入任何节点,而在读取数据时,您可以将查询广播到所有节点,无论回复者返回该查询。 所有节点都将具有相同的数据库架构,相同的表集,索引等。因此,您必须确保同一表中各个节点之间的id之间没有冲突,否则在广播期间,多个节点将针对同一个id返回不同的数据。

Generally it’s better to use UUID or GUID for id. One more disadvantage of this technique is — read queries might be inefficient since it involves broadcasting query & getting the correct result — basically scatter gather approach.

通常,最好使用UUIDGUID作为ID。 该技术的另一个缺点是- read查询可能效率低下,因为它涉及广播查询并获得正确的结果-基本上是分散收集方法。

Now you scale to 5 more cities & your system is in pain again. You are expected to handle roughly 50 request per second. You are in desperate need to handle heavy number of concurrent requests. How do you achieve that?

现在您可以扩展到另外5个城市,并且系统再次陷入困境。 您应该每秒处理大约50个请求。 您迫切需要处理大量并发请求。 您如何实现的?

模式5-分区: (Pattern 5 - Partitioning:)

You know that your location database is something which is getting high write & read traffic. Probably write:read ratio is 7:3. This is putting a lot of pressure on the existing databases. The location tables contain few primary data like longitude, latitude, timestamp, driver id, trip id etc. It does not have a much to do with user trips, user data, payment data etc. What about separating the location tables in a separate database schema? What about putting that database in separate machines with proper primary-replica or multi-primary configuration?

你知道你的location数据库的东西这是越来越高的writeread流量。 可能write:read比率是7:3 。 这给现有数据库带来了很大压力。 location表包含一些主要数据,如longitudelatitudetimestampdriver idtrip id等。它与用户旅行,用户数据,付款数据等关系不大。在单独的数据库中分离location表又如何呢?模式? 怎么样把该数据库在单独的机器与适当的primary-replica或者multi-primary配置?

This is called partitioning of data by functionality. Different database can host data categorized by different functionality, if required the result can be aggregated in the back end layer. Using this technique, you can focus on scaling those functionalities well which demand high read/write requests. Although the back end or application layer has to take the responsibility to join the results when necessary resulting in more code changes probably.

这称为按功能划分数据。 不同的数据库可以托管按不同功能分类的数据,如果需要,可以在后端层中汇总结果。 使用这种技术,您可以集中精力很好地扩展那些要求高read/write请求的功能。 尽管后端或应用程序层必须在必要时负责加入结果,但可能导致更多代码更改。

Now imagine you have expanded your business to a total of 20 cities in your country & planning to expand to Australia soon. Your increasing demand of app requires faster & faster response. None of the above method can help you to the extreme now. You must scale your system in such a way that expanding to other countries / regions does not always need you to do frequent engineering or architecture changes. How do you do that?

现在,假设您已将业务扩展到您所在国家的20个城市,并计划很快扩展到澳大利亚。 您对应用程序不断增长的需求要求越来越快的响应。 以上方法现在都无法帮助您。 您必须以这样一种方式扩展系统,使其扩展到其他国家/地区并不总是需要您经常进行工程或体系结构更改。 你是怎样做的?

模式6-水平缩放: (Pattern 6 - Horizontal Scaling:)

You do lot of googling, read a lot on how other companies have solved the issue — and come to the conclusion that you need to scale horizontally. You allocate say 50 machines — all have the same database schema which in turn contains the same set of tables. All the machines just hold a part of data.

您进行了大量的Google搜索,大量阅读了其他公司如何解决该问题的信息,并得出结论,您需要水平扩展。 您分配了50台计算机-所有计算机都具有相同的数据库架构,而数据库架构又包含相同的表集。 所有机器只保存一部分数据。

Since all databases contain same set of tables, you can design the system in such a way that locality of data is there i.e; all related data lands in the same machine. Each machine can have their own replicas, replicas can be used in failure recovery. Each of the databases is called shard. A physical machine can have one or multiple shards — it’s up to your design how you want. You need to decide on sharding key in such a way that a single sharding key always refers to the same machine. So you can imagine lot of machines all holding related data in same set of tables, read/write requests for the same row or same set of resource land in the same database machine.

由于所有数据库都包含相同的表集,因此您可以以这样的一种方式设计系统,即数据在本地。 所有相关数据都位于同一台计算机上。 每台机器可以有自己的副本,副本可以用于故障恢复。 每个数据库都称为shard 。 一台物理机器可以具有一个或多个分shards ,这取决于您的设计。 您需要以某种方式确定sharding key ,以使单个sharding key始终引用同一台计算机。 因此,您可以想象很多机器都在同一组表中保存相关数据,在同一台数据库机中对同一行或同一资源域的read/write请求。

Sharding is in general hard — at least engineers from different companies say that. But when you serve millions or billions of requests, you have to make such tough decision.

分片通常很难-至少来自不同公司的工程师都这么说。 但是,当您处理数百万或数十亿个请求时,您必须做出艰难的决定。

I will discuss sharding in greater detail in my next post, so holding back my temptation to discuss more in this post.

在下一篇文章中,我将更详细地讨论sharding ,因此,我不愿意在本篇文章中讨论更多内容。

Now since you have sharding in place, you are confident that you can scale to many countries. Your business has grown so much that investors are pushing you to scale the business across continents. You again see some problem here. API latency again. Your service is hosted in USA & people from Vietnam are having difficult time book rides. Why? What do you do about it?

现在,由于您已完成分片,因此您可以扩展到许多国家/地区。 您的业​​务发展如此之快,以至于投资者正在推动您在各大洲扩展业务。 您再次在这里看到一些问题。 API延迟再次出现。 您的服务在美国托管,越南的人们很难安排时间。 为什么? 你对此怎么办?

模式7-明智的数据中心分区: (Pattern 7 - Data Centre Wise Partition:)

Your business is growing in America, South Asia & in few countries in Europe. You are doing millions of bookings daily with billions of request hitting your server. Congrats - this is a peak moment for your business.

您的业​​务在美国,南亚和欧洲的少数几个国家/地区增长。 您每天要进行数百万个预订,而数十亿个请求就会到达您的服务器。 恭喜-这是您业务的高峰时刻。

But since requests from the app have to travel across continents through hundreds or thousands of servers in the internet, the latency arises. What about distributing traffic across data centres? You can set up a data centre in Singapore that handles all requests from South Asia, data centre in Germany can handle all requests from European countries, and a California data centre can handle all USA requests.

但是,由于来自应用程序的请求必须通过Internet上成百上千的服务器在各大洲之间传输,因此出现了延迟。 如何在数据中心之间分配流量呢? 您可以在新加坡建立一个数据中心来处理来自南亚的所有请求,德国的数据中心可以处理来自欧洲国家的所有请求,而加利福尼亚的数据中心可以处理所有美国的请求。

Also you enable cross data centre replication which helps disaster recovery. So if California data centre does replication to Singapore data centre, in case California data centre crashes due to electricity issue or natural calamity, all USA requests can fall back to Singapore data centre and so on.

您还可以启用跨数据中心复制,这有助于灾难恢复。 因此,如果加利福尼亚数据中心确实复制到新加坡数据中心,以防万一加利福尼亚数据中心由于电力问题或自然灾害而崩溃,美国的所有请求都可以退回到新加坡数据中心,依此类推。

This scaling technique is useful when you have millions of customers to serve across countries and you can’t accommodate any data loss, you have to always maintain availability of the system.

当您有数以百万计的客户需要在各个国家/地区服务并且无法承受任何数据丢失,而您必须始终保持系统的可用性时,此缩放技术非常有用。

These are some general step by step techniques for database scaling. Although most of the engineers don’t get enough chance to implement these techniques, but as a whole it’s better to get a broader idea about such system which in future may help you to do better system & architecture designing.

这些是一些用于数据库扩展的常规逐步技术。 尽管大多数工程师没有足够的机会来实施这些技术,但是总体上最好是对这种系统有更广泛的了解,这将来可能会帮助您进行更好的系统和体系结构设计。

In my next articles, I will try to discuss some of the concepts in details. Please feel free to give appropriate feedback for this post if any.

在接下来的文章中,我将尝试详细讨论一些概念。 如果有此帖子,请随时提供适当的反馈。

The article is originally published on the author's medium account: https://medium.com/@kousiknath/understanding-database-scaling-patterns-ac24e5223522

该文章最初在作者的中等帐户上发布: https : //medium.com/@kousiknath/understanding-database-scaling-patterns-ac24e5223522

翻译自: https://www.freecodecamp.org/news/understanding-database-scaling-patterns/

横向扩展 纵向扩展 数据库

横向扩展 纵向扩展 数据库_理解数据库扩展模式的指南相关推荐

  1. Oracle数据库中的方案,学习Oracle数据库_理解Oracle数据库中的方案

    理解数据库.表空间.数据文件之间的关系. 每个表空间由一个或多个数据文件组成.数据文件用于在物理上存储表空间中所有逻辑结构的数据.表空间中数据文件的大小之和就是表空间的存储容量(图中系统表空间存储容量 ...

  2. mysql 的数据库实例理解_理解数据库和实例

    数据库:物理操作系统文件或者其他形式文件类型的集合.在mysql中,数据库文件可以是frm.MYD.MYI.ibd等结尾的文件.当使用NDB引擎时,数据库的文件可能不是操作系统文件,而是存放于内存之中 ...

  3. 如何避免循环查询数据库_与数据库无关的查询是不可避免的

    如何避免循环查询数据库 As the amount of data managed by an organization grows, the difficulty of managing and q ...

  4. tidb数据库_异构数据库复制到TiDB

    tidb数据库 This article is based on a talk given by Tianshuang Qin at TiDB DevCon 2020. 本文基于Tianshuang ...

  5. mysql时序性数据库_时序数据库入门

    数据库的模型包含关系型.key-value 型.Document 型等很多种,那么为什么新型的时序数据库成为监控数据存储的新宠呢? 下面就会从 为什么需要时序数据库? 时序数据库的数据结构 两个方面来 ...

  6. 操作 神通数据库_国产数据库最好的时代

    全文约2580字,阅读约15分钟 近日,墨天轮发布了2020年新一期的国产数据库名单,东方国信完全自主研发的分布式分析型数据库CirroData名列其中. "墨天轮"是国内数据库领 ...

  7. gp数据库创建数据库_创建数据库简介

    gp数据库创建数据库 MySQL是当今最流行的开源数据库之一. 它在商业和开源双重许可模式下可用. MySQL找到了从嵌入式设备到集群企业环境的各种应用程序. POWER5™处理器是IBMPPC®AS ...

  8. 电子表格转换成数据库_创建数据库,将电子表格转换为关系数据库,第1部分...

    电子表格转换成数据库 Part 1: Creating an Entity Relational Diagram (ERD) 第1部分:创建实体关系图(ERD) A Relational Databa ...

  9. sql还原数据库备份数据库_有关数据库备份,还原和恢复SQL面试问题–第一部分

    sql还原数据库备份数据库 So far, we've discussed a lot about database backup-and-restore process. The backup da ...

最新文章

  1. Java向MySQL数据库插入时间类型Date数据时需要注意的问题
  2. 创业要拿出独门秘籍才行
  3. 第2章 网页基础知识
  4. 工作285:判断绑定逻辑
  5. arm ida 伪代码 安卓 符号表_使用IDA动态调试及ARM指令学习笔记
  6. android的ui怎么做到流畅,android提高UI的流畅度
  7. 第十二天--Property List和NSUserDefaults
  8. 通向财务自由之路08_入市或市场时机选择
  9. SSH密钥 - 仍然要求输入密码和密码
  10. fx2n4ad模块中文手册_特殊功能模块FX2N-4AD用户指南手册三菱FX2N-4AD手册 - 广州凌控...
  11. matlab 峰值位置,在数据中查找峰值 - MATLAB Simulink - MathWorks 中国
  12. IDL调用ENVI-FLAASH大气校正异常退出解决办法
  13. Axure设计原型如何如何插入视频
  14. 易捷行云新一代私有云平滑无感升级|轻运维之升级
  15. iOS 15提示“此App的开发者需要更新APP以在此IOS版本上正常工作”
  16. 导出虚拟机的OVF 模板
  17. Java 调用Google Map Api解析地址,解析经纬度实例
  18. 各种格式文件转PDF的免费网站-转
  19. android和ios系统的内存,为什么说IOS系统的2G运存相当于安卓手机的8G运存
  20. jkd环境安装脚本(jdk-8u201-linux-x64.tar.gz)

热门文章

  1. 疯狂的程序员1-40
  2. 第2章 第2节-Dijkstra Astar
  3. vue---lodash的使用
  4. Noise2Noise的一些学习总结
  5. 手机读卡但显示无服务器,手机不读卡了怎么修复
  6. 穿山甲别于传统广告联盟,造势创建新角色
  7. 这段代码不讲武德,劝你耗子尾汁
  8. 用户画像打标签之RFM客户价值分析
  9. 四大组件之activity生命周期探索
  10. 培训班出来,碰到查学历,哎宝宝苦。