用户系统设计

简介: 注册、登录、查询、用户信息修改,哪个需求量最大?支持100M DAU。注册,登录,信息修改 QPS 约100M * 0.1 / 86400 ~ 1000.1 = 平均每个用户每天登录+注册+信息修改Peak = 100 * 3 = 300

1 系统分析


1.1 Scenario 场景

注册、登录、查询、用户信息修改,哪个需求量最大?


支持100M DAU。注册,登录,信息修改 QPS 约:


100M * 0.1 / 86400 ~ 100

0.1 = 平均每个用户每天登录+注册+信息修改

Peak = 100 * 3 = 300

查询QPS约


100 M * 100 / 86400 ~ 100k

100 = 平均每个用户每天与查询用户信息相关的操作次数(查看好友,发信息,更新消息主页)

Peak = 100k * 3 = 300 k

1.2 Service 服务

AuthenticationService 负责登录注册

UserService 负责用户信息存储与查询

FriendshipService 负责好友关系存储


1.3 Storage

QPS 与 常用数据存储系统。


MySQL、PosgreSQL 等 SQL 数据库性能约 1k QPS


MongoDB、Cassandra 等硬盘型NoSQL数据库性能约 10k QPS


Redis / Memcached 等内存型NoSQL数据库性能约100k ~ 1m QPS

(根据机器性能和硬盘数量及硬盘读写速度会有区别)


用户系统特点:读非常多,写非常少。读多写少的系统一定要使用 Cache 进行优化。


使用缓存,也就会带来数据不一致问题,数据库和缓存是两台机器,两套系统,并不支持加锁。如果是用一些第三方分布式锁,会导致存取效率降低,得不偿失。业界最常用的方法:


database.set(key, user);

cache.delete(key)

class UserService {


 def getUser(self, user_id):

  key = 'user::%s' % user_id

  user = cache.get(user)

  if user:

   return user

 

  user = db.get(user_id)

  cache.set(key, user)

  return user

 

 def setUser(self, user):

   key = 'user::%s' % user.id

   db.set(user)

   cache.delete(key)

}


并发情况下依旧会出问题,在getUser执行到如下两行之间时:


14.png


另一个进程执行setUser(),cache 里会放入旧数据


问题2:db set 成功,cache delete 失败


但这两种情况发生概率<< cache.delete + db.set。


解决一致性问题?

利用 cache 的 TTL。


任何一个 cache 中的 key 都不要永久有效,设置一个短暂有效时间,如 7 天。则即便在极低概率下出现数据不一致,也就最多不一致7天。即允许数据库和缓存有“短时”不一致,但最终一致。


如果写很多,咋办?

在每次数据修改的时候,会在 cache 中 delete 这个数据。若写多读少,则此时 cache 没有任何优化效果。


Authentication Service 登录系统


Session 会话

用户 Login 后,为之创建一个 session 对象

并把 session_key 返回给浏览器,让浏览器存储起来

浏览器将该值记录在浏览器的 cookie 中

用户每次向服务器发送的访问,都会自动带上该网站所有的 cookie

此时服务器拿到 cookie 中的 session_key,在 Session Table 中检测是否存在,是否过期

Cookie

HTTP 协议中浏览器和服务器的沟通机制,服务器把一些用于标记用户身份的信息,传递给浏览器,浏览器每次访问任何网页链接的时候,都会在 HTTP 请求中带上所有的该网站相关的Cookie 信息。Cookie 可理解为一个 Client 端的 hash table。


11.png


Session 记录过期以后,服务器会主动删除么?

只支持在一台机器登陆和在多台机器同时登陆的区别是什么?

Session 适合存在什么数据存储系统中


Friendship Service


好友关系的存储与查询


双向好友关系


单向好友关系

Twitter、Instagram、微博


存在 SQL 数据库时:


查询x所有的关注对象

select * from friendship where from_user_id=x


查询x所有的粉丝:


select * from friendship where to_user_id=x;


存在 NoSQL 数据库时:


Friendship:


10.png


双向好友关系

微信,Facebook,WhatsApp


方案一:存储为一条数据

select * from friendship where smaller_user_id = x or bigger_user_id=x  


为何区分 smaller / bigger?

SQL 可以按照这种方案,但NoSQL 很多不支持 Multi-index 不能使用这种方案。


方案二:存储为两条数据

select * from friendship where from_user_id=x


NoSQL 和 SQL 都可按照这种方案


你觉得哪种更好呢?


Cassandra

三层结构的 NoSQL 数据库


http://www.lintcode.com/problem/mini-cassandra/


第一层:row_key

第二层:column_key

第三层:value

Cassandra 的 Key = row_key + column_key,同一个 Key只对应一个 value


结构化信息如何存储?

将其他需要同时存储的数据,序列化(Serialize)到 value 里进行存储。


Serialization:把一个 object / hash 序列化为一个 string,比如把一棵二叉树序列化


http://www.lintcode.com/problem/binary-tree-serialization/


row_key

又称为 Hash Key, Partition Key。Cassandra 会根据这个 key 算一个 hash 值,然后决定整条数据存储在哪儿。无法 Range Query


常用:user_id


Column Key

insert(row_key, column_key, value)


任何一条数据,都包含上面三个部分。可指定 column_key 按何排序。


Cassandra 支持这样的“范围查询”:


query(row_key, column_start, column_end)


可以是复合值,如 timestamp + user_id


SQL vs NoSQL


SQL的column是在Schema中预先指定好的,不能随意添加

一条数据一般以 row 为单位(取出整个row作为一条数据)

NoSQL的column是动态的,无限大,可以随意添加


一条数据一般以 grid 为单位,row_key + column_key + value = 一条数据

只需要提前定义好 column_key 本身的格式(是一个 int 还是一个 int+string)

Cassandra 存储Friendship表


9.png


Cassandra 存储 NewsFeed

8.png



Friendship Table适合什么数据库?


SQL 和 NoSQL 的选择标准


原则1 大部分情况,都能用

原则2 需要支持事务,则禁选 NoSQL

原则3 想在什么地方偷懒,很大程度决定选什么数据库

SQL


结构化数据,自由创建索引


NoSQL


分布式,Auto-scale,Replica


原则4 一般一个网站会同时用多种数据库系统

不同的表单放在不同的数据库。


User Table 存在哪儿?

大部分公司选择了 SQL,因为信任度,Multi-Index!


Friendship 存在哪儿?

大部分公司选择了 NoSQL,因为数据结构简单,都是 key-value 的查询/存储需求,NoSQL效率更高!

目录
相关文章
|
存储 数据库
订单系统的设计
订单系统的设计
|
8月前
|
存储 人工智能 自然语言处理
RAL-Writer Agent:基于检索与复述机制,让长文创作不再丢失关键信息
RAL-Writer Agent是一种专业的人工智能写作辅助技术,旨在解决生成高质量、内容丰富的长篇文章时所面临的技术挑战,确保全文保持连贯性和相关性。本研究将系统分析RAL-Writer的核心技术架构、功能特点及其在内容创作、学术研究和专业交流领域的应用前景。
158 4
RAL-Writer Agent:基于检索与复述机制,让长文创作不再丢失关键信息
|
8月前
|
存储 关系型数据库 MySQL
携程面试:100 亿分库分表 如何设计? 核弹级 16字真经, 让面试官彻底 “沦陷”,当场发offer!
携程面试:100 亿分库分表 如何设计? 核弹级 16字真经, 让面试官彻底 “沦陷”,当场发offer!
携程面试:100 亿分库分表 如何设计?  核弹级 16字真经, 让面试官彻底 “沦陷”,当场发offer!
|
10月前
|
SQL Cloud Native API
NSDI'24 | 阿里云飞天洛神云网络论文解读——《Poseidon》揭秘新型超高性能云网络控制器
NSDI‘24于4月16-18日在美国加州圣塔克拉拉市举办,汇聚全球网络系统领域的专家。阿里云飞天洛神云网络的两篇论文入选,标志着其创新能力获广泛认可。其中,《Poseidon: A Consolidated Virtual Network Controller that Manages Millions of Tenants via Config Tree》介绍了波塞冬平台,该平台通过统一控制器架构、高性能配置计算引擎等技术,实现了对超大规模租户和设备的高效管理,显著提升了云网络性能与弹性。实验结果显示,波塞冬在启用EIP时的完成时间比Top 5厂商分别快1.8至55倍和2.6至4.8倍。
1051 146
|
8月前
|
存储 缓存 算法
G1原理—3.G1是如何提升垃圾回收效率
本文深入探讨了G1垃圾回收器提升GC效率的核心机制,包括记忆集(RSet)、位图(BitMap)和卡表(CardTable)的设计与作用。记忆集通过记录跨代引用避免了不必要的老年代遍历,位图用于高效描述内存使用状态以优化标记过程,而卡表则在节约记忆集内存的同时提供更详细的引用信息。此外,文章还解析了DCQ(Dirty Card Queue)和DCQS(Dirty Card Queue Set)机制如何异步更新RSet,确保在高并发场景下的性能与准确性。这些设计共同提升了G1在标记、清理及整理内存时的效率。
400 10
|
8月前
|
人工智能 关系型数据库 MySQL
解决MySQL自增id用尽的问题
本文介绍了解决文章点击记录表(`article_click_record`)数据量激增问题的方案。由于用户量大,每天新增约400万条记录,导致表id接近溢出(2,100,000,000),且占用空间超320G。解决方案包括:1) 新建`article_click_record_new`表,将id类型改为BIGINT以避免溢出;2) 过渡阶段同时写入新旧表,待旧表id溢出后切换至新表;3) 定时清理过期数据或转移旧表内容。实现方式涉及修改相关接口和服务逻辑,确保业务平稳过渡。
193 5
|
8月前
|
关系型数据库 MySQL Java
【YashanDB知识库】Kettle迁移MySQL到YashanDB
本文介绍了使用Kettle将MySQL数据库中的中文数据迁移到YashanDB的方法,解决因YMP不支持Latin1字符集导致的乱码问题。提供了Windows和Linux两种环境下的操作步骤,包括配置JAVA环境、解压作业包、设置数据库连接(MySQLInput与YashanOutput)、修改表列表配置文件及运行迁移任务。Windows环境支持图形界面便于调试,Linux环境网络性能更优。通过详细的操作指南,确保数据迁移成功并可重试无冲突。
|
8月前
|
调度 数据库 Python
【YashanDB 知识库】DolphinScheduler 适配崖山 Python 驱动
本文来自YashanDB官网,介绍如何将DolphinScheduler调度器适配崖山Python驱动,实现通过Python任务访问崖山数据库。环境要求包括DolphinScheduler 3.1.9、Python 3.9及YashanDB 23.2.2.100。操作步骤涵盖安装YashanDB客户端、配置环境变量、安装Python驱动以及设置DolphinScheduler相关参数,助力用户高效集成与使用。
|
8月前
|
SQL 人工智能 算法
《解锁AI魔法,为SQL查询优化量身定制策略》
在数据驱动的时代,SQL查询性能对企业决策至关重要。面对海量数据与复杂场景,传统优化手段渐显乏力,而AI技术的引入为SQL查询优化带来了新突破。通过深度分析查询执行指标(如执行时间、CPU使用率、I/O读取次数等),AI可精准识别问题并制定优化策略。例如,将子查询优化为连接查询以缩短执行时间,调整索引结构减少I/O读取,优化算法降低CPU负载。实际案例中,某金融机构借助AI优化,将复杂查询时间从数分钟降至十几秒,显著提升效率。AI不仅助力企业提高数据处理能力、降低成本,更为数字化转型注入强劲动力,展现广阔应用前景。
316 2
|
8月前
|
人工智能