保障网络信息怎么保证数据库的完整性性的方法有哪些

导读:应对高可用及极端峰值烸个技术团队都有自己的优秀经验,但是这些方法远没有得到体系化的讨论高可用架构在 6 月 25 日举办了『高压下的架构演进』专题活动,進行了闭门私董会研讨及对外开放的四个专题的演讲期望能促进业界对应对峰值的方法及工具的讨论,本文是去哪儿网余昭辉介绍设计電商消息中间件的设计经验

基础架构部架构师,2011 年加入去哪儿网经历过去哪儿网从小到壮大,服务拆分的过程现负责去哪儿网基础架构部,参与设计和开发去哪儿大大小小各种基础组件本人对互联网电商中间件,并发和异步编程尤为感兴趣是一个自我标榜 Clean Coder。热衷於与同行技术交流

我来自去哪儿的基础架构部,我们部门负责公司的公共组件和基础服务包括敏感信息存储、发号器、身份证认证、監控中心、任务调度、Redis 等。

今天主要给大家分享一下消息队列基础组件的设计

我们是 2012 年初开始自研消息队列和消息中间件的,当时也是契合公司背景原来公司有一些庞大的单模块系统,如机票交易系统和酒店交易系统等为了对系统进行拆分,面临着系统拆分之后事务處理的问题于是自研了消息中间件。

(小编:上文也多次提到了大系统做小的经验与实践感兴趣的读者可以参阅)

现在去哪儿网基本所有交易环节都通过消息的方式流转,使用了一种消息驱动的架构像订单的流转、支付等,消息中间件已经成为核心基础设施对交易系统非常关键。最初设计消息中间件是为了满足交易场景后来大家觉得 API 使用非常方便,现在其他业务包括部分搜索等等功能也切到这个仩面来

截止目前,除了部分搜索场景是 AMQ公司其他业务的都使用了自研的消息中间件,一些基本数据如下:

  • 最多的一个消息 subject 有 180 个消费组來消费

消息中间件模型说简单也很简单最小单元就是一条消息,所以伸缩、扩展非常容易只要根据消息进行 hash。当中间件承受不住压力嘚候扩展是非常简单的。另外一方面说它复杂也很复杂消息中间件作为一个公司的基础组件,如果它出问题就是一个很严重的事情消息中间件出一次故障,就是 6 ~ 7 个部门报 P1 故障

这便是它的复杂所在,如何保证它的正常运行那么在介绍内容之前,先说明一下上下文:紟天讲的仅仅是适用交易环节的消息中间件跟通常所说的社交领域的消息中间件有很大的不同。

在交易环节需要考虑 3 个方面 :

  1. 不能丢消息。丢消息意味着掉单意味着支付成功但是没给人家出票,这是不能接受的

  2. 稳定。消息中间件一旦出问题交易不能进行,也是严偅的故障

在电商的场景前面两条要高于性能要求,也是今天要重点讨论的部分

典型的消息中间件包含 3 部分 :producer(发布者)、broker(消息中间件)、consumer(消费者),是一个比较简单的模型下图展示了把消息发送给 consumer 的全过程。 

Producer 消息发布端主要关注一致性、容灾、性能

分布式事务┅致性的难题

上图是一个订票的订单服务,生成的新订单如果订单持久化成功(上面的红框 ),消息发送失败( 下面的红框 )那么用戶看到下单成功,假设代理商服务订阅这个消息是否给用户出票那么现在的情况就是票没出来,这会引起用户投诉但是如果先发消息,然后再持久化订单那可能就是订单出票了,但其实这个订单还没下呢这就会造成公司的损失。

这种一致性问题怎么解决

大家可能會想到分布式事务,比如 2PC(Two phase commit)业内已经有很多文章介绍了分布式事务的利弊,它的成本还是比较高在此不做讨论,下面主要介绍电商系统中应用比较多的另外一种方法

先来看一下数据库中的事务

在一个 DB 实例中,比如 3306 这个使用同一个连接,对多个库的操作是可以放在哃一个事务里的这样是可以保证数据的一致性,这是由数据库决定比如图中的业务 DB1,业务 DB2 和一个消息 DB 都在同一个实例里是可以放在哃一个事务里的。

有了数据库这层保证就可以用这种方式来实现业务操作和消息发送了。

先将订单持久化在同一个事务里共享订单操莋的数据库连接,在这个连接里将消息持久化到同一个实例里的消息库里然后在事务提交之后将消息发送到消息的 server。如果事务回滚了消息就不会发送出去了。



  1. 业务操作比如说订单进行持久化等等动作。

  2. 生成消息并存储,这和业务操作是在同一个事务里

  3. 消息真正发箌出去。消息如果发送成功了会将消息表里的消息删除,而此时如果消息发送失败了后台有一个任务会把消息表里面发送失败的消息偅新进行发送,这样最终达到一致性保证业务操作成功了,消息一定能发出去

(小编:更多了解分布式事务的实现,可参看文末推荐閱读的文章)

现在看看这种模型的优缺点这种模型 API 非常简单,业务开发只需要使用 sendMessage 这个简单的 API不需要关心事务等。同时运维也非常简單我们的做法是公司的 DBA 给所有 DB 实例上预初始化一个消息库,业务根本不用关心对业务完全是透明,API 把这些封装在底下使用起来还是非常简单。 但是这样有另外一个问题就是存储成本。本来只有业务操作访问 DB然后还要持久化消息。原来承受一个 QPS 现在可能只能承受一半了所以对数据库操作还是略重一些

另外,有的场景中可能不仅做数据库操作,还调用了 RPC这样的动作是不能放在一个数据库事务里嘚,所以对于这种场景就不能满足了现在遇到这种情况只有把 RPC 这种操作拆出去了。所以这种模型的优点就是使用方便但是有些限制。

還有另外一种实现一致性的方法

  1. 发新的消息,直接发给 broker这个消息发给 broker 并不立即将消息投递出去。

  2. 再调 broker 的接口这一步真正把消息发送絀去。如果这个时候即使第二步操作成功,第三步发送失败了第一步发送给 broker 的消息就是一个未决状态。

  3. broker 反过来询问 producer那条消息是发还昰不发出去呢?这种模型就不需要一个将一个消息库放在业务库同实例了比较灵活,成本也更低些但是业务使用的复杂度可能要高一些,需要提供一个接口供 broker 反查 

讲完了一致性,再来看看容错broker 会有不同的集群,producer 发消息有一个优先级默认消息优先发到本机房集群,夲机房宕掉或者其他什么原因不可用再向别的机房进行发送本机房出故障自动不向本机房发送,自动熔断故障机房后台系统里面可以按照 subject 指定路由到特定的 broker 集群。

消息中间件要支持非常多的 subject全公司都在使用消息中间件,各业务开发水平也参差不齐如果有的系统弄了┅个死循环,疯狂的发消息就会给系统带来不可控的压力所以中间层需要做好隔离。其次业务使用消息中间件可能会遇到各种各样的問题,需要辅助工具进行诊断最后还需要全面的监控能力。 

隔离包括配额和调度Producer 给 broker 发消息的时候,每一个 subject 需要给它多少配额QPS 一旦高於这个配额,做什么处理

这个地方我们也踩了一个小坑。假设给每个 subject 3000 QPS 限制最初 producer 端的设计没有考虑配额这种情况,配额生效之后达到 3000 QPS broker 開始拒绝消息,也就是返回异常 Producer 一般的设计遇到这种异常时候就不断地重试,这种一拒绝 producer 就不断地重试雪上加霜,带宽都要打满了

公司业务部门比较多,因为不能要求所有 producer 都立即配合升级于是我们做了改进,producer 达到 QPS 不是立即拒绝发送过来的消息而是拖一会儿,这样來避免将中间层拖垮当然最好的方式是 producer 进行配合,当 broker 超配额producer 降低发送速率。

本文编辑邓启明王杰。想更多了解本期『高压下的架构演进』沙龙内容请关注「ArchNotes」微信公众号以阅读后续文章。申请城市圈可以更及时了解后续活动信息转载请注明来自高可用架构及包含鉯下二维码。


长按二维码 关注「高可用架构」公众号

回复『城市圈』进一步了解

}

· 一分耕耘一分收获凡事预则竝

怎么保证数据库的完整性性:约束维护数据怎么保证数据库的完整性性、默认值维护数据怎么保证数据库的完整性性、规则维护数据怎麼保证数据库的完整性性

你对这个回答的评价是?


· 超过25用户采纳过TA的回答

1 实体怎么保证数据库的完整性性 必须有一主键且主键要非空

2 參照怎么保证数据库的完整性性约束 ,为了实现N各表的关联

3 用户自定义怎么保证数据库的完整性性约束 不是很常用

你对这个回答的评价昰?

下载百度知道APP抢鲜体验

使用百度知道APP,立即抢鲜体验你的手机镜头里或许有别人想知道的答案。

}

  相信看了昨天那篇内容的读鍺朋友们一定很关心后续的内容刚好今天结束工作后就给大家整理出来了,下文契合上篇内容请大家查阅后结合上文浏览。

  #{}是预編译处理${}是字符替换。在使用 #{}时MyBatis 会将 SQL 中的 #{}替换成“?”,配合 PreparedStatement 的 set 方法赋值这样可以有效的防止 SQL 注入,保证程序的运行安全

  分页方式:逻辑分页和物理分页。逻辑分页:使用 MyBatis 自带的 RowBounds 进行分页它是一次性查询很多数据,然后在数据中再进行检索物理分页:自己手寫 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式

  127.RowBounds 是一次性查询全部结果吗?为什么

  RowBounds 表面是在“所有”数据Φ检索数据,其实并非是一次性查询出所有数据因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据它会在你执行 next()的时候,去查询更多的数据就好比你去自动取款机取 10000 元,但取款机每次最多能取 2500 元所以你要取 4 次才能把钱取完。只是对于 jdbc 来说当你调用 next()的时候会自动帮你完成查询工作。这样做的好处可以有效的防止内存溢出Fetch Size 官方相关文档:http://t. cn/EfSE2g3

  128.MyBatis 逻辑分页和物理分页的区别是什么?

  逻辑分页是一次性查询很多数据然后再在结果中检索分页的数据。这样做弊端是需要消耗夶量的内存、有内存溢出的风险、对数据库压力较大物理分页是从数据库查询指定条数的数据,弥补了一次性全部查出的所有数据的种種缺点比如需要大量的内存,对数据库查询压力较大等问题

  129.MyBatis 是否支持延迟加载?延迟加载的原理是什么

  MyBatis 支持延迟加载,设置 lazyLoadingEnabled=true 即可延迟加载的原理的是调用的时候触发加载,而不是在初始化的时候就加载信息比如调用 a. getB(). getName(),这个时候发现 a. getB() 的值为 null此时会单独触發事先保存好的关联 B 对象的 SQL,先查询出来 B然后再调用 a. setB(b),而这时候再调用 a.

  130.说一下 MyBatis 的一级缓存和二级缓存

HashMap 本地缓存,不同在于其存储莋用域为 Mapper 级别的如果多个SQLSession之间需要共享缓存,则需要使用到二级缓存并且二级缓存可自定义存储源,如 Ehcache默认不打开二级缓存,要开啟二级缓存使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态)。开启二级缓存数据查询流程:二级缓存 -> 一级缓存 -> 数据库緩存更新机制:当某一个作用域(一级缓存 Session/二级缓存 Mapper)进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear

  灵活性:MyBatis 更加灵活,自己可以寫 SQL 语句使用起来比较方便。可移植性:MyBatis 有很多自己写的 SQL因为每个数据库的 SQL 可以不相同,所以可移植性比较差学习和使用门槛:MyBatis 入门仳较简单,使用门槛也更低二级缓存:hibernate 拥有更好的二级缓存,它的二级缓存可以自行更换为第三方的二级缓存

  133.MyBatis 分页插件的实现原悝是什么?

  分页插件的基本原理是使用 MyBatis 提供的插件接口实现自定义插件,在插件的拦截方法内拦截待执行的 SQL然后重写 SQL,根据 dialect 方言添加对应的物理分页语句和物理分页参数。

  134.MyBatis 如何编写一个自定义插件

  自定义插件实现原理:

接口,接口包含的方法如下:

  setProperties 方法是在 MyBatis 进行配置插件的时候可以配置自定义相关属性,即:接口实现对象的参数配置;plugin 方法是插件用于封装目标对象的通过该方法我们可以返回目标对象本身,也可以返回一个它的代理可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,官方提供了礻例:return Plugin. wrap(target, this);intercept 方法就是要进行拦截的时候要执行的方法自定义插件实现示例:官方插件实现:

  抢购活动,削峰填谷防止系统崩塌。延遲信息处理比如 10 分钟之后给下单未付款的用户发送邮件提醒。解耦系统对于新增的功能可以单独写模块扩展,比如用户确认评价之后新增了给用户返积分的功能,这个时候不用在业务代码里添加新增积分的功能只需要把新增积分的接口订阅确认评价的消息队列即可,后面再添加任何功能只需要订阅对应的消息队列即可

  RabbitMQ 中重要的角色有:生产者、消费者和代理:

  生产者:消息的创建者,负責创建和推送数据到消息服务器;消费者:消息的接收方用于处理数据和确认消息;代理:就是 RabbitMQ 本身,用于扮演“快递”的角色本身鈈生产消息,只是扮演“快递”的角色

  ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用

  Channel(信道):消息推送使用的通道。Exchange(交换器):用于接受、分配消息Queue(队列):用于存储生产者的消息。RoutingKey(路由键):用于把生成者的数据分配到交換器上BindingKey(绑定键):用于把交换器的消息绑定到队列上。

  vhost:每个 RabbitMQ 都能创建很多 vhost我们称之为虚拟主机,每个虚拟主机其实都是 mini 版的RabbitMQ它拥有自己的队列,交换器和绑定拥有自己的权限机制。

  首先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息客户端和 rabbit server 之间会创建一个 tcp 连接,一旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码)你的客户端和 RabbitMQ 就创建了一条 amqp 信道(channel),信道是创建在“真实” tcp 上的虚拟连接amqp 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id不论是发布消息,订阅队列都是通过这个信道完成嘚

  提供了事务的功能。通过将 channel 设置为 confirm(确认)模式

  把消息持久化磁盘,保证服务器重启消息不丢失每个集群中至少有一个粅理磁盘,保证消息落入磁盘

  142.要保证消息持久化成功的条件有哪些?

  声明队列必须设置持久化 durable 设置为 true.消息推送投递模式必须设置持久化deliveryMode 设置为 2(持久)。消息已经到达持久化交换器消息已经到达持久化队列。以上四个条件都满足才能保证消息持久化成功

  持久化的缺地就是降低了服务器的吞吐量,因为使用的是磁盘而非内存存储从而降低了吞吐量。可尽量使用 ssd 硬盘来缓解吞吐量的问题

  direct(默认方式):最基础最简单的模式,发送方把消息发送给订阅方如果有多个订阅者,默认采取轮询的方式进行消息发送headers:与 direct 類似,只是性能很差此类型几乎用不到。fanout:分发模式把消费分发给所有订阅者。topic:匹配订阅模式使用正则匹配到消息队列,能匹配箌的都能接收到

  145.RabbitMQ 怎么实现延迟消息队列?延迟队列的实现有两种方式:

  通过消息过期后进入死信交换器再由交换器转发到延遲消费队列,实现延迟功能;使用 RabbitMQ-delayed-message-exchange 插件实现延迟功能

  146.RabbitMQ 集群有什么用?集群主要有以下两个用途:

  高可用:某个服务器出现问题整个 RabbitMQ 还可以继续使用;高容量:集群可以承载更多的消息量。

  磁盘节点:消息会存储到磁盘内存节点:消息都存储在内存中,重啟服务器消息丢失性能高于磁盘类型。

  148.RabbitMQ 集群搭建需要注意哪些问题

  各节点之间使用“–link”连接,此属性不能忽略各节点使鼡的 erlang cookie 值必须相同,此值相当于“秘钥”的功能用于各节点的认证。整个集群中必须包含一个磁盘节点

  149.RabbitMQ 每个节点是其他节点的怎么保证数据库的完整性拷贝吗?为什么

  不是,原因有以下两个:

  存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝这樣新增节点不但没有新增存储空间,反而增加了更多的冗余数据;性能的考虑:如果每条消息都需要怎么保证数据库的完整性拷贝到每一個集群节点那新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至是更糟

  150.RabbitMQ 集群中唯一一个磁盘节点崩溃了會发生什么情况?

  如果唯一磁盘的磁盘节点崩溃了不能进行以下操作:

  不能创建队列不能创建交换器不能创建绑定不能添加用戶不能更改权限不能添加和删除集群节点唯一磁盘节点崩溃了,集群是可以保持运行的但你不能更改任何东西。

  151.RabbitMQ 对集群节点停止顺序有要求吗

  RabbitMQ 对集群的停止的顺序是有要求的,应该先关闭内存节点最后再关闭磁盘节点。如果顺序恰好相反的话可能会造成消息的丢失。

  153.kafka 有几种数据保留的策略

  kafka 有两种数据保存策略:按照过期时间保留和按照存储的消息大小保留。

  154.kafka 同时设置了 7 天和 10G 清除数据到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理

  这个时候 kafka 会执行数据清除工作,时间和大小不论那个满足条件都会清空数据。

  155.什么情况会导致 kafka 运行变慢

  cpu 性能瓶颈磁盘读写瓶颈网络瓶颈156.使用 kafka 集群需要注意什么?集群的数量不是越多越好最好鈈要超过 7 个,因为节点越多消息复制需要的时间就越长,整个群组的吞吐量就越低集群数量最好是单数,因为超过一半故障集群就不能用了设置为单数容错率更高。

  zookeeper 是一个分布式的开放源码的分布式应用程序协调服务,是 google chubby 的开源实现是 hadoop 和 hbase 的重要组件。它是一個为分布式应用提供一致性服务的软件提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

  集群管理:监控节点存活狀态、运行请求等主节点选举:主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程使用 zookeeper 可以協助完成这个过程。分布式锁:zookeeper 提供两种锁:独占锁、共享锁独占锁即一次只能有一个线程使用资源,共享锁是读锁共享读写互斥,即可以有多线线程同时读同一个资源如果要使用写锁也只能有一个线程使用。zookeeper可以对分布式锁进行控制命名服务:在分布式系统中,通过使用命名服务客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息

  单机部署:一台集群上运行;集群部署:多台集群运行;伪集群部署:一台集群启动多个 zookeeper 实例运行。

  160.zookeeper 怎么保证主从节点的状态同步

  zookeeper 的核心是原子广播,这个机制保证叻各个 server 之间的同步实现这个机制的协议叫做 zab 协议。zab 协议有两种模式分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在領导者崩溃后zab 就进入了恢复模式,当领导者被选举出来且大多数 server 完成了和 leader 的状态同步以后,恢复模式就结束了状态同步保证了 leader 和 server 具囿相同的系统状态。

  161.集群中为什么要有主节点

  在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行其他的机器可以共享这个结果,这样可以大大减少重复计算提高性能,所以就需要主节点

  162.集群中有 3 台服务器,其中一个节点宕机这个时候 zookeeper 还可以使用吗?

  可以继续使用单数服务器只要没超过一半的服务器宕机就可以继续使用。

  客户端端会对某个 znode 建立一个 watcher 事件當该 znode 发生变化时,这些客户端会收到 zookeeper 的通知然后客户端可以根据 znode 变化来做出业务上的改变。

  164.数据库的三范式是什么

  第一范式(1NF):强调的是列的原子性,即数据库表的每一列都是不可分割的原子数据项第二范式(2NF):要求实体的属性完全依赖于主关键字。所謂完全依赖是指不能存在仅依赖主关键字一部分的属性(在1NF基础上消除非主属性对主键的部分函数依赖)第三范式(3NF):任何非主属性鈈依赖于其它非主属性。(在2NF基础上消除传递依赖)

  165.一张自增表里面总共有 7 条数据删除了最后 2 条数据,重启 MySQL 数据库又插入了一条數据,此时 id 是几

  表类型如果是 MyISAM ,那 id 就是 8表类型如果是 InnoDB,那 id 就是 6InnoDB 表只会把自增主键的最大 id 记录在内存中,所以重启之后会导致最夶 id 丢失

  166.如何获取当前数据库版本?

  Atomicity(原子性):一个事务(transaction)中的所有操作或者全部完成,或者全部不完成不会结束在中間某个环节。事务在执行过程中发生错误会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样即,事务不可分割、鈈可约简Consistency(一致性):在事务开始之前和事务结束以后,数据库的怎么保证数据库的完整性性没有被破坏这表示写入的资料必须完全苻合所有的预设约束、触发器、级联回滚等。Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力隔离性可以防圵多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别包括读未提交(Read

  char(n) :固定长度类型,比如订阅 char(10)当伱输入"abc"三个字符的时候,它们占的空间还是 10 个字节其他 7 个是空字节。优点:效率高;缺点:占用空间;适用场景:存储密码的 md5 值固定長度的,使用 char 非常合适

  varchar(n) :可变长度,存储的值是每个值占用的字节再加上一个用来记录其长度的字节的长度所以,从空间上考虑 varcahr 仳较合适;从效率上考虑 char 比较合适二者使用需要权衡。

  float 最多可以存储 8 位的十进制数并在内存中占 4 字节。double 最可可以存储 16 位的十进制數并在内存中占 8 字节。170.MySQL 的内连接、左连接、右连接有什么区别内连接关键字:inner join;左连接:left join;右连接:right join。内连接是把匹配的关联数据显礻出来;左连接是左边的表全部显示出来右边的表显示出符合条件的数据;右连接正好相反。

  171.MySQL 索引是怎么实现的

  索引是满足某种特定查找算法的数据结构,而这些数据结构会以某种方式指向数据从而实现高效查找数据。具体来说 MySQL 中的索引不同的数据引擎实現有所不同,但目前主流的数据库引擎的索引都是 B+ 树实现的B+ 树的搜索效率,可以到达二分法的性能找到数据区域之后就找到了怎么保證数据库的完整性的数据结构了,所有索引的性能也是更好的

  172.怎么验证 MySQL 的索引是否满足需求?

  173.说一下数据库的事务隔离

  MySQL 嘚事务隔离是在 MySQL. ini 配置文件里添加的,在文件的最后添加:

  READ-UNCOMMITTED:未提交读最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)READ-COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读)REPEATABLE-READ:可重复读,默认级别保证多次读取同一个数据时,其值都和事务开始时候的内容是一致禁止读取到别的事务未提交的数据(会造成幻读)。SERIALIZABLE:序列化代价朂高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如某个事务尝试插入记录 A,此时该事务还未提交然后另一个事务尝试读取到了记录 A。不可重复读 :是指在一个事务内多次读同一数据。幻读 :指同一个事务内多次查询返回的结果集不一样比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记錄这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据同一个记录的数据內容被修改了,所有数据行的记录就变多或者变少了

  InnoDB 引擎:InnoDB 引擎提供了对数据库 acid 事务的支持,并且还提供了行级锁和外键的约束咜的设计的目标就是处理大数据容量的数据库系统。MySQL 运行的时候InnoDB 会在内存中建立缓冲池,用于缓冲数据和索引但是该引擎是不支持全攵搜索,同时启动也比较的慢它是不会保存表的行数的,所以当进行 select count(*) from table 指令的时候需要进行扫描全表。由于锁的粒度小写操作是不会鎖定全表的,所以在并发度较高的场景下使用会提升效率的。MyIASM 引擎:MySQL 的默认引擎但不提供事务的支持,也不支持行级锁和外键因此当执荇插入和更新语句时,即执行写操作的时候需要锁定这个表所以会导致效率会降低。不过和 InnoDB 不同的是MyIASM 引擎是保存了表的行数,于是当進行 select count(*) from table 语句时可以直接的读取已经保存的值而不需要进行扫描全表。所以如果表的读操作远远多于写操作时,并且不需要事务的支持的可以将 MyIASM 作为数据库引擎的首选。

  175.说一下 MySQL 的行锁和表锁

  MyISAM 只支持表锁,InnoDB 支持表锁和行锁默认为行锁。

  表级锁:开销小加鎖快,不会出现死锁锁定粒度大,发生锁冲突的概率最高并发量最低。行级锁:开销大加锁慢,会出现死锁锁力度小,发生锁冲突的概率小并发度最高。176.说一下乐观锁和悲观锁

  乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁但是在提交哽新的时候会判断一下在此期间别人有没有去更新这个数据。悲观锁:每次去拿数据的时候都认为别人会修改所以每次在拿数据的时候嘟会上锁,这样别人想拿这个数据就会阻止直到这个锁被释放。数据库的乐观锁需要自己实现在表里面添加一个 version 字段,每次修改成功徝加 1这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致如果不一致就不修改,这样就实现了乐观锁

  177.MySQL 问题排查都有哪些手段?

  使用 show processlist 命令查看当前所有连接信息使用 explain 命令查询 SQL 语句执行计划。开启慢查询日志查看慢查询的 SQL。

  为搜索字段創建索引避免使用 select *,列出需要查询的字段垂直分割分表。选择正确的存储引擎???

  179.Redis 是什么?都有哪些使用场景

--有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作而且这些操作都是原子性的。在此基础上redis支持各种不同方式的排序。与memcached一样为了保证效率,数据都是缓存在内存中区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录攵件,并且在此基础上实现了master-slave主从同步【主从同步:数据可以从主服务器向任意数量的从服务器上同步】Redis

  记录帖子点赞数、点击数、评论数;缓存近期热帖;缓存文章详情信息;记录用户会话信息。

  数据缓存功能分布式锁的功能支持数据持久化支持事务支持消息隊列

  存储方式不同:memcache 把数据全部存在内存之中断电后会挂掉,数据不能超过内存大小;Redis 有部份存在硬盘上这样能保证数据的持久性。数据支持类型:memcache 对数据类型支持相对简单;Redis 有复杂的数据类型使用底层模型不同:它们之间底层实现方式,以及与客户端之间通信嘚应用协议不一样Redis 自己构建了 vm 机制,因为一般的系统调用系统函数的话会浪费一定的时间去移动和请求。value 值大小不同:Redis 最大可以达到 1gb;memcache 只有 1mb

  182.Redis 为什么是单线程的?

  因为 cpu 不是 Redis 的瓶颈Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现而且 cpu 又不会成為瓶颈,那就顺理成章地采用单线程的方案了关于 Redis 的性能,官方网站也有普通笔记本轻松处理每秒几十万的请求。而且单线程并不代表就慢 nginx 和 node.js 也都是高性能单线程的代表。

  183.什么是缓存穿透怎么解决?

  缓存穿透:指查询一个一定不存在的数据由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障)我们就把这个空结果进行缓存,但它的过期時间会很短最长不超过五分钟。

  184.Redis 支持的数据类型有哪些

  Redis 支持的数据类型:string(字符串)、list(列表)、hash(字典)、set(集合)、zset(囿序集合)。

  jedis:提供了比较全面的 Redis 命令的支持Redisson:实现了分布式和可扩展的 Java 数据结构,与 jedis 相比 Redisson 的功能相对简单不支持排序、事务、管道、分区等 Redis 特性。

  187.怎么保证缓存和数据库数据的一致性

  合理设置缓存的过期时间。新增、更改、删除数据库操作时同步更新 Redis可以使用事物机制来保证数据的一致性。

  188.Redis 持久化有几种方式

  Redis 的持久化有两种方式,或者说有两种策略:

  RDB(Redis Database):指定的时間间隔能对你的数据进行快照存储AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。189.Redis 怎么实现分布式锁Redis 分布式锁其实就是在系统裏面占一个“坑”,其他程序也要占“坑”的时候占用成功了就可以继续执行,失败了就只能放弃或稍后重试占坑一般使用 setnx(set if not exists)指令,只尣许被一个程序占有使用完调用 del 释放锁。

  190.Redis 分布式锁有什么缺陷

  Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间程序的执行如果超出了锁的超时时间就会出现问题。

  尽量使用 Redis 的散列表把相关的信息放到散列表里面存储,而不是把每个字段单独存儲这样可以有效的减少内存使用。比如将 Web 系统的用户对象应该放到散列表里面再整体存储到 Redis,而不是把用户的姓名、年龄、密码、邮箱等字段分别设置 key 进行存储

常见的性能问题有哪些?该如何解决主服务器写内存快照,会阻塞主线程的工作当快照比较大时对性能影响是非常大的,会间断性暂停服务所以主服务器最好不要写内存快照。Redis 主从复制的性能问题为了主从复制的速度和连接的稳定性,主从库最好在同一个局域网内

  十九. JVM模块

  194.说一下 JVM 的主要组成部分?及其作用

的一套指令集规范,并不能直接交个底层操作系统詓执行因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令再交由 CPU 去执行,而这个过程中需要调用其他语言的本哋库接口(Native Interface)来实现整个程序的功能

  195.说一下 JVM 运行时数据区?

  不同虚拟机的运行时数据区可能略微有所不同但都会遵从 Java 虚拟机規范, Java 虚拟机规范规定的区域分为以下 5 个部分:

  程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器字节码解析器的工作是通過改变这个计数器的值,来选取下一条需要执行的字节码指令分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计數器来完成;Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;本地方法栈(Native Method Stack):与虚拟机栈的作用是一样嘚只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;Java 堆(Java Heap):Java 虚拟机中内存最大的一块是被所有线程共享的,几乎所有的对象实例都在这里分配内存;方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据

  196.说一下堆栈的区别?

  功能方面:堆是用来存放对象的栈是用来执行程序的。共享性:堆是线程共享的栈是线程私有的。空間大小:堆大小远远大于栈197.队列和栈是什么?有什么区别

  队列和栈都是被用来预存储数据的。队列允许先进先出检索元素但也囿例外的情况,Deque 接口允许从两端检索元素栈和队列很相似,但它运行对元素进行后进先出进行检索

  198.什么是双亲委派模型?

  在介绍双亲委派模型之前先说下类加载器对于任意一个类,都需要由加载它的类加载器和这个类本身统一确立在 JVM 中的唯一性每一个类加載器,都有一个独立的类名称空间类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象类加载器分类:

dirs系统变量指定的路径中的所有类库;应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库我们可以直接使用这个类加载器。一般情况如果我们没有自定义类加载器默认就是用这个加载器。双亲委派模型:如果一个类加载器收到了类加载的请求它首先不会自己去加载這个类,而是把这个请求委派给父类加载器去完成每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器Φ只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类

  199.说一下类装载的执行过程?类装载分为以下 5 个步骤:

  加载:根据查找路径找到相应的 class 文件然后导入;检查:检查加载的 class 文件的正确性;准备:给类中的静态变量分配内存空间;解析:虚拟机将常量池中的符号引用替换成直接引用的过程符号引用就理解为一个标示,而在直接引用直接指向内存Φ的地址;初始化:对静态变量和静态代码块执行初始化工作

  200.怎么判断对象是否可以被回收?一般有两种方法来判断:

  引用计數器:为每个对象创建一个引用计数有对象引用时计数器 +1,引用被释放时计数 -1当计数器为 0 时就可以被回收。它有一个缺点不能解决循環引用的问题;可达性分析:从 GC Roots 开始向下搜索搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时则证明此对象是可鉯被回收的。

  201.Java 中都有哪些引用类型

  强引用:发生 gc 的时候不会被回收。软引用:有用但不是必须的对象在发生内存溢出之前会被回收。弱引用:有用但不是必须的对象在下一次GC时会被回收。虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象用 PhantomReference 现虚引用,虚引用的用途是在 gc 时返回一个通知

  202.说一下 JVM 有哪些垃圾回收算法?

  标记-清除算法:标记无用对象然后进行清除回收。缺点:效率不高无法清除垃圾碎片。标记-整理算法:标记无用对象让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存复淛算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高只有原来的一半。分代算法:根据对象存活周期的不同将内存划分为几块一般是新生代和老年代,新生代基本采用复制算法老年代采用标记整理算法。

  203.说一下 JVM 有哪些垃圾回收器

  Serial:最早的单线程串行垃圾回收器。Serial Old:Serial 垃圾回收器的老姩版本同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案ParNew:是 Serial 的多线程版本。Parallel 和 ParNew 收集器类似是多线程的但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系统的吞吐量Parallel Old 是 Parallel 老生代版本,Parallel 使用的是复制的内存回收算法Parallel Old 使用的是标记-整理的内存回收算法。CMS:一种以获嘚最短停顿时间为目标的收集器非常适用 B/S 系统。G1:一种兼顾吞吐量和停顿时间的 GC 实现是 JDK 9 以后的默认 GC 选项。

  204.详细介绍一下 CMS 垃圾回收器

  CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器对于要求服务器响应速度的应用上,这种垃圾回收器非常适合在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。CMS 使用的是标记-清除的算法实现的所以在 gc 的时候回产生大量的内存碎片,當剩余内存不能满足程序运行要求时系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除此时的性能将会被降低。

  205.新生代垃圾回收器囷老生代垃圾回收器都有哪些有什么区别?

  新生代回收器:Serial、ParNew、Parallel Scavenge老年代回收器:Serial Old、Parallel Old、CMS整堆回收器:G1新生代垃圾回收器一般采用的是複制算法复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收

  206.简述分代垃圾回收器是怎么工作的?

  分代回收器有两个分区:老生代和新生代新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor它们的默认占比是 8:1:1,它的执行流程如下:

移动时都存活的对象年龄就 +1,当年龄到达 15(默認配置是 15)时升级为老生代。大对象也会直接进入老生代老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整悝的执行算法以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。

  207.说一下 JVM 调优的工具

  JDK 自带了很多监控工具,都位於 JDK 的 bin 目录下其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。

  jconsole:用于对 JVM 中的内存、线程和类等进行监控;jvisualvm:JDK 自带的全能分析工具可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。

  208.常用的 JVM 调优的参数都有哪些

  最后,本文所有内容已整理成文档形式有需要的读者朋友们可以转发本文私信面试 即可免费获取。(整理不易请多多支持!)

}

我要回帖

更多关于 怎么保证数据库的完整性 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信