当前位置:首页 » 服务存储 » 新一代微信后台数据存储架构设计
扩展阅读
webinf下怎么引入js 2023-08-31 21:54:13
堡垒机怎么打开web 2023-08-31 21:54:11

新一代微信后台数据存储架构设计

发布时间: 2022-11-13 11:02:43

1. 微信第三方平台 后台mysql 用户表怎么设计

说起用户表,大概是每个应用/网站立项动工(码农们)考虑的第一件事情。用户表结构的设计,算是整个后台架构的基石。如果基石不稳,待到后面需求跟进了发现不能应付,回过头来反复修改用户表,要大大小小作改动的地方也不少。与其如此,不妨设计用户表之初就考虑可拓展性,争取不需要太多额外代价的情况下一步到位。
先前设计:
id
username
password
用户名加上密码,解决简单需求,留个id作为其他表的外键。当然,那时候密码还可能是明文存储,好点的知道md5。
后来呢,随着业务需求的拓展,要加个用户状态 status 判断用户是否被封禁,注册时间和注册IP地址、上次登录时间和IP地址备查(并衍生出登录记录表,用来判断是否异地登录等,在此不表),用户角色/权限 role (又衍生出用户角色权限关系,还是另文讨论),业务也需要个人的个人信息如真实姓名、地址等也一股脑往上添加,现在形成了一个很完整的用户关系表。

2. 关于微信的架构问题

最初的方案是客户端记录一个本地数据的快照(Snapshot),需要同步数据时,将Snapshot带到服务器,服务器通过计算Snapshot与服务器数据的差异,将差异数据发给客户端,客户端再保存差异数据完成同步。不过这个方案有两个问题:一是Snapshot会随着客户端数据的增多变得越来越大,同步时流量开销大;二是客户端每次同步都要计算Snapshot,会带来额外的性能开销和实现复杂度。

几经讨论后,方案改为由服务计算Snapshot,在客户端同步数据时跟随数据一起下发给客户端,客户端无需理解Snapshot,只需存储起来,在下次数据同步数据时带上即可。同时,Snapshot被设计得非常精简,是若干个Key-Value的组合,Key代表数据的类型,Value代表给到客户端的数据的最新版本号。Key有三个,分别代表:帐户数据、联系人和消息。这个同步协议的一个额外好处是客户端同步完数据后,不需要额外的ACK协议来确认数据收取成功,同样可以保证不会丢数据:只要客户端拿最新的Snapshot到服务器做数据同步,服务器即可确认上次数据已经成功同步完成,可以执行后续操作,例如清除暂存在服务的消息等等。

此后,精简方案、减少流量开销、尽量由服务器完成较复杂的业务逻辑、降低客户端实现的复杂度就作为重要的指导原则,持续影响着后续的微信设计开发。记得有个比较经典的案例是:我们在微信1.2版实现了群聊功能,但为了保证新旧版客户端间的群聊体验,我们通过服务器适配,让1.0版客户端也能参与群聊。

3. 定型了后台架构



图 2 微信后台系统架构

微信后台使用三层架构:接入层、逻辑层和存储层。

接入层提供接入服务,包括长连接入服务和短连接入服务。长连接入服务同时支持客户端主动发起请求和服务器主动发起推送;短连接入服务则只支持客户端主动发起请求。

逻辑层包括业务逻辑服务和基础逻辑服务。业务逻辑服务封装了业务逻辑,是后台提供给微信客户端调用的API。基础逻辑服务则抽象了更底层和通用的业务逻辑,提供给业务逻辑服务访问

存储层包括数据访问服务和数据存储服务。数据存储服务通过MySQL和SDB(广硏早期后台中广泛使用的Key-Table数据存储系统)等底层存储系统来持久化用户数据。数据访问服务适配并路由数据访问请求到不同的底层数据存储服务,面向逻辑层提供结构化的数据服务

3. 微信技术总监谈架构:微信之道——大道至简(演讲全文)

微信——腾讯战略级产品,创造移动互联网增速记录,10个月5000万手机用户,433天之内完成用户数从零到一亿的增长过程,千万级用户同时在线,摇一摇每天次数过亿...在技术架构上,微信是如何做到的?日前,在腾讯大讲堂在中山大学校园宣讲活动上,腾讯广研助理总经理、微信技术总监周颢在两小时的演讲中揭开了微信背后的秘密。

周颢把微信的成功归结于腾讯式的“三位一体”策略:即产品精准、项目敏捷、技术支撑。微信的成功是在三个方面的结合比较好,能够超出绝大多数同行或对手,使得微信走到比较前的位置。所谓产品精准,通俗的讲就是在恰当的时机做了恰当的事,推出了重量级功能,在合适的时间以最符合大家需求的方式推出去。他认为在整个微信的成功中,产品精准占了很大一部分权重。

相关链接

周颢

2001 年毕业于华南理工大学,计算机专业硕士。

2005 年加入腾讯广州研发部,历任 QQ 邮箱架构师,

广研技术总监,T4 技术专家,微信中心助理总经理。

微信研发团队里鼓励一种试错的信仰:他们坚信,在互联网开发里,如果能够有一个团队在更短的时间内尝试了更多机会(并能改进过来),就能有(更多的)机会胜出。敏捷是一种态度,在软件开发过程中,项目管理者都会非常忌讳“变更”这个词,但是在微信的项目运作中是不可以的。因为微信必须要容忍说哪怕在发布前的十分钟,也要允许他变更。这是非常大的挑战,因为打破了所有传统项目开发的常识。所有人都说不可能做到的,但微信做到了。研发团队所做的一切都是要给产品决策者有最大的自由度,而这个决策正是微信能够胜出的关键。

敏捷有很多困境,如果做一个单机版程序,是可以做到很敏捷的,但是腾讯正在运作的是一个海量系统,有千万级用户同时在线,在一个单独的功能上每天有百亿级的访问,同时还要保证99.95%的可用性。在海量系统上应对项目开发会有很严谨的规范,都说要尽可能少的变化,因为90%-95%的错误都是在变更中产生的,如果系统一直不变更会获得非常高的稳定度,但是微信就是要在悬崖边跳舞。微信的研发团队要做一些事情,让敏捷开发变得更简单。

如何做到这一切?周颢认为,首先,必须建立起一种狂热的技术信念,就是一定是可以做到的。然后,需要用一些稳固的技术(理念)来支撑,例如大系统小做、让一切可扩展、必须有基础组件、轻松上线(灰度、灰度、再灰度;精细监控;迅速响应)...等等来支撑。

当设计庞大系统的时候,应该尽量分割成更小的颗粒,使得项目之间的影响是最小的。仅仅把模块变得更为清晰,这在海量系统设计开发中是不够的,还需要在物理环境上进行分离部署,出现问题的时候可以快速发现,并且在最快的情况下解决掉。

大系统小做,混搭模式:

将不同的应用逻辑物理分割独立出来,用户注册登录、LBS逻辑、摇一摇逻辑、漂流瓶逻辑、消息逻辑独立开来。把关键的逻辑混搭在一起,当所有的逻辑部署在同一个服务器上,确实也会带来很大敏捷上的好处,因为不需要额外的考虑部署和监控的问题。在整个微信的逻辑中,可能现在已经有上百种不同的逻辑,因为会在逻辑的分割上拆分成8-10种做分离部署。

在高稳定度、高性能的系统中间,为了稳定性能把它设计成不变化的系统,但为了支持敏捷需要让一切的东西都要变得可以扩展。

扩展的关键点有两块。一个是网络协议需要扩展,当要升级一个新功能的时候,会有一些比较大的困难,所以所有协议设计都比较向前兼容,但是向前兼容还是不够的,因为网络协议设计本身有非常多的功能也会有比较大的字段,相关的代码可能会有数千行,这一块不能通过手写方式完成。可以通过XML描述,再通过工具自动生成所有的代码,这是微信获得快速开发的一个重要的点。

另外一块就是在数据存储方面是必须可扩展的。在2005年绝大多数海量系统的设计都是采用固定字段的存储,但是在现代系统中会意识到这个问题,会采用KV或者TLV的方式,微信也做了不同的设计。

把复杂逻辑都固化下来,成为基础软件。在微信后台会有几种不同的基础组件。大致包括:

在变更后的部署方式上,微信在一些规则会限定不能一次把所有的逻辑变更上去,每一次变更一小点观察到每一个环节没有问题的时候,才能布局到全网上去。微信后台每一天可以支撑超过20个后台变更,在业界来说,通常做到5个已经是比较快了,但是微信可以做到快4倍。

腾讯内部的上线系统:

而所谓灰度发布,是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让一部用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面 来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。(在腾讯,灰度发布是最常采用的发布方式之一)

常识上,解决一个复杂问题的时候,会用高明的技巧解决复杂的问题,这个不是微信团队的目标,他们追求的要做到让所有问题很自然和简单的方式解决掉。在周颢看来,微信架构的技术复杂点在四个要点:协议、容灾、轻重、监控。

微信架构:

在协议设计上,移动互联网和常规互联网有很大的区别。首先有CMWAP和CMNET的不同,在中国现在有相当多的手机用户使用WMWAP连接,还有就是在线和离线的概念,当QQ下线的时候叫离线,当你登录的时候叫在线。但是在移动互联网这两个概念比较模糊。从微信的设计中,不管在线还是离线系统表现都应该是一致的。还有一个是连接不稳定的问题,由于手机信号强弱的变化,当时信号很好,5秒钟走到信号不好的地区,连接就必须断掉。这个中间带来不稳定的因素为协议设计带来较大困难。此外就是资费敏感的问题,因为移动互联网是按照流量计费的,这个计费会使得在协议设计中如何最小化传输的问题。最后就是高延迟的问题。

对此,业界标准的解决方案:Messaging And Presence Protocol:1)XMPP;2)SIP/SIMPLE。它的优点是简单,大量开源实现。而缺点同样明显:1)流量大:状态初始化;2)消息不可靠。

微信在系统中做了特殊设计,叫SYNC协议,是参考Activesyec来实现的。特点首先是基于状态同步的协议,假定说收发消息本身是状态同步的过程,假定终端和服务器状态已经被迟了,在服务器端收到最新的消息,当客户端、终端向服务器对接的时候,收取消息的过程实际上可以简单的归纳为状态同步的过程,收消息以及收取你好友状态更新都是相同的。在这样的模式之下,我们会也许会把交互的模式统一化,只需要推送一个消息到达的通知就可以了,终端收到这个通知就来做消息的同步。在这样的简化模式之下,安卓和塞班都可以得到统一。这样的系统本身的实现是更为复杂的,但是获得很多额外的好处。

让剩下系统实现的部分更加简单,简化了交互模式,状态同步可以通过状态同步的差值获得最小的数据变更,通过增量的传输得到最小的数据传输量。通过这样的协议设计,微信可以确保消息是稳定到达的,而且是按序到达。引用一句俗话:比它炫的没它简单,比它简单的没它快,没谁比他更快,哪怕在GPRS下,微信也能把进度条轻易推到底。

周颢介绍了在微信上具体容灾设计的做法。在所有的容灾中存储层的容灾是最难的,一个系统的设计分为三层:接入层、逻辑层、存储层。接入层和逻辑层的容灾都有比较成熟的方案。逻辑层的容灾相对来说比较简单,尽量不要有状态的设计,比如说当你做上一个请求的时候,会保持一些状态,要使得下一个请求发到下一个服务器。如果任何一个请求之间互相不关联的话,这个就是无状态的设计,只要做到这一点逻辑层的容灾可以随意的切换。在回到存储层本身的容灾设计上,相对来说困难一些,但是微信研发团队采用了一些技巧,叫分而治之,分离业务场景,寻求简单的设计,并不会寻求大而同一的解决方案,因为这样会使得系统的复杂度大幅度上升,而微信会尽可能把产品拆细,寻求简化的设计。

首先是主备容灾,这是最常见的方案。在有一些业务场景中是可以容忍最终一致性的,比如账号系统的设计,每天写入账号系统的请求是非常少的,但是访问的请求非常多,这个差异可能会达到数万倍的规模,在这样的场景下,微信会在账号系统中采用简化的方案,也可以获得比较大的稳定度。

SET模型+双写:

第二种容灾的模式叫双写,两台Master的机器,当一台机故障的时候,另外一台机还是可以接收到写请求,当两台机交错启动的时候,会得到数据的丢失。但是有一些场景是可以容忍轻度数据丢失的,比如说会有一个存储专门记录用户终端的类型,比如说安卓还是塞班以及他们使用终端的微信版本是什么,这样的数据是可以容忍轻度数据丢失的,因为偶尔有一些丢失的话,下一次访问会把这些数据带上来,会尽快的修复所有的数据。双写也是非常简单的模式。

微信的研发团队做了一个叫Simple Quorum的机制,在微信的后台中,同步协议有一个很重要的基石叫序列发生器,这样的一个序列发生器需要有极高的稳定度。首先可以看到序列号有一个特点永远是递增的,用递增方式往前推进的时候,最大的序列号就是最新的系列号。有一个毕业才加入广研的毕业生想到一个绝佳的方案,按SET分布,从2G减到200K。

周颢还谈到了轻重的概念。这个概念的提出主要是从终端本身的一些困境所带来的。首先在终端上需要表现最多的一个产品的逻辑,逻辑非常复杂,变更的成本也非常高,当需要修复的时候必须发布一个新版本,这个新版必须由自己下载才能完成,下载的成本非常高。在这样的前提下,如果手机终端产生了任何变化的时候,如果这个变化有非常大的问题就会有极大的困境,所以需要在每一个发布之前做一些充分的数据,确保不会发生致命问题。如果一旦出现致命问题难以修复,需要把关键的点从终端移到后台实现,把功能点后移,来充分发挥后台快速变更的能力。

接入优化:从GSLB到IP重定向

在接入层的优化,速度很重要的因素,是不是能够就近接入一个最优的节点,比如说移动用户最好接入移动的节点,海外的用户可能需要寻找更佳的路由,有的时候可能无法自动做到这一点,一点是在终端上做测速,微信会通过在后台IP逆向的能力,通过后台指挥微信终端联网的能力,寻找最优的接入点。上图就是每分钟收到同一项指令曲线的报表。

如何解决“偷流量”的问题 ——当国内类微信类产品发布的时候出现一个大的问题就是“偷流量”,当用户在某一些逻辑下进行一个死循环,不断访问某一些数据,这样的死循环是非常可怕的,如果在用户不知觉的情况之下,可能会在一个小时之内偷到数10兆甚至数百兆的流量。有非常多业内的同行都需要花大量的精力解决这个问题,微信研发团队用了非常强大的方式解决它。通过在后台建立起严厉的监控系统,对每一个用户的行为做一个监控,当发现异常的时候,后台会给终端发出指令,使得微信终端在一段时间无法联网,但是可以保证用户流量不会白白的使用掉。

功能适配的例子 ——第一期微信版本发布的时候,当时没有群聊的功能,第二版发布的时候做了这个功能。当时有两个选择,对于早期版本的用户,因为不支持群聊,就无法享用到这个功能,但是微信希望提供更好的选择,想让早期不支持群聊的版本,也可以被拉到一个群里面收消息、发消息,通过后台功能的适配也能做到这个事情。

对于一个海量系统来说,一个精密的仪表盘非常重要。监控是非常痛苦的,对于这样一个系统来说,每小时会产生数百G的监控日志。微信希望在1分钟之内监控的数据就能够显示在报表上,因为只有这样的精准和实时度才能够赢得处理故障的时间。微信会做关联统计,通过摇一摇加了好友,他们活跃度如何,过了一段时间他们的活跃度变化情况又是如何。这种需求是需要通过大量日志的关联统计来获得的。研发团队也花了一段时间来理解这个问题,发现了中间一个重要的经验叫做“鱼和熊掌不能兼得”。

为了让监控数值更敏感,需要把监控细化再细化,上面数据表示每一栏子系统的数据,下面这个是按微信版本号来划分的,这里的数据项是非常多。

微信还需要采集一些异常的点,如果有异常的话会发布紧急的版本,尽可能快的替换它。对收发消息延时做的监控,比如说0—1秒端到端的速度,会对不同的区段做一些统计,当某一个环节出现异常的时候,通常会在中间的延时上体现出来。有一个很重要的点叫自动报警,现在有数千项的数据,不可能每一项都靠人工去看的,必须要跟自动报警相关联,微信有一些智能的算法,是不是在正常的范围内,跟 历史 的数值进行对比,如果有异常的话,会通过短信、邮件还有微信本身来发出报警信息。

微信会把监控嵌入到基础框架里面去,因为并不是每一个人都会意识到在需要的地方嵌入一个监控点,所以在基础框架本身内置很重要的监控点,比如说这个表上的栏目,非常多的栏目大概会有数百项的栏目,都不需要程序员自己去写,当用基础组件搭建一个系统的时候,就可以直接观测系统数据。

在谈到微信未来的技术挑战时,周颢首先希望能够让微信成为可用性99.99%的系统;设计出面向现在10倍容量的系统以及完全的IDC容灾。

网上盛传的凌晨两点,腾讯大厦那多层大片大片的灯光和楼下那长长的出租车队伍说明了一切。引用一句话做结尾:“可怕的不是微信,真正可怕的是,比你领先比你更有天赋的团队比你更努力”。

4. 微信后台如何做的过载保护

首先要谈到架构,微信后台系统的模型大致理解为三层架构,接入层(proxy)、逻辑层(CGI)、存储层(后台模块)。啥意思呢?由接入层mmproxy调用逻辑层的CGI,比如摇一摇或者发消息的CGI,逻辑层再和存储层通信,比如存取账户信息,文件,地址信息等等。这是常规主流的架构没有什么特别需要说明的了,这边采用的是微服务架构,通信使用RPC框架,调用下级模块过多,就有 高扇出 问题。

我们先来理一下过载可能导致的一些现象。假设后台模块系统极限请求量是60万,我们知道当实际请求量超过这个数值就会过载,那么过载会发生什么情况?是不是发送了70万请求过来,只有60万之外的那10万请求不能被处理?答案显然不是,实际情况要比这个糟很多。很显然系统一旦过载,可能就只能正常处理20万的请求,原来的60万极限值处理能力是保不住的,甚至还有可能雪崩,直至毫无处理能力。当然,这里面的原因有很多,比如无效响应,失败重试,级联传递等等。

假如一时间好多人发送了摇一摇请求,这些请求都依次入队准备去调用寻找同摇好友的CGI并等待响应结果。但是队列很长,假设这些请求的超时时间是5秒,等待时间如果6秒过长就会导致RPC超时,一旦超时调用方可能直接关闭不再等待响应,等队列排到后执行完逻辑返回的响应调用方此时并不接收也等于是属于无效的响应了。来举个栗子,春运快到了,你挤在车站排队买车票,假设最后一班发车只有60分钟了,窝了个大叉,当你排队了半天到达售票窗口时已经用时80分钟了,这时候售票员再给你吐出一张车票已经毫无意义了,车都跑了。这也叫“无效输出”。

当然除了大部分无效的响应处理耗走了资源,别忘了还有失败重试。一般RPC框架都会设置失败重试机制,当请求得不到响应调用端会重试,如果一时间失败过多将会导致短时间内大量重试请求的发起,导致进一步过载,无疑是对本已经并无富裕的系统负载雪上加霜,滚雪球效应。

由于采用微服务架构,各模块之间的调用相互影响。由于服务依赖的复杂性当前模块的雪崩会导致上游模块的变慢,性能下降,如果此时上游模块也开始过载,继续向上级联,从而导致整个系统崩溃。怎么理解呢?假设一个餐厅里的2个厨师,A桌定了2个大菜烹饪时间很长,那么就会影响传菜员的上菜速度,传菜员要等待,假设全店传菜员有限只有2个都在等厨师出A桌的2个菜后才能去服务其他桌,这个时候就影响了整个餐厅的顾客的用餐速度,最终可能导致好几桌的顾客都吃不上菜且骂骂咧咧跑了。其实这个在Hystrix里采用了资源隔离来解决这个问题,即一个传菜员只服务于一桌,A服务员只服务A桌,B服务B桌,A慢的话就A这一桌慢,B桌的传菜员只传他B桌的菜哪怕闲着玩手机也不理其他桌。

谈完原因谈解决方法。该怎么处理呢?其实Hystrix的这套思想很好了,基本思想核心也相似,但今天主要是谈微信后台实践的解决方案。

常见的过载保护方法

1.减少需求:调用端的请求限速

2.避免响应超时:根据调用端超时,设置T超时时间;控制队列长度,容纳T超时时间内的请求。

调用端限速

限多了,服务器能力没有充分利用,限少了,过载问题没解决。而且过载时,响应时间会阶梯式上涨,这样超时时间就会 波动 ,队列长度难设定。虽然极限能力 绝对值 比较难找,但服务器最佳负载点容易找。所以过载保护的目标:尽量让服务端维持在最佳负载点, 让有效输出接近最佳吞吐 。

古希腊水钟

那如何维持在最佳负载点呢?水钟的启示:反馈控制系统。

如图,图中的浮子上浮,上水箱下水减速,相反如果下沉则加速上水箱下水。浮子起到了稳定液位的作用,使得中水箱的水位相对稳定,往下水箱的滴水速率自然也相对稳定,使得计时相对准确。

那么OK啦,也就搞一个像浮子一样的机制,多了及早拒绝,降低通过率,少了缓慢提升通过率。

1、通过反馈动态调整通过率:及早拒绝

根据CPU使用率和队列等待时间,控制输入,这里有个算法FastReject V1计算的是 通过率 ( 过载时 按一定概率通过请求),算法公式暂不展示,主要讲究的是快降慢升。触发降低通过率的场景比如有:比如每隔一秒统计一下队列等待时间,超过n豪秒就降低通过率,或者当前CPU使用率超过阈值(比如85%)也降低通过率。及时反馈控制,不让队列等很久等到轮到自己被处理后前端已经关闭了请求,导致无效的响应。这样请求要么超载被直接拒绝,要么是有效响应,保持原来的处理能力,不让过载雪崩。这就是出队时再判断超时还是入队时就判断的区别。记得这个在Hystrix限流熔断里叫做“降级返回”。

2、同时计算业务的优先级权重:优先服务重要业务

这有点类似早高峰的公交专用道了,由公交车优先通行。实现其实也比较简单,为每个业务(CGI)指定一个优先级(这个优先级可以由后台根据业务实际配置),每个请求带个是属于什么业务的标识,FastReject对每个业务优先级维持一个通过率,低优先级通过率降为0,开始拒绝更高优先级。比如聊天消息发送的优先级为1,上传文件为2,数字越小优先级越高。当上传文件的通过率降为0后开始降聊天消息的通过率,这样保证了优先服务重要业务。这个在Hystrix里类似服务的“优雅降级”。

这里的优先级信息传递用的是RPC COOKIE。因为RPC框架支持cookie,那就刚好借用类似http cookie 的思想,请求/响应包预留cookie 字段,请求携带cookie跨模块自动传递。比如CGI_ID标识或者下面分组提到的用户ID。

3、用户分组:保证要不过第一次就不过

注意了,这里的内容大家可能会比较感兴趣。这里要从刚刚上面的那个优先级说起。我们知道,微服务架构各模块之间的调用相互影响,如果一个请求服务端需要请求多个模块才能处理完逻辑返回结果。那假如说请求R需要经过A模块请求B模块,B模块再请求C,B再请求D等等(注意这里B需要同时请求C、D两个模块获取结果),那么如果请求通过了A模块的通过率,C模块的通过率,到D模块没通过,那么前面的处理是不是都白费了,又违背了我们之前所做的及时拒绝无效响应的原则。那怎么解决呢?这里采用了用户分组的方式。举个例子,比如现在根据QQ号对用户进行分组,从0-9取尾号散列分为10组。QQ号为123的请求过来,首先用户是被分到3组的,如果在C模块被拒绝,那么该用户乃至该组的其他用户直接在D模块或者后续E,F等模块的调用都会被全部拒绝,这就是按组拒绝。保证一个用户要不过第一次请求就不过,不会说在聊天聊一半发不了消息了或者消息在中途某个服务才开始失败。 用户分组思想类似单双号限行。

这里有个有趣的事情,比如某用户被分到3组,3组被拒,那么他是不是每次过来都是被拒的,这要是抢红包那你不是会直呼“不公平”。所以这里做个小调整,如果这里每隔一段时间变换散列算法,前一分钟123可能是3组,后面可能是7组,这样来解决同一个用户每次来都被拒的情况,而其他用户都不会被拒的情况,也算是公平了。

4、让调用端也带FastReject接收服务端反馈:服务不行调用端就直接不发起请求了

这一点也很好理解,RPC将每次返回信息也反馈给调用端,调用端在调RPC前先自我检查,看看服务是不是已经不行了,如果不行了就干脆直接不发起请求过来了,避免请求过多。这一点有点类似Hystrix的熔断器机制,接收反馈并自动作熔断操作,服务降级。

总结:实际上,这和Hystrix思想其实大同小异,可以看下 《通俗一点讲“限流熔断之Hystrix”》 就会发现太多的异曲同工之妙。就到这里,说好的浅谈辄止的,感谢品阅~~。

恭祝大家新年快乐!万事大吉!

相关文献:

《Overload control for scaling WeChat microservices》

《10亿红包从天而降,揭秘微信摇一摇背后的技术细节》

5. 微信公众号的运营大数据分析

微信公众号的运营大数据分析

微信运营,到底是什么鬼东西?周末约了几个朋友聊天,大家讨论微信怎么做,目前大部分都处于迷茫状态,策划好的话题,设计、编辑、发布,然后没人看,然后坚持了大半个月,然后仰天长叹:“滚犊子,微信”。

经过大半年的研究,总结了一些后台数据,给大家分析一下,如何有效利用微信后台数据,有预谋有组织的做微信运营。

第一部分:用户增长来源分析

从上图可以明显的看出,微信用户的增长主要来源于“搜索公众号”和其他,我们先搞清楚这些指标的具体含义。

搜索微信公众号的名称:指通过搜索微信公众号的名称获得关注,比如搜索“人和网”这个名字,在搜索的时候,一直排在第一名,进行外部推广的时候,用户很容易通过搜索找到你。所以取个简单有联想的名字更容易让用户记住,认证比非认证更容易获得用户关注。

其他:大部分账号的粉丝来源,都是“其他”类最多,很多人搞不明白其他是什么,一般包括3个渠道,

1、图文消息标题下蓝色链接。

图文标题下蓝色链接

2、微信公众号二维码:微信可以长按识别二维码大大促使了这个渠道的用户来源,可惜的是目前只有微信可以做到。

3、广点通系统推广:付费推广的一种,据说目前加粉的成本1.2左右,比活动的性价比要高了。

搜索微信号,因此微信号要足够简洁容易让用户记住,在外部推广的时候用户方便搜索。一般搜索微信ID的占比不是很高,大概也就8%左右,这是一个很奇怪的数据,大部分做推广的时候留下的是微信号,但是用户来源的时候更多是通过公众号名称搜索,可以看出,其实用户对于资讯网站或者社区看到的企业推广信息更多选择公众号名称搜索而不是微信号搜索。

图文消息右上角菜单,这个关注按钮隐藏较深,很多人不知道阅读文章时的右上角按钮里还隐藏了这么多功能,而且需要经过2步才能到公众号介绍页,最坑爹的是这个按钮不是在所有阅读的情况下都会出现,所以后台通过这个关注的几乎为零,也不知道哪些用户习惯这种操作。

名片分享,直接的名片分享,一般是用户通过分享给好友或者朋友圈微群,这个数据占比越高,说明这个号的质量越好,大家愿意主动分享传播。

第二部分:图文阅读分析

图文阅读分析主要包含7个指标:图文页阅读人数、图文页阅读次数、原文页阅读人数、原文页阅读次数、分享转发人数、分享转发次数、微信收藏人数;

分析数据首先需要了解这些指标的含义:

图文页阅读人数:指你发的那条图文消息,有多少人看过。

原文页阅读人数:指的是你添加的原文链接有多少人看过。如果没有加,那么原文页阅读人数就显示为0,更多用于活动的链接宣传,根据统计,一般文章的原文链接点击率非常低。

这里重点看下图文页阅读人数来源,微信后台提供了5个来源渠道:会话、好友转发、朋友圈、腾讯微博、历史消息。

会话:指通过你推送的消息(会话窗口)查看到你的内容,复制链接发送给好友等等。

好友转发:通过转发直接分享给好友,多见于好文,干货,同行之间或者好友之间乐意分享。

朋友圈:这个不用说了,大家都非常熟悉。

腾讯微博:用腾讯微博的不多,所以这个渠道来源少之又少。

历史消息:微信阅读历史文章率不是很高,一般用户更多通过收藏去阅读你的历史文章。

其他:以上四种以外的都是其他,具体怎么来的,其实我也不清楚,反正数据不是很大, 所以参考意义也不是很大。

以人和网公众号一篇10万+阅读文章为例,看下用户的阅读来源:

文章阅读量主要来源于用户分享后的朋友圈,标题影响用户打开率,但是无法保证足够多的阅读量,转发才是阅读增长的核心。所以,微信的运营最终还是回到内容的价值。

做好内容,拓展分享渠道,才是获得用户的重点之策。

第三部分:用户属性分析

微信后台提供了性别、语言、省份、城市、终端、机型。这部分根据你针对的用户不同主要起到参考作用,也就是你的推广所获得粉丝是否是你的想要的。

以人和网为例:

从前十的占比情况看,显然符合人和网的人群定位,主要分布与江浙沪北上广,占总用户分布的57.2%.

还有用户机型、性别数据,针对不同的微信做针对性的分析,不同微信的定位人群不一样,在推广以及活动中,需要有效的送达到目标用户,这部分数据就能够提供很多帮助。

第四部分:10万+文章案例分析

10万+阅读文章6月1日的当天各个时间段转发次数和阅读人数,很明显的看出,在晚上20点到24点,用户分享和阅读是直线上升的,也说明这个时间段阅读微信的人数是最多的。

这个表格是具体的时间段转发和阅读人数,我们重点关注下转发阅读比,这个数据的好处在于避开因为累积分享造成阅读量过高造成的数据误差。表格中凌晨1点到2点的时候比例较高,说明这个时间段分享的人虽然少,但是朋友圈阅读率比较高,大部分人已经关机入睡,部分夜猫子还在刷屏看微信,很少的分享可以获得更高用户到达率。这个数据占比比较高的在中午、晚上,尤其是19点以后,都保持在10%以上,这个时间段用户有更多的时间支配刷微信,也是很多公众号推送文章的高峰期。

现实中,很多微信运营者只是一份工作的诉求,所以大部分选择在临近下班的时候推送图文消息,一方面下班了可以及时回家,一方面感觉下班路上看微信的人比较多,其实这是一种错误的想当然,很多人开车、挤地铁挤公交其实看手机的并不一定就是最多的时间段。所以建议微信运营在晚上20点-22点推送比较好,有些人可能觉得不方便,晚上回家还要开电脑推送文章,其实微信已经有手机端服务了,关注微信公众号助手,就可以通过手机推送了。一般我推送文章时间都在晚上20点左右,或者晚些。

第五部分:10万+文章后台数据

大概一周时间阅读量分布数据

最终阅读量

至于这篇文章怎么操作的,可以查看下人和网以前的文章,有分享过,而且粉丝数量很少的情况下做到的10万+阅读。

第六部分:自媒体推广方式

这是其中一次的推广文章详细列表,这样做的好处有:

1、文末可以做公众号的推广。根据上文分析来看,留下公众号名称比微信号效果更好,不同平台的管理办法不一样,这个要根据实际情况具体对待。

2、可以积累一些媒体资源,跟媒体网站编辑搞好关系,可以不断的扩增你的媒体圈和在行业中的影响力。

3、很多微信运营每天在找优质的素材,如果你能提供比较好的文章,他们也会转载。由于我做的公众号已经被邀请原创。所以转载的公众号排版推广信息都无法修改,有着很好的传播效果。不过根据最近的数据来看,加粉效果不是很好。

部分文章被转载的情况,有些大号进行了转发, 阅读量也都还不错,不过对加粉来说效果不是很理想,这部分的用户增长来源渠道为“其他”。

转发公众号的显示效果, 点击人和网会直接跳转到人和网公众号,不过似乎这个点击数据效果不是很好,所以腾讯即使做了原创保护功能,但是转载的对于原公众号产生的效果有限,最多是他们知道“人和网”,至于到底是什么东西,还是不知道。或者感兴趣的用户会通过文章最下方的二维码扫描关注,一个大号帮你转发了原创文章,其实对于一个小号来说,还是非常实惠的,所以运营者一定要在内容上下工夫才是王道。

好的内容一定要通过媒体传播出去,尤其是一些垂直权威性的网站,毕竟很多运营者都是通过这些网站寻找优质的素材,前期公众号的传播有限,其他运营者不可能发现你的优质内容,通过外部权威网站就是最好的方法。

当然这个前提是你的文章是原创,这样别人转载对你才有帮助,否则都白谈。

运营微信号一定要找到方法,总结做过的好的方法的经验,通过数据分析来优化推广方式。

以上是小编为大家分享的关于微信公众号的运营大数据分析的相关内容,更多信息可以关注环球青藤分享更多干货

6. 微信朋友圈的基本数据结构设计是怎么样的

因为规则很简单:任何信息只有发布者本人的好友可见(准确的说是有朋友圈权限的账号可见)。谁发布 ,谁的好友可见 ,无论信息是 话题主体或话题下的评论。
所以,解决方案也应该比较简单。
比如:背后的表可以是 :
表1:以好友关系为管理对象的表,key为用户账号:userid,好友01id,。。。。。。。。
表2:以发布内容为管理对象的表,key为用户账号:userid,发布内容编号,发布时间,具体内容数

7. 【OpenIM转载】万亿级调用系统:微信序列号生成器架构设计及演变

微信在立项之初,就已确立了利用数据版本号实现终端与后台的数据增量同步机制,确保发消息时消息可靠送达对方手机,避免了大量潜在的家庭纠纷。时至今日,微信已经走过第五个年头,这套同步机制仍然在消息收发、朋友圈通知、好友数据更新等需要数据同步的地方发挥着核心的作用。

而在这同步机制的背后,需要一个高可用、高可靠的序列号生成器来产生同步数据用的版本号。这个序列号生成器我们称之为seqsvr,目前已经发展为一个每天万亿级调用的重量级系统,其中每次申请序列号平时调用耗时1ms,99.9%的调用耗时小于3ms,服务部署于数百台4核CPU服务器上。 本文会重点介绍seqsvr的架构核心思想,以及seqsvr随着业务量快速上涨所做的架构演变。

微信服务器端为每一份需要与客户端同步的数据(例如消息)都会赋予一个唯一的、递增的序列号(后文称为sequence),作为这份数据的版本号。 在客户端与服务器端同步的时候,客户端会带上已经同步下去数据的最大版本号,后台会根据客户端最大版本号与服务器端的最大版本号,计算出需要同步的增量数据,返回给客户端。这样不仅保证了客户端与服务器端的数据同步的可靠性,同时也大幅减少了同步时的冗余数据。

这里不用乐观锁机制来生成版本号,而是使用了一个独立的seqsvr来处理序列号操作,一方面因为业务有大量的sequence查询需求——查询已经分配出去的最后一个sequence,而基于seqsvr的查询操作可以做到非常轻量级,避免对存储层的大量IO查询操作;另一方面微信用户的不同种类的数据存在不同的Key-Value系统中,使用统一的序列号有助于避免重复开发,同时业务逻辑可以很方便地判断一个用户的各类数据是否有更新。

从seqsvr申请的、用作数据版本号的sequence,具有两种基本的性质:

举个例子,小明当前申请的sequence为100,那么他下一次申请的sequence,可能为101,也可能是110,总之一定大于之前申请的100。而小红呢,她的sequence与小明的sequence是独立开的,假如她当前申请到的sequence为50,然后期间不管小明申请多少次sequence怎么折腾,都不会影响到她下一次申请到的值(很可能是51)。

这里用了每个用户独立的64位sequence的体系,而不是用一个全局的64位(或更高位)sequence,很大原因是全局唯一的sequence会有非常严重的申请互斥问题,不容易去实现一个高性能高可靠的架构。对微信业务来说,每个用户独立的64位sequence空间已经满足业务要求。

目前sequence用在终端与后台的数据同步外,同时也广泛用于微信后台逻辑层的基础数据一致性cache中,大幅减少逻辑层对存储层的访问。虽然一个用于终端——后台数据同步,一个用于后台cache的一致性保证,场景大不相同。

但我们仔细分析就会发现,两个场景都是利用sequence可靠递增的性质来实现数据的一致性保证,这就要求我们的seqsvr保证分配出去的sequence是稳定递增的,一旦出现回退必然导致各种数据错乱、消息消失;另外,这两个场景都非常普遍,我们在使用微信的时候会不知不觉地对应到这两个场景:小明给小红发消息、小红拉黑小明、小明发一条失恋状态的朋友圈,一次简单的分手背后可能申请了无数次sequence。

微信目前拥有数亿的活跃用户,每时每刻都会有海量sequence申请,这对seqsvr的设计也是个极大的挑战。那么,既要sequence可靠递增,又要能顶住海量的访问,要如何设计seqsvr的架构?我们先从seqsvr的架构原型说起。

不考虑seqsvr的具体架构的话,它应该是一个巨大的64位数组,而我们每一个微信用户,都在这个大数组里独占一格8bytes的空间,这个格子就放着用户已经分配出去的最后一个sequence:cur_seq。每个用户来申请sequence的时候,只需要将用户的cur_seq+=1,保存回数组,并返回给用户。

预分配中间层

任何一件看起来很简单的事,在海量的访问量下都会变得不简单。前文提到,seqsvr需要保证分配出去的sequence递增(数据可靠),还需要满足海量的访问量(每天接近万亿级别的访问)。满足数据可靠的话,我们很容易想到把数据持久化到硬盘,但是按照目前每秒千万级的访问量(~10^7 QPS),基本没有任何硬盘系统能扛住。

后台架构设计很多时候是一门关于权衡的哲学,针对不同的场景去考虑能不能降低某方面的要求,以换取其它方面的提升。仔细考虑我们的需求,我们只要求递增,并没有要求连续,也就是说出现一大段跳跃是允许的(例如分配出的sequence序列:1,2,3,10,100,101)。于是我们实现了一个简单优雅的策略:

请求带来的硬盘IO问题解决了,可以支持服务平稳运行,但该模型还是存在一个问题:重启时要读取大量的max_seq数据加载到内存中。

我们可以简单计算下,以目前uid(用户唯一ID)上限2^32个、一个max_seq 8bytes的空间,数据大小一共为32GB,从硬盘加载需要不少时间。另一方面,出于数据可靠性的考虑,必然需要一个可靠存储系统来保存max_seq数据,重启时通过网络从该可靠存储系统加载数据。如果max_seq数据过大的话,会导致重启时在数据传输花费大量时间,造成一段时间不可服务。

为了解决这个问题,我们引入号段Section的概念,uid相邻的一段用户属于一个号段,而同个号段内的用户共享一个max_seq,这样大幅减少了max_seq数据的大小,同时也降低了IO次数。

目前seqsvr一个Section包含10万个uid,max_seq数据只有300+KB,为我们实现从可靠存储系统读取max_seq数据重启打下基础。

工程实现

工程实现在上面两个策略上做了一些调整,主要是出于数据可靠性及灾难隔离考虑

接下来我们会介绍seqsvr的容灾架构。我们知道,后台系统绝大部分情况下并没有一种唯一的、完美的解决方案,同样的需求在不同的环境背景下甚至有可能演化出两种截然不同的架构。既然架构是多变的,那纯粹讲架构的意义并不是特别大,期间也会讲下seqsvr容灾设计时的一些思考和权衡,希望对大家有所帮助。

seqsvr的容灾模型在五年中进行过一次比较大的重构,提升了可用性、机器利用率等方面。其中不管是重构前还是重构后的架构,seqsvr一直遵循着两条架构设计原则:

这两点都是基于seqsvr可靠性考虑的,毕竟seqsvr是一个与整个微信服务端正常运行息息相关的模块。按照我们对这个世界的认识,系统的复杂度往往是跟可靠性成反比的,想得到一个可靠的系统一个关键点就是要把它做简单。相信大家身边都有一些这样的例子,设计方案里有很多高大上、复杂的东西,同时也总能看到他们在默默地填一些高大上的坑。当然简单的系统不意味着粗制滥造,我们要做的是理出最核心的点,然后在满足这些核心点的基础上,针对性地提出一个足够简单的解决方案。

那么,seqsvr最核心的点是什么呢?每个uid的sequence申请要递增不回退。这里我们发现,如果seqsvr满足这么一个约束:任意时刻任意uid有且仅有一台AllocSvr提供服务,就可以比较容易地实现sequence递增不回退的要求。

但也由于这个约束,多台AllocSvr同时服务同一个号段的多主机模型在这里就不适用了。我们只能采用单点服务的模式,当某台AllocSvr发生服务不可用时,将该机服务的uid段切换到其它机器来实现容灾。这里需要引入一个仲裁服务,探测AllocSvr的服务状态,决定每个uid段由哪台AllocSvr加载。出于可靠性的考虑,仲裁模块并不直接操作AllocSvr,而是将加载配置写到StoreSvr持久化,然后AllocSvr定期访问StoreSvr读取最新的加载配置,决定自己的加载状态

同时,为了避免失联AllocSvr提供错误的服务,返回脏数据,AllocSvr需要跟StoreSvr保持租约。这个租约机制由以下两个条件组成:

这两个条件保证了切换时,新AllocSvr肯定在旧AllocSvr下线后才开始提供服务。但这种租约机制也会造成切换的号段存在小段时间的不可服务,不过由于微信后台逻辑层存在重试机制及异步重试队列,小段时间的不可服务是用户无感知的,而且出现租约失效、切换是小概率事件,整体上是可以接受的。

到此讲了AllocSvr容灾切换的基本原理,接下来会介绍整个seqsvr架构容灾架构的演变

最初版本的seqsvr采用了主机+冷备机容灾模式:全量的uid空间均匀分成N个Section,连续的若干个Section组成了一个Set,每个Set都有一主一备两台AllocSvr。正常情况下只有主机提供服务;在主机出故障时,仲裁服务切换主备,原来的主机下线变成备机,原备机变成主机后加载uid号段提供服务。

可能看到前文的叙述,有些同学已经想到这种容灾架构。一主机一备机的模型设计简单,并且具有不错的可用性——毕竟主备两台机器同时不可用的概率极低,相信很多后台系统也采用了类似的容灾策略。

主备容灾存在一些明显的缺陷,比如备机闲置导致有一半的空闲机器;比如主备切换的时候,备机在瞬间要接受主机所有的请求,容易导致备机过载。既然一主一备容灾存在这样的问题,为什么一开始还要采用这种容灾模型?事实上,架构的选择往往跟当时的背景有关,seqsvr诞生于微信发展初期,也正是微信快速扩张的时候,选择一主一备容灾模型是出于以下的考虑:

前两点好懂,人力、机器都不如时间宝贵。而第三点比较有意思,下面展开讲下

微信后台绝大部分模块使用了一个自研的RPC框架,seqsvr也不例外。在这个RPC框架里,调用端读取本地机器的client配置文件,决定去哪台服务端调用。这种模型对于无状态的服务端,是很好用的,也很方便实现容灾。我们可以在client配置文件里面写“对于号段x,可以去SvrA、SvrB、SvrC三台机器的任意一台访问”,实现三主机容灾。

但在seqsvr里,AllocSvr是预分配中间层,并不是无状态的。而前面我们提到,AllocSvr加载哪些uid号段,是由保存在StoreSvr的加载配置决定的。那么这时候就尴尬了,业务想要申请某个uid的sequence,Client端其实并不清楚具体去哪台AllocSvr访问,client配置文件只会跟它说“AllocSvrA、AllocSvrB…这堆机器的某一台会有你想要的sequence”。换句话讲,原来负责提供服务的AllocSvrA故障,仲裁服务决定由AllocSvrC来替代AllocSvrA提供服务,Client要如何获知这个路由信息的变更?

这时候假如我们的AllocSvr采用了主备容灾模型的话,事情就变得简单多了。我们可以在client配置文件里写:对于某个uid号段,要么是AllocSvrA加载,要么是AllocSvrB加载。Client端发起请求时,尽管Client端并不清楚AllocSvrA和AllocSvrB哪一台真正加载了目标uid号段,但是Client端可以先尝试给其中任意一台AllocSvr发请求,就算这次请求了错误的AllocSvr,那么就知道另外一台是正确的AllocSvr,再发起一次请求即可。

也就是说,对于主备容灾模型,最多也只会浪费一次的试探请求来确定AllocSvr的服务状态,额外消耗少,编码也简单。可是,如果Svr端采用了其它复杂的容灾策略,那么基于静态配置的框架就很难去确定Svr端的服务状态:Svr发生状态变更,Client端无法确定应该向哪台Svr发起请求。这也是为什么一开始选择了主备容灾的原因之一。

在我们的实际运营中,容灾1.0架构存在两个重大的不足:

在主备容灾中,Client和AllocSvr需要使用完全一致的配置文件。变更这个配置文件的时候,由于无法实现在同一时间更新给所有的Client和AllocSvr,因此需要非常复杂的人工操作来保证变更的正确性(包括需要使用iptables来做请求转发,具体的详情这里不做展开)。

对于第二个问题,常见的方法是用一致性Hash算法替代主备,一个Set有多台机器,过载机器的请求被分摊到多台机器,容灾效果会更好。在seqsvr中使用类似一致性Hash的容灾策略也是可行的,只要Client端与仲裁服务都使用完全一样的一致性Hash算法,这样Client端可以启发式地去尝试,直到找到正确的AllocSvr。

例如对于某个uid,仲裁服务会优先把它分配到AllocSvrA,如果AllocSvrA挂掉则分配到AllocSvrB,再不行分配到AllocSvrC。那么Client在访问AllocSvr时,按照AllocSvrA -> AllocSvrB -> AllocSvrC的顺序去访问,也能实现容灾的目的。但这种方法仍然没有克服前面主备容灾面临的配置文件变更的问题,运营起来也很麻烦。

最后我们另辟蹊径,采用了一种不同的思路:既然Client端与AllocSvr存在路由状态不一致的问题,那么让AllocSvr把当前的路由状态传递给Client端,打破之前只能根据本地Client配置文件做路由决策的限制,从根本上解决这个问题。

所以在2.0架构中,我们把AllocSvr的路由状态嵌入到Client请求sequence的响应包中,在不带来额外的资源消耗的情况下,实现了Client端与AllocSvr之间的路由状态一致。具体实现方案如下:

seqsvr所有模块使用了统一的路由表,描述了uid号段到AllocSvr的全映射。这份路由表由仲裁服务根据AllocSvr的服务状态生成,写到StoreSvr中,由AllocSvr当作租约读出,最后在业务返回包里旁路给Client端。

把路由表嵌入到取sequence的请求响应包中,那么会引入一个类似“先有鸡还是先有蛋”的哲学命题:没有路由表,怎么知道去哪台AllocSvr取路由表?另外,取sequence是一个超高频的请求,如何避免嵌入路由表带来的带宽消耗?

这里通过在Client端内存缓存路由表以及路由版本号来解决,请求步骤如下:

基于以上的请求步骤,在本地路由表失效的时候,使用少量的重试便可以拉到正确的路由,正常提供服务。

到此把seqsvr的架构设计和演变基本讲完了,正是如此简单优雅的模型,为微信的其它模块提供了一种简单可靠的一致性解决方案,支撑着微信五年来的高速发展,相信在可预见的未来仍然会发挥着重要的作用。

                                                                                                                                          本文系 微信后台团队 ,如有侵犯,请联系我们立即删除

我们的官网及论坛:   OpenIM官网

                                     OpenIM官方论坛

8. 微信朋友圈数据库模式如何设计的

其实微信朋友圈的数据库设计模式无非就是符合了三种设置模式,其中最常用的是第三种。
第一范式(1NF)
在任何一个关系数据库中,第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库。
所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例的信息。例如,对于图3-2 中的员工信息表,不能将员工信息都放在一列中显示,也不能将其中的两列或多列在一列中显示;员工信息表的每一行只表示一个员工的信息,一个员工的信息在表中只出现一次。简而言之,第一范式就是无重复的列。
第二范式(2NF)
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。如图3-2 员工信息表中加上了员工编号(emp_id)列,因为每个员工的员工编号是惟一的,因此每个员工可以被惟一区分。这个惟一属性列被称为主关键字或主键、主码。
第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,第二范式就是非主属性非部分依赖于主关键字。
第三范式(3NF)
满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在图3-2的员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性。

9. 聊天工具包括微信QQ等的服务器后台数据库是用什么数据库什么结构什么方式存储聊天记录的

下文是我猜测的:
持久存储,采用的是mysql
消息中间件 里面使用了很多nosql技术,
请采纳!