MySQL之复制(十三)

复制

复制的问题和解决方案

在主-主复制结构总写入两台主库

在这里插入图片描述

试图向两台主库写入并不是一个好主意,如果同时还希望安全地写入两台主库,会碰到很多问题,有些问题可以解决,有些则很难。一个专业人员可能需要经历大量的教训才能明白其中的不同。在MySQL5.0中,有两个变量可以用于帮助解决AUTO_INCREMENT自增主键冲突的问题:auto_increment_increment和auto_increment_offset。可以通过设置这两个变量来错开主库和备库生成的数字,这样可以避免自增列的冲突。但是这并不能解决所有由于同时写入两太主库所带来的问题;自增问题只是其中的一小部分。而且这种做法也带来了一些新的问题:

  • 1.很难在复制拓扑间作故障转移
  • 2.由于在数字之间出现间隙,会引起键空间的浪费
  • 3.只有在使用了AUTO_INCREMENT主键的时候才有用。有时候使用AUTO_INCREMENT列作为主键并不总是好主意

你也可以自己来生成不冲突的主键值。一种办法时创建一个多个列的主键,第一列使用服务器ID值。这种办法很好,但却使得主键的值变得更大,会对InnoDB二级索引键值产生多重影响。也可以使用只有一列的主键,在主键的高字节位存储服务器ID。简单地左移法(除法)和加法就可以实现。例如,使用的是无符号BIGINT(64位)的高8位来保存服务器ID,可以按照如下方法在服务器15上插入值11:

mysql>INSERT INTO test(pk_col, ...) VALUE ((15 << 56) + 11, ....)

如果想把结果转换为二进制,并将其填充64位,其效果显而易见:

mysql>SELECT LPAD(CONV(pk_col, 10,2), 64 , '0') FROM test;

该方法的缺点是需要额外的方式来产生键值,因为AUTO_INCREMENT无法做到这一点。不要在INSERT语句中将常量15替换为@@server_id,因为这可能在备库产生不同的结果。还可以使用MD5()或者UUID()等函数来获取伪随机数,但这样做性能可能会很差,因为它们产生的值较大,并且本质上是随机的,这尤其会影响到InnoDB(除非是在应用中产生之,否则不要使用UUID(),因为基于语句的复制模式下UUID()不能正确复制)。
这个问题很难解决,通常推荐重构应用程序,以保证只有一个主库是可写的。谁能想得到呢?

过大的复制延迟

复制延迟是一个很普遍的问题。不管怎么样,最好在设计应用程序时能够让其容忍备库出现延迟,如果系统在备库出现延迟时就无法很好地工作,那么应用程序也许就不应该用到复制。但是也有一些办法可以让备库跟上主库。MySQL单线程复制的设计导致备库的效率相当低下。即使备库有很多磁盘、CPU或者内存,也会很容易落后于主库。因为备库的单线程通常只会有效地使用一个CPU和磁盘。而事实上,备库通常哦都会和主库使用相同配置的机器。
备库上的锁同样也是问题。其他在备库运行的查询可能会阻塞主复制线程,因为复制时单线程的,复制线程在等待时将无法做别的事情。复制一般有两种产生延迟的方式:突然产生延迟然后再跟上,或者稳定的延迟增大。前一种通常是由于一条运行很长的查询导致的,而后者即使在没有长时间运行的查询时也会出现。不幸的是,目前我们还没那么容易确定备库是否接近其容量上限。正如之前提到的。如果负载总是保持均匀的,备库在负载达到99%时和其负载10%的时候表现的性能相同,但一旦达到100%时就会突然产生延迟。但实际上负载不太可能很稳定,所以当备库接近写容量时,就可能在尖峰负载时看到复制延迟的增加。当备库无法跟上时,可以记录备库上的查询并使用一个日志分析工具找出哪里满了。不要依赖于自己的直觉,也不要基于查询在主库上的查询性能进行判断,因为主库和备库性能特征很不相同。最好的分析办法是暂时在备库上打开慢查询日志记录,然后使用pt-query-digest工具来分析。如果打开了log_slow_slave_statements选项,在标准的MySQL慢查询日志能够记录MySQL5.1及更新的版本中复制线程执行的语句,这样就可以找到在复制时哪些语句执行慢了。Percona Server和MariaDB允许开启或进制该选项而无须重启服务器。
除了购买更快的磁盘和CPU(固态硬盘能够提供极大的帮助),备库没有太多的调优空间。大部分选项都是禁止某些额外的工作以减少备库的负载。一个简单的办法是配置InnoDB,使其不要那么频繁地刷新磁盘,这样事务会提交得更快些。可以通过设置innodb_flush_log_at_trx_commit得值为2来上限。还可以在备库上禁止二进制日志记录,把innodb_locks_unsafe_for_binlog设置为1,并把MyISAM得delay_key_write设置为ALL.但是这些设置以牺牲安全换取速度。如果需要将备库提升为主库,记得把这些选项设置回安全的值

不要重复写操作中代价较高的部分

重构应用程序并且/或者优化查询通常是最好的保持同步的办法。尝试去最小化系统中重复的工作。任何主库上昂贵的写操作都会在每一个备库上重放。如果可以把工作转移到备库,那么久只有一台备库需要执行,然后我们可以把写的结果回传到主库,例如通过执行LOAD DATA INFILE.
举个例子,假设有一个大表,需要汇总到一个小表中用于日常的工作:

mysql>REPLACE INTO main_db.summary_table(col1, col2, ...) SELECT col1, sum(col2, ...) FROM main_db.enormous_table GROUP BY col1;

如果在主库上执行查询,每个备库将同样需要执行庞大的GROUP BY 查询。当进行太多这样的操作时,备库将无法跟上。把这些工作转移到一些备库上也许会有帮助。在备库上创建一个特别保留的数据库,用于避免和从主库上复制的数据产生冲突。可以执行以下查询:

mysql>REPLACE INTO summary_db.summary_table(col1, col2, ...) SELECT col1, sum(col2,...) FROM main_db.enormous_table GROUP BY col1;

现在可以执行SELECT INTO OUTFILE,然后再执行LOAD DATA INFILE,将结果集加载到主库中。现在重复工作被简化为LOAD DATA INFILE操作。如果有N个备库,就节约了N-1次庞大的GROUP BY 操作。
该策略的问题是需要处理陈旧数据。有时候从备库读取的数据和写入主库的数据很难保持一致。如果难以在备库上读取数据,依然能够简化并节省备库工作。如果分离查询的REPLACE和SELECT部分,就可以把结果返回给应用程序,然后将其差人到主库中。首先,在主库执行如下查询:

mysql>SELECT col1, sum(col2,...) FROM main_db.enormous_table GROUP BY col1;

然后为结果集的每一行重复执行如下语句,将结果插入到汇总表中:

mysql>REPLACE INTO main_db.summary_table (col1, col2,...) VALUES(?,?,...)

这种方法再次避免了在备库上执行查询中的GROUP BY 部分。将SELECT和REPLACE分离后意味着查询的SELECT操作不会在每一台备库上重放。这种通用的策略——节约了备库上昂贵的写入操作部分——在很多情况下很有帮助:计算查询的结果代价很昂贵

在复制之外并行写入

另一种避免备库严重延迟的办法是绕过复制。任何在主库上的写入操作必须在备库串行化。因此有理由认为"串行化写入"不能充分利用资源。所有写操作都应该从主库传递到备库码?如果把备库优先的串行写入容量留给哪些真正需要通过复制进行的写入?
这种考虑有助于对写入进行区分。特别是,如果能确定一些写入可以轻易地在复制之外执行,就可以并行化这些操作以利用备库的写入容量。一个很好的例子是之前讨论过的数据归档。OLTP归档需求通常是简单的单行操作。如果只是把不需要的记录从一个表转移到另一个表,就没有必要将这些写入复制到备库。可以禁止归档查询记录到二进制日志中,然后分别在主库和备库上单独执行这些归档查询。自己复制数据到另外一台服务器,而不是通过复制,这听起来有些疯狂,但却对一些应用有意义,特别是如果应用是某些表的唯一更新源。复制的瓶颈通常集中在小部分表上,如果能在复制之外单独处理这些表,就能够显著地加快复制。

为复制线程预取缓存

如果有正确的工作负载,就能通过预先将数据读入内存中,以受益于在备库上的并行IO所带来的好处。这种方式并不广为人知。大多数人不会使用,因为除非有正确的工作负载特性和硬件配置,否则可能没有任何用处。上面讨论的其他几种变通方式通常是更好的选择,并且有更多的方法来应用它们。但是我们直到也有小部分应用会受益于数据预取。
有两种可行的实现方法。一种是通过程序实现,略微比备库SQL线程提前读取中继日志,并将其转换为SELECT语句执行。这会使得服务器将数据从磁盘加载到内存中,这样当SQL线程执行到相应的语句时,就无须从磁盘读取数据。事实上,SELECT语句可以并行地执行,所以可以加速SQL线程的串行IO.当一条语句正在执行时,下一条语句需要的数据也正在从磁盘加载到内存中。
如果满足下面这些条件,预取可能会有效:

  • 1.复制SQL线程是IO密集型的,但备库服务器并不是IO密集型的.一个完全的IO密集型服务器不会受益于预取,因为它没有多余的磁盘性能来提供预取
  • 2.备库有多个硬盘驱动器,也许8个或者更多
  • 3.使用的是InnoDB以前宁,并且工作集远不能完全加载到内存中

一个受益于预读取的例子是随机单行UPDTE语句,这些语句通常在主库上高并发执行。DELETE语句也可能受益于这种方法,但INSERT语句则不太可能会——尤其是当顺序插入时——因为前一次插入已经使索引"预热"了。如果表上有很多索引,同样无法预取所有将要被修改的数据。UPDATE语句可能需要更新所有索引,但SELECT语句通常只会读取主键和一个二级索引。UPDATE语句依然需要去读取其他索引的数据以进行更新。在多索引表上这种方法的效率会降低。这种技术并不是"银弹",有很多原因会导致其不能工作,甚至适得其反。只有在清楚硬件和操作系统的状况时才能尝试这种方法。我们直到有些人利用这种办法将复制速度提升了300%到400%,但也尝试过很多次,并发现这种办法常常无法工作。正确地设置参数非常重要,但并没有绝对正确的参数组合。
mk-slave-perfetch是Maatkit种的一款工具,该工具实现了提到的预取策略。mk-slave-prefetch本身有很多复杂的策略以保证其尽可能多的场景下工作,但缺点是它实在太复杂并且需要许多专业知识来使用,另一款工具是Anders Karlsson的slavereadahead工具。
另一种方法是在InnoDB内部实现的。它可以允许设置事务为特殊的模式,以允许InnoDB执行"假"更新。因此可以使用一个程序来执行这些加更新,这样复制线程就可以更快地执行真正的更新。Percona Server为一个非常流行的互联网网络应用单独开发了该功能。可以去检查一下此特性现在的状况。
如果正在考虑这项技术,可以从一个熟悉其工作原理及可用选项的专家那里获得很好的建议,这应该作为其他方法都不可行时最后的解决办法

来自主库的过大的包

另一个难以追踪的问题是主库的max_allowed_packet值和备库的不匹配。在这种情况下,主库可能会记录一个备库认为过大的包。当备库获取到该二进制日志事件时,可能会碰到各种各样的问题,包括无限报错和充实,或者中继日志损坏

受限制的复制带宽

如果使用受限的带宽进行复制,可以开启备库上的slave_compressed_protocol选项。当备库连接主库时,会请求一个被压缩的连接——和MySQL客户端使用的压缩连接一样。使用的压缩引擎是zlib,测试表明它能将文本类型的数据压缩到大约其原始大小的三分之一。其代价是需要额外的CPU时间,包括在主库上压缩数据和在备库上解压数据。
如果主库和备库间的连接是慢速连接,可能需要将分发主库和备库分布在同一地点。这样就只有一台服务器通过慢速连接和主库相连,可以减少链路上的带宽负载以及主库的CPU负载

磁盘空间不足

复制有可能因为二进制日志、中继日志或临时文件将磁盘撑满,特别是在主库上执行了LOAD DATA INFILE查询并在备库开启了log_slave_updates选项。延迟越严重,接收到但尚未执行的中继日志会占用越多的磁盘空间。可以通过监控磁盘并设置relay_log_space选项来避免这个问题

复制的局限性

MySQL复制可能失败或者不同步,不管有没有报错,这是因为其内部的限制导致的。大量的SQL函数和编程实践不能被可靠地复制。很难确保应用代码里不会出现这样或那样的问题,特别是应用或者团队非常庞大的时候。另外一个问题是服务器的bug,虽然听起来很消极,但大多数MySQL的主版本都存在着历史遗留的复制Bug。特别是每个主版本的第一个版本。诸如存储过程这样的新特性常常会导致更多的问题。
MySQL复制非常复杂,应用程序越复杂,你就需要越小心。但是如果学会了如何使用,复制会工作得很好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/858019.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“Driver not loaded“问题解决方案

这两天又碰到了离谱的&#xff0c;愚蠢的&#xff0c;莫名其妙的&#xff0c;丧尽天良的错误。 之前已经解决过这个问题。这几天又碰上了&#xff0c;明明都已经把相应的dll放到了exe的同级目录&#xff0c;NND还是有问题&#xff01;&#xff01;&#xff01;卡了我一个晚上加…

c库函数:strrchr使用demo案例

1. strrchr库函数说明 头文件 <string.h> 函数形式 char *strrchr( const char *str, int ch ); 功能 在str所指向的空终止字节串中寻找字符ch的最后出现。 参数 str - 指向要分析的空终止字节字符串的指针 ch - 要搜索的字符 返回值 指向 str 中找到的字符的…

Emacs之实现目录替换(一百四十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

vue 中监听生命周期事件

vue 中监听生命周期事件 常见的添加自定义事件的写法希望在子组件挂载时通知父组件在模板上监听组件生命周期vue3 有类似的写法吗&#xff1f;jsx 中如何监听 vue3 组件的生命周期事件呢&#xff1f; vue3 父组件组件的生命周期的执行顺序是什么&#xff1f;小结 vue2 提供了一…

pytest测试框架pytest-rerunfailures插件重试失败用例

Pytest提供了丰富的插件来扩展其功能&#xff0c;介绍下插件pytest-rerunfailures &#xff0c;用于在测试用例失败时自动重新运行这些测试用例。 pytest-rerunfailures官方显示的python和pytest版本限制&#xff1a; Python 3.8pytest 7.2 或更新版本 此插件可以通过以下可…

Scala运算符及流程控制

Scala运算符及流程控制 文章目录 Scala运算符及流程控制写在前面运算符算数运算符关系运算符赋值运算符逻辑运算符位运算符运算符本质 流程控制分支控制单分支双分支多分支 循环控制for循环while循环循环中断嵌套循环 写在前面 操作系统&#xff1a;Windows10JDK版本&#xff…

1027. 方格取数

Powered by:NEFU AB-IN Link 文章目录 1027. 方格取数题意思路代码 1027. 方格取数 题意 某人从图中的左上角 A 出发&#xff0c;可以向下行走&#xff0c;也可以向右行走&#xff0c;直到到达右下角的 B 点。 在走过的路上&#xff0c;他可以取走方格中的数&#xff08;取…

FOC方案大合集!

获取链接&#xff01;&#xff01;&#xff01; 本次小编给大家带来了一份FOC的方案大合集。此套方案是基于峰岹科技FU68系列MCU的系列方案&#xff0c;包含常用的无感&#xff0c;有感无刷电机的应用&#xff0c;每份方案都包含了原理图&#xff0c;PCB&#xff0c;代码文件&…

【TOOL】ceres学习笔记(一) —— 教程练习

文章目录 一、Ceres Solver 介绍二、Ceres 使用基本步骤1. 构建最小二乘问题2. 求解最小二乘问题 三、使用案例1. Ceres Helloworld2. Powell’s Function3. Curve Fitting4. Robust Curve Fitting 一、Ceres Solver 介绍 Ceres-solver 是由Google开发的开源C库&#xff0c;用…

2024年P气瓶充装证模拟考试题库及P气瓶充装理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年P气瓶充装证模拟考试题库及P气瓶充装理论考试试题是由安全生产模拟考试一点通提供&#xff0c;P气瓶充装证模拟考试题库是根据P气瓶充装最新版教材&#xff0c;P气瓶充装大纲整理而成&#xff08;含2024年P气瓶…

[Open-source tool]Uptime-kuma的簡介和安裝於Ubuntu 22.04系統

[Uptime Kuma]How to Monitor Mqtt Broker and Send Status to Line Notify Uptime-kuma 是一個基於Node.js的開軟軟體&#xff0c;同時也是一套應用於網路監控的開源軟體&#xff0c;其利用瀏覽器呈現直觀的使用者介面&#xff0c;如圖一所示&#xff0c;其讓使用者可監控各種…

足底筋膜炎的症状

足底筋膜炎是足底的肌腱或者筋膜发生无菌性炎症所致&#xff0c;其症状主要包括&#xff1a; 1、疼痛&#xff1a;这是足底筋膜炎最常见和突出的症状。疼痛通常出现在足跟或足底近足跟处&#xff0c;有时压痛较剧烈且持续存在。晨起时或长时间不活动后&#xff0c;疼痛感觉尤为…

高通安卓12-安卓系统定制2

将开机动画打包到system.img里面 在目录device->qcom下面 有lito和qssi两个文件夹 现在通过QSSI的方式创建开机动画&#xff0c;LITO方式是一样的 首先加入自己的开机动画&#xff0c;制作过程看前面的部分 打开qssi.mk文件&#xff0c;在文件的最后加入内容 PRODUCT_CO…

Python | Leetcode Python题解之第174题地下城游戏

题目&#xff1a; 题解&#xff1a; class Solution:def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:n, m len(dungeon), len(dungeon[0])BIG 10**9dp [[BIG] * (m 1) for _ in range(n 1)]dp[n][m - 1] dp[n - 1][m] 1for i in range(n - 1, -1, …

一文读懂LLM API应用开发基础(万字长文)

前言 Hello&#xff0c;大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者&#xff0c;上一篇文章中我们详细介绍了LLM开发的基本概念&#xff0c;包括LLM的模型、特点能力以及应用&#xff1b;&#x1f632; 在本文中作者将通过&#xff1a…

Redis—Set数据类型及其常用命令详解

文章目录 一、Redis概述Set类型1 SADD:向集合&#xff08;Set&#xff09;中添加一个或多个成员2 SCARD:获取集合&#xff08;Set&#xff09;中成员数量3 SDIFF:获取多个集合之间的差集4 SDIFFSTORE:计算多个集合之间的差集&#xff0c;并将结果存储在指定的目标集合中5 SMEMB…

Android 你应该知道的学习资源 进阶之路贵在坚持

coderzheaven 覆盖各种教程&#xff0c;关于Android基本时案例驱动的方式。 非常推荐 thenewcircle 貌似是个培训机构&#xff0c;多数是收费的&#xff0c;不过仍然有一些free resources值得你去挖掘。 coreservlets 虽然主打不是android&#xff0c;但是android的教程也​ 是…

Linux配置中文环境

文章目录 前言中文语言包中文输入法中文字体 前言 在Linux系统中修改为中文环境&#xff0c;通常涉及以下几个步骤&#xff1a; 中文语言包 更新源列表&#xff1a; 更新系统的软件源列表和语言环境设置&#xff0c;确保可以安装所需的语言包。 sudo apt update sudo apt ins…

华为某员工爆料:三年前985本科起薪30万,现在硕士起薪还是30w,感慨互联网行情变化

“曾经的30万年薪&#xff0c;是985本科学历的‘标配’&#xff0c;如今硕士也只值这个价&#xff1f;” 一位华为员工的爆料&#xff0c;揭开了互联网行业薪资变化的冰山一角&#xff0c;也引发了不少人的焦虑&#xff1a;互联网人才“通货膨胀”的时代&#xff0c;真的结束了…

Java基础的重点知识-04-封装

文章目录 面向对象思想封装 面向对象思想 在计算机程序设计过程中&#xff0c;参照现实中事物&#xff0c;将事物的属性特征、行为特征抽象出来&#xff0c;描述成计算机事件的设计思想。 面向对象思想的三大基本特征: 封装、继承、多态 1.类和对象 类是对象的抽象&#xff…