01|结构梳理:大并发下,你的数据库表可能成为性能隐患
01|结构梳理:大并发下,你的数据库表可能成为性能隐患
讲述:徐长龙
时长16:23大小14.96M
精简数据会有更好的性能
数据的归类及深入整理
数据实体表
实体辅助表
实体关系表
动作历史表
总结
思考题
赞 16
提建议
精选留言(29)
- peter置顶2022-10-25 来自北京请教老师几个问题: Q1:MySQL一个表最大记录是2000万吗? 多个地方看到一种说法:MySQL的表,记录数不要超过两千万,根据是什么?经验值吗?还是和MySQL的底层结构有关? Q2:亿级用户是分表吗? 比如微信,十亿用户,要分成多个表吗?分的话,一般根据什么分?展开
作者回复: 你好,peter,感谢你的留言~由于问题比较多,我只能简略回答一下,后续我们的课程内容也会有相关的内容 Q1:MySQL innoDB的2000w性能下降的说法,不一定准确,这个数字和单条数据大小以及数据量有直接关系,我们的数据是行存储并且和聚簇索引在一起,并且查询索引是B+Tree,如果这个服务用途不是马上要求返回结果,也是能用的,只不过响应变得缓慢了,但是大部分我们对外服务对响应速度和并发是有要求的。缓慢原因是我们的MySQL的索引是整棵树完整结构,并且我们的实际数据都在最深处保存,如果树深度过深(比如超过3层),查询时和回表时相应的我们查询对比的次数和io会变多,这会直接导致了性能会有下降,能够承担的服务请求量也相应下降,我们用MySQL不推荐这么多的原因核心在于,我们还是想让接口请求能够快速回复查询请求 Q2:我想这个重点在于,业务怎么用这些数据,这决定了我们怎么分表,我们分表的目的主要是为了提高响应速度,我的建议是,如果是存储可以按取值范围拆分,这样可以简单做二分法查找确定数据服务在哪里,如果是用来对用户查询,那么在他的上层加上多层的缓存分片,类似LevelDB的层级,被查询的时候可以回源,当然这个是适合读多写少的场景,如果是其他场景,需要另外设计对应的方式,这些方式在后面都有介绍,敬请期待 你的问题很有趣,期待你的下次留言!
共 6 条评论5 - 业余草2022-10-25 来自北京https://static001.geekbang.org/horde/8c/8c13453d81da3149b58334b4625d2788.jpeg 这里放一张我在部落里发的图。用户邀请其他用户注册的记录,属于历史记录还是关系记录,主要取决于业务结构。我推荐使用历史记录
作者回复: 你好,业余草,感谢你的分享,当我们需要查询这个用户的邀请人时,还需要查询这个表,那么和我们对表的职能拆分有一定冲突,那么如何改进这个实现呢
4 - 移横为固2022-11-03 来自北京思考题:一开始觉得注册邀请表应该作为历史表. 思考了下作为关系表也是可以的 在满足下面的注册邀请前提下: 1.邀请人用类似二维码分享方式,注册人主动扫码注册。(不使用点对点邀请,被邀请人可能不接受) 2.只能注册成功一次 这样每一条邀请记录都是一个用户的注册记录:可以定义如下字段 (邀请者,注册人,注册时间,邀请方式) 表的字段结构都非常简单,记录的总量最多就是账号量,并不会随时间不断膨胀。因此可以胜任关系表的查询需求。展开
作者回复: 你好,移横为固,很高兴收到你的思考,你的回答和我当时想法很一致,我们做项目会碰到很多类似的情况,需要我们去预防超出预期的操作。 简单的说就是:我们怎么约束用的人,以及我们怎么用这个数据。
共 4 条评论2 - Daniel2022-10-24 来自北京我认为是“历史记录”, 因为在统计一个用户一共邀请了多少个人的时候,是需要在总体的邀请人数中去筛选这部分人,而总共邀请的人数会是一个动态不断增长的数字。 老师,我想请问一下,在什么业务场景下(是不是历史记录的表信息就可以用非关系型数据库来处理),可以考虑把关系型数据库的数据转移到类似于mongoDB这类Nosql类型的数据库,而不是用缓存来处理呢,二者选取的关键因素有哪些?展开
作者回复: mongoDB是一个很有趣的数据库,很多设计开创了行业先河,但有几个老版本因为全局锁问题导致使用的时候,需要慎重,因为这个锁在并发高的时候加服务器也不能提升性能,后续的版本我没有太关注。 建议在选择的时候能够压测验证下现状来确认是否给不确定用户流量的系统使用,即使使用也建议对他加一层缓存。 至于历史记录是否选择mongodb,主要看我们的业务场景,如果是给大量用户使用不推荐,如果是我们存起来用来做数据分析,并且不会有大量流量的话可以使用。核心在于是否能够高可用,它的性能是否符合我们场景需要,是否满足我们的业务需要。
2 - 一步2022-10-30 来自北京邀请注册记录:如果 历史表个关系表(关系表中保存 邀请人ID, 邀请历史记录 ID)同时保存呢? 一般邀请统计的业务,会要求看到邀请的具体统计信息,比如:每天的邀请人数,邀请总人数等,这里邀请关系可以放到缓存中; 邀请历史表保存 具体的邀请历史记录,这样就可以通过邀请关系缓存拿到记录id 进而获取到邀请历史记录详情信息
作者回复: 你好,一步,很高兴收到你的心得,邀请关系放缓存是个好办法,建议表也拆一下,然后查找历史详情这样感觉会更方便一些~
1 - 人无远虑,必有近忧2022-10-25 来自北京感谢老师,获益匪浅值得学习!
作者回复: 你好,感谢你的支持!学习过程中有任何问题,欢迎提出,多多交流
1 - 拾掇拾掇2022-10-24 来自北京邀请很像转介绍业务,所以应该是历史记录
作者回复: 你好,拾掇拾掇,感谢你的留言,在使用中,如果我们想查询这个人的邀请人,这个表还是会被业务查询,那么如何设计改进呢?
1 - dk.wu2022-12-07 来自北京个人倾向:历史记录 有个场景是我邀请了,但是邀请的人并没有注册,那么用户表也就不存在,不属于真实的关系。 本身通过用户拉人头的方式,就是记录有效的邀请数,进而给予对应奖励。
作者回复: 你好,这里未注册确实是一个需要思考的地方
共 2 条评论 - Geek_e7d3962022-12-02 来自北京老师,请教一下,上面说 "长度小的数据在吞吐、查询、传输上都会很快" 这是为什么呢? 只要不是SELECT *,而只取所需要的的字段,对传输的影响差别不大 索引只是保存主键和索引的字段,对查询没有影响 感觉只是影响缓存池内能存储的数据大小
作者回复: 你好,其实有影响只不过现代使用SSD已经不明显了,只有在数据量大的时候会有区别,可以看看第三章内容,行存储和列存储心中会有解答
- 刘章2022-11-30 来自北京老师你好: 我们现在用的是一主多从的数据库,设置了读写分离,写是 主库, 读是从库。 有时间会出现一些莫名问题,就是同步延迟问题,明明是修改完成了,但是读取不到就会是程序出现问题,这个有什么好的办法吗
作者回复: 你好,刘章,可以用MySQL proxy一类的中间件自动确认数据情况切换主从,如果是云服务,像阿里云的polardb可以在代理设置强一致。如果都没有可以业务上优先更新缓存,优先读缓存数据
共 2 条评论 - 请叫我和尚2022-11-26 来自北京我们要解决一些问题: 问题1. 用户 A 邀请了哪些用户 问题2. 用户 B 是被哪个用户邀请的 根据具体的业务场景来分析: 方案一、如果邀请关系是 1:1 那就是做历史记录表 1.2 点都可以解决 方案二、如果邀请关系是 N:1,但是最终只能一个人邀请成功 那就是关系记录表 但是最终会在用户的其他表里记录最终被邀请成功的 user_id 1.2 点也都可以解决 但是现在出现了一个问题,如果考虑到分表,然后分表键的设计: 如果是以 user_id 分表,那现有方案一不能解决问题 2,要解决问题 2,同理需要在用户的其他表记录最终被邀请成功的 user_id展开
作者回复: 你好,这个思路更详细,点赞~
- ls2022-11-18 来自北京请教老师一个问题: 对于一些上下级关系的数据该怎么建模和保存?比如代理商下面有下级代理,下级代理下面还有下下级,层级有10多级,这种怎么去保存?是一个树形层次结构
作者回复: 你好,ls,虽然他们是树形依赖关系,但是他们的数据结构是相同的,可以做在一个表内,类似parent_id方式去记录上级关联关系,当我们需要按这个树形去处理时完全可以在界面让前端处理好给服务端具体树形层级,同时可以记录一些冗余的数据,如当前所在层级,同时注意如果是代理商,很有可能存在循环依赖情况,如a供货商同时是b供货商的上游,b供货商是c供货商的上游,但是c供货商还是会从a供货商拿一些配件~所以这里要做好预防规划
- edward2022-11-17 来自北京我认为属于历史记录,因为邀请动作是发生过就不会再改变了。
作者回复: 你好,edward,很高兴收到你的思考,但是有个问题,如果是多个活动,会出现多次邀请时这个结论就会发生变化
- 库嚓嚓2022-11-09 来自北京请教老师一个和用户系统相关的问题: 我们有几个spring mvc的web项目,这几个项目都涉及简单的用户和权限体系,几个服务都使用了spring security框架做权限控制。 现在想抽取一个用户服务(非web服务)出来进行业务和代码的复用,将用户表和权限表都统一迁移到用户库中,这一步没啥问题。但在处理spring security相关代码,发现不能直接迁移到用户服务中,而几个web项目中的spring security代码都是类似的模板代码。关于这种情况,请问老师有什么好的解决思路?展开
作者回复: 你好,库嚓嚓,这个组件我没有研究过,我的大部分业务相关的服务都是自制,我理解需要看看官方架构相关文档了
- Geek_lucas2022-11-09 来自北京你思考一下,用户邀请其他用户注册的记录,属于历史记录还是关系记录? 从关系来说,这是关系记录,然而从业务上来说,这个其实很少会去查询的,应用场景也不是很多的,所以应该归属到‘历史记录’,毕竟用的少。你们觉得呢?
作者回复: 你好,lucas,没错~取决于怎么用,但是怎么保证别人按我们设计初衷使用
- Geek_96685a2022-11-07 来自北京看业务场景吧: 1,如果需要查看邀请人的邀请记录,邀请成功或者邀请未成功的记录,此时需要设计为邀请记录表吧 2,如果不需要查看邀请人记录,只关注成功邀请的话,而且一个人只能被邀请一次的话,可以设计为关系表
作者回复: 你好,很高兴收到你的思考,没错~同时还要注意,这个数据不会被别人滥用
- Mr.Tree2022-11-07 来自北京这个不同的业务需求划分归属就会不同吧,比如邀请记录不会发生变化且数据量庞大,对于历史记录访问较少,这些都属于冷数据,它应该作为历史记录,如果属于访问较平凡的数据,考虑一下分区,并且储存较少的字段,统计邀请人id和被邀请人id等少量信息,作为关系表存在
作者回复: 你好,很高兴收到你的思考,这个思考没错,核心在于我们怎么用它,但是我们的数据放在哪里,别人怎么用是不好把控的,如何防止别人滥用这些数据,是我们需要思考的
- 张申傲2022-10-29 来自北京对数据库表进行分类的思想很有启发,学习了~
作者回复: 你好,张申傲,感谢你的留言~
- frag0072022-10-26 来自北京是关系还是历史记录,还是要看具体业务决定的。如果有需求是查询推荐人是xx的话,就是关系了
作者回复: 你好,frag007,很高兴收到你的留言,首先,你的理解没错。 如果这个表里可以有多人邀请一个人,历史的职能就会十分明显~,这样的话给他放缓存会很不方便,所以,还是业务形态决定了他的职能,这也意味着这个表的数据决定了这个表的职能,而不是单单的结构就能看出来
- 徐曙辉2022-10-26 来自北京1. 为什么时间字段是int格式不是timestamp或datetime? 2. 历史记录,当我们在查询用户A和邀请人关系的时候其实也可以看作关系记录,邀请表有两个字段用户user_id和邀请人用户ID invite_user_id,如果需要查询用户的邀请人要么通过invite_user_id 联用户表,要么增加冗余字段invite_user_XXX等,当然可以选择把一部分用户信息缓存到redis,拿invite_user_id去缓存查
作者回复: 你好,徐曙辉,很高兴收到你的回复 1.这个和个人习惯有关,我个人喜欢用unix time,因为这个不带时区,不同服务器环境时区不同容易乱 2.我们排斥历史表是因为他数据量太多,查询缓慢,所以虽然我们用它当作关系表也可以,但是不太好用它做缓存,最后一句拆一部分进入redis是个很好的办法,同时要考虑下第一次查询很缓慢,然后才能放入缓存缓解压力