当前位置: 首页 > news >正文

分库分表详解

文章目录

    • 分库分表概述
    • 分库分表详解
    • 分库分表的策略
    • 分库分表的注意事项
    • 常用的分库分表中间件
    • mysql单表达到多少数据量需要分库分表
    • 数据库分库分表缺点
    • 分表要停服吗,不停服怎么做

分库分表概述

分库分表是数据库架构设计中的一种常见策略,尤其是在面对大规模数据和高并发场景时。这种策略通过将数据分散存储在多个数据库和表中,来提高系统的性能和可扩展性。
分库分表是为了解决由于数据量过大而导致数据库性能降低的问题,通过将原来独立的数据库拆分成若干数据库组成,将数据大表拆分成若干数据表组成,使得单一数据库、单一数据表的数据量变小,从而达到提升数据库性能的目的。
分库分表是提升数据库性能和扩展性的重要策略,但同时也带来了设计和维护上的复杂性。在具体实施时,需要综合考虑业务需求、数据特性和系统架构,选择最合适的分库分表方案。同时,可以借助分布式中间件来简化实现和管理。

分库分表详解

分库
分库是指将数据分散到多个数据库中,通常有以下几种方式:
垂直分库(Vertical Partitioning):
将数据库按照功能模块或业务划分成多个数据库。例如,一个电商系统可以将用户数据、订单数据、商品数据分别存储在不同的数据库中。
优点:可以减少单个数据库的压力,模块之间相对独立,易于管理和维护。
缺点:跨库查询复杂,事务管理难度增加。
水平分库(Horizontal Partitioning):
将相同结构的数据按照某种规则(如ID范围、哈希值等)分布到多个数据库中。例如,将用户数据按照用户ID进行拆分,ID为1-1000的用户存储在数据库A,ID为1001-2000的用户存储在数据库B。
优点:单个数据库的压力显著降低,易于扩展。
缺点:分片规则设计复杂,跨库查询和事务处理复杂度增加。

分表
分表是指将一个大表拆分成多个小表存储,主要有以下几种方式:
垂直分表:
将表的列进行拆分,不同的列存储在不同的表中。例如,将用户表的基本信息(ID、姓名、邮箱)和详细信息(地址、电话、年龄)分开存储在两个表中。
优点:减少表的宽度,提高查询效率。
缺点:需要进行表的联合查询,复杂度增加。
水平分表:
将表的数据行进行拆分,不同的行存储在不同的表中。例如,将订单表按照订单创建时间进行拆分,2023年的订单存储在orders_2023表中,2024年的订单存储在orders_2024表中。
优点:减少单个表的数据量,提高查询效率。
缺点:需要根据拆分规则进行数据的路由,跨表查询和统计较为复杂。

分库分表方案
水平分库:以字段为依据,按照一定策略(hash、range 等),将一个库中数据拆分到多个库中。
水平分表:以字段为依据,按照一定策略(hash、range 等),将一个表中数据拆分到多个表中。
垂直分库:以表为依据,按照业务归属不同,将不同表拆分到不同库中。
垂直分表:以字段为依据,按照字段活跃性,将表中字段拆到不同表

分库分表的策略

在实际应用中,分库和分表可以结合使用,具体的策略和方案应根据业务需求和数据特点来设计:

  1. 选择合适的分片键:
    分片键是决定数据如何分布的关键。通常选择数据的唯一标识(如用户ID、订单ID)作为分片键。
    分片键应满足均匀分布的要求,避免数据倾斜。
  2. 路由策略:
    数据存储时需要确定其存储的位置,通常通过哈希算法、取模算法等来实现路由。查询时需要根据分片键确定数据所在的库和表。
  3. 事务处理:
    分布式事务管理较为复杂,常用的解决方案包括两阶段提交(2PC)、三阶段提交(3PC)、基于消息队列的最终一致性等。
  4. 分布式中间件
    使用分布式中间件(如Sharding-JDBC、MyCAT等)可以简化分库分表的实现和管理,中间件负责数据的分片、路由和聚合查询。
  5. 数据迁移和扩展
    系统上线后,可能需要进行数据的迁移和扩展,如增加新的数据库和表。这时需要考虑数据迁移策略、数据一致性和系统的停机时间等问题。

分库分表的注意事项

  1. 数据一致性和完整性:在分库分表的过程中,需要确保数据的一致性和完整性,避免出现数据不一致或数据丢失的情况。
  2. 数据安全和可靠性:特别是在高并发访问的情况下,需要采取有效的安全措施来保护数据的安全性和可靠性。
  3. 跨分片事务一致性:当更新内容同时分布在不同库中时,可能会带来跨库事务问题,需要使用分布式事务处理方案。
  4. 跨节点关联查询:分库分表后,数据可能分布在不同的节点上,此时跨节点的关联查询(如JOIN)性能可能会较差,需要尽量避免或优化。
    分库分表的优点
  5. 提升性能:通过分散数据存储和查询负载,提高数据库性能和吞吐量。
  6. 扩展性:可以更容易地扩展数据库系统,以应对不断增长的数据量和查询需求。
  7. 灵活性:可以根据业务需求和数据特点灵活选择分库分表的方式和策略。

常用的分库分表中间件

常用分库分表中间件:
sharding-jdbc(当当)
Mycat
TDDL(淘宝)
Oceanus(58 同城数据库中间件)
vitess(谷歌开发数据库中间件)
Atlas(Qihoo 360)

sharding-jdbc 目前是基于 jdbc 驱动,无需额外的 proxy,因此也无需关注proxy 本身的高可用。
Mycat 是基于 Proxy,它复写了 MySQL 协议,将 Mycat Server 伪装成一个MySQL 数据库,而 Sharding-JDBC 是基于 JDBC 接口的扩展,是以 jar 包的形式提供轻量级服务的
在大规模数据库系统的设计和实现过程中,分库分表是提升系统性能和扩展性的常见策略。为了简化分库分表的实现和管理,业界广泛使用了一些分布式中间件。这些中间件在数据的分片、路由、聚合查询等方面提供了丰富的功能,以下是几种常用的分库分表中间件:

ShardingSphere
ShardingSphere(原名Sharding-JDBC)是Apache基金会的顶级项目之一,主要提供数据库的水平分片、读写分离、分布式事务等功能。
特性:
水平分片:支持按范围、哈希等规则进行分片。
读写分离:自动将读请求路由到从库,提高查询性能。
分布式事务:支持柔性事务(TCC、最大努力通知、最终一致性)和强一致性事务(XA)。
弹性伸缩:支持动态添加或删除分片。
优点:
透明化:对应用程序透明,支持JDBC和Proxy两种接入方式。
高可扩展性:可以与现有的数据库无缝集成,支持多种数据库类型。

MyCAT
MyCAT是一个基于Java开发的开源分布式数据库中间件,具有强大的分布式计算能力,常用于构建大规模、高并发的互联网系统。
特性:
分库分表:支持水平拆分和垂直拆分。
读写分离:支持多种读写分离策略。
分布式事务:支持XA事务和柔性事务。
高可用性:支持故障转移和负载均衡。
优点:
灵活性:配置灵活,支持多种分片规则和路由策略。
性能优异:具备良好的并发处理能力,适用于大规模数据场景。

Vitess
Vitess是一个由YouTube开发并开源的分布式数据库解决方案,专为处理大规模MySQL数据库集群而设计。
特性:
自动分片:支持数据的水平分片和自动分片管理。
读写分离:支持多主多从架构,自动路由读写请求。
容错性:提供自动故障转移和恢复功能。
高可用性:支持滚动升级和在线扩展。
优点:
云原生支持:适用于云环境,支持Kubernetes部署。
兼容性:与MySQL高度兼容,易于迁移和管理。

TiDB
TiDB是PingCAP开发的开源分布式NewSQL数据库,融合了传统关系型数据库和NoSQL数据库的优点,具有良好的扩展性和强一致性。
● 特性:
分布式架构:原生支持水平扩展和高可用性。
强一致性:采用Raft协议保证数据的一致性。
SQL兼容性:兼容MySQL协议和生态,支持复杂查询和事务。
在线弹性伸缩:支持在线扩展和收缩集群规模。
优点:
性能优异:在处理大规模数据和高并发场景下表现出色。
易于运维:提供完善的监控和管理工具,简化集群运维。

Citus

Citus是基于PostgreSQL的分布式数据库扩展,专为扩展和高性能设计,能够处理大规模OLTP和实时分析工作负载。
特性:
数据分片:自动分片数据到多个节点,实现高并发处理。
实时分析:支持高效的实时数据分析。
兼容性:完全兼容PostgreSQL,支持现有应用无缝迁移。
优点:
扩展性强:通过添加节点来扩展存储和计算能力。
灵活查询:支持复杂的SQL查询,适用于多种应用场景。
综上所述,这些分库分表中间件各具特点,适用于不同的业务需求和技术环境。选择合适的中间件需要综合考虑系统的性能要求、数据量规模、运维复杂度以及团队的技术栈和经验。

mysql单表达到多少数据量需要分库分表

理论上确实5000万以上才会性能急剧下降,有些db架构师也是这么说过。但是实际情况却不⼀定,按照⼀部分的真实项⽬,
500万以上就会开始慢了。当然,这个跟具体的业务逻辑、响应要求、表设计也是很有关系的。表字段全是int类型的,可以到1000万以上
再优化。到了300万,是时候作好优化的技术储备了。但是不⼀定分表,可以通过缓存、搜索引擎等技术也都可以提⾼性能。

单表数据量超过1000万或者超过2G
阿里的开发手册中有条建议,单表行数超500万行或者单表容量超过2GB,就推荐分库分表
单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表
数据量太大的话,SQL的查询就会变慢。如果一个查询SQL没命中索引,千百万数据量级别的表可能会拖垮整个数据库。
即使SQL命中了索引,如果表的数据量超过一千万的话,查询也是会明显变慢的。这是因为索引一般是B+树结构,数据千万级别的话,B+树的高度会增高,查询就变慢啦

数据库分库分表缺点

1.事务问题,已经不可以用本地事务了,需要用分布式事务。
2. 跨节点 Join 问题:解决这一问题可以分两次查询实现
3. 跨节点count,order by,group by 以及聚合函数问题:分别在各个节点上得到
结果后在应用程序端进行合并。
4. ID 问题:数据库被切分后,不能再依赖数据库自身主键生成机制啦,最简单可以考虑 UUID
5. 跨分片排序分页问题(后台加大 pagesize 处理?)

分表要停服吗,不停服怎么做

不用。不停服的时候,应该怎么做呢,分五个步骤:

  1. 编写代理层,加个开关(控制访问新DAO 还老 DAO,或者都访问),灰度期间,还访问老DAO。
  2. 发版全量后,开启双写,既在旧表新增和修改,也在新表新增和修改。日志或者临时表记下新表 ID 起始值,旧表中小于这个值数据就存量数据,这批数据就要迁移。
  3. 通过脚本旧表存量数据写入新表。
  4. 停读旧表改读新表,此时新表已经承载了所有读写业务,但这时候不要立刻停写旧表,需要保持双写一段时间。
  5. 当读写新表一段时间之后,如果没有业务问题,就可以停写旧表啦

有时候不推荐使用分库分表的原因

有时候不推荐使用分库分表的原因主要有以下几点:

  1. 架构设计难度大:分库分表需要谨慎选择分布键,并且需要考虑跨库查询、事务处理等问题,这需要仔细的架构设计和规划。如果设计不当,可能导致系统性能下降或数据一致性问题。
  2. 功能缺失和改造成本高:由于各个数据节点各自为政,分库分表模式下的SQL限制多、功能缺失多,这可能导致应用需要付出巨大的改造成本。
  3. 跨库查询性能差:在分库分表系统中,跨库查询需要将数据收到中间件节点再JOIN,这可能导致性能下降,甚至打爆中间节点。
  4. 分布式事务性能差:分库分表系统通常不支持分布式事务,或者性能较差。这可能导致业务受到影响。
  5. 运维难度大:分库分表系统需要额外的运维和管理成本,包括备份、恢复、监控等。
  6. 扩展性限制:分库分表系统的扩展性受到限制,需要预先分配一定的存储空间和计算资源。如果数据量增长超过预期,可能会导致存储空间不足或计算资源不够用的情况。
    综上所述,虽然分库分表可以解决一些问题,但同时也带来了很多额外的复杂性和维护成本。因此,在选择是否使用分库分表时,需要综合考虑系统的实际情况和需求,权衡利弊后做出决策。如果数据量还没有达到一定量级,或者对应用查询等性能没有明显影响,可以考虑不分库分表。

替代解决方案

除了分库分表,还有其他一些解决方案可以处理大量数据和提升数据库性能。

  1. 表设计的优化:在设计表的时候,可以考虑性能问题。例如字段尽量避免NULL,时间类型尽量使用TIMESTAMP,单表的字段不宜过多等等。
  2. 索引的优化:索引不是越多越好,也不是所有的字段都适合建立索引,使用多列索引的时候,要注意SQL中的条件顺序等。
  3. SQL的优化:查询尽量用到索引,避免错误的写法导致索引失效,避免使用select *查询出来所有的列,拆分复杂的SQL语句,查询使用分页等等。
  4. 分区:分区表是独立的逻辑表,底层由多个物理表组成,这些对用户来说是透明的。如果按照分区字段查询数据的话,就会在某一张分区表内查询,速度回比较快。分区字段的选择,需要根据实际业务来。
  5. 读写分离:就是将数据库的读写操作分开,比如让主服务器读,从服务器去做写操作,或者让性能比较好的服务器去做写操作,性能不太好的服务器做读操作。具体如何去读写分离,要看如何去分了。
  6. 静态缓存:分为本地缓存和服务缓存,本地缓存就是将数据加载到本地,服务缓存就是比如使用Redis这样的k-v数据库进行存储热点数据。但是使用服务缓存也有缺点,最常见的问题就是,“击穿”,就是假如缓存都失效了,这时候并发请求都去访问db,此时可能造成服务器挂掉。
    综上所述,除了分库分表外,还有很多其他的解决方案可以处理大量数据和提升数据库性能。需要根据实际情况和需求来选择最合适的方案。

相关文章:

分库分表详解

文章目录 分库分表概述分库分表详解分库分表的策略分库分表的注意事项常用的分库分表中间件mysql单表达到多少数据量需要分库分表数据库分库分表缺点分表要停服吗,不停服怎么做 分库分表概述 分库分表是数据库架构设计中的一种常见策略,尤其是在面对大规…...

【java前端课堂】04_类的继承

类的继承 在Java中,继承是面向对象编程的四大基本特性之一,它允许我们根据一个已有的类来定义一个新的类,这个新的类继承了原有类的特性(属性和方法),并可以添加新的特性或修改原有特性。这样,…...

React nginx配置,一个端口代理多个项目(转发后找不到CSS,JS及图片资源问题解决)

场景: nginx 配置负载均衡,甲方只提供一个端口,一个域名地址 方法: 一个端口一个域名匹配多个应用 方法一: 依靠设备浏览器区分: 使用UserAgent头来识别用户的客户端, CDN监测vary头的信息,如果内容不一致…...

Unity协程详解

什么是协程 协程,即Coroutine(协同程序),就是开启一段和主程序异步执行的逻辑处理,什么是异步执行,异步执行是指程序的执行并不是按照从上往下执行。如果我们学过c语言,我们应该知道&#xff0…...

【iOS】UI学习(二)

目录 前言UIViewContorllerUIViewContorller基础UIViewContorller使用 定时器和视图移动UISwitch控件UIProgressView和UISlider总结 前言 本篇博客是笔者在学习UI部分内容时的成果和遇到的一些问题,既是我自己的学习笔记,也希望对你有帮助~ …...

React路由(React笔记之五)

本文是结合实践中和学习技术文章总结出来的笔记(个人使用),如有雷同纯属正常((✿◠‿◠)) 喜欢的话点个赞,谢谢! React路由介绍 现在前端的项目一般都是SPA单页面应用,不再是以前多个页面多套HTML代码项目了,应用内的跳转不需要刷新页面就能完成页面跳转靠的就是路由系统 R…...

调用讯飞星火API实现图像生成

目录 1. 作者介绍2. 关于理论方面的知识介绍3. 关于实验过程的介绍,完整实验代码,测试结果3.1 API获取3.2 代码解析与运行结果3.2.1 完整代码3.2.2 运行结果 3.3 界面的编写(进阶) 4. 问题分析5. 参考链接 1. 作者介绍 刘来顺&am…...

reduce过滤递归符合条件的数据

图片展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head><…...

Go微服务: 基于rocketmq:5.2.0搭建RocketMQ环境,以及示例参考

概述 参考最新官方文档&#xff1a;https://rocketmq.apache.org/zh/docs/quickStart/03quickstartWithDockercompose以及&#xff1a;https://rocketmq.apache.org/zh/docs/deploymentOperations/04Dashboard综合以上两个文档来搭建环境 搭建RocketMQ环境 1 ) 基于 docker-c…...

Wpf 使用 Prism 开发MyToDo应用程序

MyToDo 是使用 WPF &#xff0c;并且塔配Prism 框架进行开发的项目。项目中进行了前后端分离设计&#xff0c;客户端所有的数据均通过API接口获取。适合新手入门学习WPF以及Prism 框架使用。 首页统计以及点击导航到相关模块功能待办事项增删改查功能备忘录增删改查功能登录注册…...

vue-Dialog 自定义title样式

展示结果 vue代码 <el-dialog :title"title" :visible.sync"classifyOpen" width"500px" :showClose"false" class"aboutDialog"> <el-form :model"classifyForm" :rules"classifyRules">…...

数据库主键设计

文章目录 前言1. 自增ID&#xff08;Auto-Increment&#xff09;2. GUID (Globally Unique Identifier)3. 雪花算法&#xff08;Snowflake&#xff09;处理时钟回拨的方法1. 简单等待2. 配置时钟回拨安全窗口3. 使用不同的机器 ID 小结稳定的雪花算法实现方案示例实现1. 定义雪…...

小熊家务帮day13-day14 门户管理(ES搜索,Canal+MQ同步,索引同步)

目录 1 服务搜索1.1 需求分析1.2 技术方案1.2.1 使用Elasticsearch进行全文检索&#xff08;为什么数据没有那么多还要用ES&#xff1f;&#xff09;1.2.2 索引同步方案1.2.2.1 Canal介绍1.2.2.1 Canal工作原理 1 服务搜索 1.1 需求分析 服务搜索的入口有两处&#xff1a; 在…...

Android8.1高通平台修改默认输入法

需求 安卓8.1 SDK原生的输入法只能打英文, 需要替换成中文输入法. 以高通平台为例, 其它平台也适用. 查看设备当前默认输入法 adb shell settings list secure | grep input 可以看到当前默认是LatinIME这个安卓原生输入法. default_input_methodcom.android.inputmethod.l…...

49. 字母异位词分组

思路&#xff1a;题目的意思是&#xff0c;将所有字母相同的字符串放到一个数组中 解题思路是&#xff1a;使用map,使用排序好的字符串作为key&#xff0c;源字符串作为value,就可以实现所有字母相同的字符串对应一个key vector<vector<string>> groupAnagrams(ve…...

负压实验室设计建设方案

随着全球公共卫生事件的频发&#xff0c;负压实验室的设计和建设在医疗机构中的重要性日益凸显。负压实验室&#xff0c;特别是负压隔离病房&#xff0c;主要用于控制传染性疾病的扩散&#xff0c;保护医护人员和周围环境的安全。广州实验室装修公司中壹联凭借丰富的实验室装修…...

作文笔记10 复述故事

一、梳理内容&#xff08;用表格&#xff0c;示意图&#xff09; 救白蛇 得宝石 救相亲 变石头 人们纪念海力布 二、按顺序&#xff0c;不遗漏主要情节 &#xff08;猎人海力布热心救人&#xff09;救白蛇 得宝石&#xff08;白蛇强调宝石禁忌&#xff09;&#xff08;海力…...

业务安全蓝军测评标准解读—业务安全体系化

目录 1.前言 2.业务蓝军测评标准 2.1 业务安全脆弱性评分(ISVS) 2.2 ISVS评分的参考意义<...

关于焊点检测SJ-BIST)模块实现

关于焊点检测SJ-BIST&#xff09;模块实现 语言 &#xff1a;Verilg HDL 、VHDL EDA工具&#xff1a;ISE、Vivado、Quartus II 关于焊点检测SJ-BIST&#xff09;模块实现一、引言二、焊点检测功能的实现方法&#xff08;1&#xff09; 输入接口&#xff08;2&#xff09; 输出接…...

使用 Logback.xml 配置文件输出日志信息

官方链接&#xff1a;Chapter 3: Configurationhttps://logback.qos.ch/manual/configuration.html 配置使用 logback 的方式有很多种&#xff0c;而使用配置文件是较为简单的一种方式&#xff0c;下述就是简单描述一个 logback 配置文件基本的配置项&#xff1a; 由于 logba…...

Allegro-开店指南

开店指南 Allegro企业账户注册流程 Allegro注册流程分成两个主要阶段: 第一创建您的账户&#xff0c;第二激活您账户的销售功能。完成两个阶段&#xff0c;才能在Allegro进行销售。 中国企业应该入驻Business account&#xff08;企业账户&#xff09;。 第二阶段&#xff…...

Spring AI 第二讲 之 Chat Model API 第二节Ollama Chat

通过 Ollama&#xff0c;您可以在本地运行各种大型语言模型 (LLM)&#xff0c;并从中生成文本。Spring AI 通过 OllamaChatModel 支持 Ollama 文本生成。 先决条件 首先需要在本地计算机上运行 Ollama。请参阅官方 Ollama 项目 README&#xff0c;开始在本地计算机上运行模型…...

服务器环境搭建

服务器的使用。 本地服务器 虚拟机服务器 云服务器。 服务器配置内容 如何实现部署到云服务器&#xff1f; 环境部署是一件费劲的事。 自己一个人坚持慢慢弄&#xff0c;也能行。 但是要是一个组的人&#xff0c;问你怎么弄环境。 可就难了&#xff0c;不同的人部署的环境不同&…...

数仓建模—指标体系指标拆解和选取

数仓建模—指标拆解和选取 第一节指标体系初识介绍了什么是指标体系 第二节指标体系分类分级和评价管理介绍了指标体系管理相关的,也就是指标体系的分级分类 这一节我们看一下指标体系的拆解和指标选取,这里我们先说指标选取,其实在整个企业的数字化建设过程中我们其实最…...

微信小程序如何在公共组件中改变某一个页面的属性值

需求 公共组件A改变页面B的属性isShow的值。 思路 首先目前我不了解可以直接在组件中改变页面的值的方法&#xff0c;所以我通过监听的方式在B页面监听app.js的某一属性值的改变从而改变B页面的值&#xff0c;众所周知app.js的某一属性值是很容易就能更改的。 app.js globa…...

TCP/UDP的区别

首先来介绍一下什么是TCP和UDP TCP&#xff08;传输控制协议&#xff09;和UDP&#xff08;用户数据报协议&#xff09;是互联网协议套件中两个重要的传输层协议。它们在数据传输的方式、可靠性、连接性等方面有显著的区别。 总之他们两个就是个协议&#xff0c;协议也就是数…...

JavaWeb1 Json+BOM+DOM+事件监听

JS对象-Json //Json 字符串转JS对象 var jsObject Json.parse(userStr); //JS对象转JSON字符串 var jsonStr JSON.stringify(jsObject);JS对象-BOM BOM是浏览器对象模型&#xff0c;允许JS与浏览器对话 它包括5个对象&#xff1a;window、document、navigator、screen、hi…...

DSP6657 GPIO中断学习(只支持GPIO0-15)

1 简介 使用创龙板卡的KEY2按键通过中断的方式控制LED3的亮灭 2 中断学习 在C665x设备上&#xff0c;CPU中断是通过C66x CorePac中断控制器进行配置的。该中断控制器允许最多128个系统事件被编程到任意12个CPU可屏蔽中断输入&#xff08;CPUINT4至CPUINT15&#xff09;、CPU…...

vue数字翻盘,翻转效果

数字翻转的效果 实现数字翻转的效果上面为出来的样子 下面为代码&#xff0c;使用的时候直接引入&#xff0c;还有就是把图片的路径自己换成自己或者先用颜色替代&#xff0c;传入num和numlength即可 <template><div v-for"(item, index) in processedNums&quo…...

【简单讲解TalkingData的数据统计】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…...