zoukankan      html  css  js  c++  java
  • MySql数据库优化、备份和恢复

    一、数据库优化

    1、为什么要优化

    • 系统的吞吐量瓶颈往往出现在数据库的访问速度上
    • 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢
    • 数据是存放在磁盘上的,读写速度无法和内存相比

    优化原则:减少系统瓶颈,减少资源占用,增加系统的反应速度。

    2、数据库结构优化

    需要考虑数据冗余、查询和更新的速度、字段的数据类型是否合理等多方面的内容。

    • 将字段很多的表分解成多个表。对于字段较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来形成新表。因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。
    • 增加中间表。对于需要经常联合查询的表,可以建立中间表以提高查询效率。通过建立中间表,将需要通过联合查询的数据插入到中间表中,然后将原来的联合查询改为对中间表的查询。
    • 减少冗余字段。设计数据表时应尽量遵循范式理论的规约,尽可能的减少冗余字段,让数据库设计看起来精致、优雅。但是,合理的加入冗余字段可以提高查询速度。表的规范化程度越高,表和表之间的关系越多,需要连接查询的情况也就越多,性能也就越差。

    3、MySQL数据库cpu飙升到500%的话怎么处理

    当 cpu 飙升到 500%时,先用操作系统命令 top 命令观察是不是 mysqld 占用导致的,如果不是,找出占用高的进程,并进行相关处理。如果是 mysqld 造成的, show processlist,看看里面跑的 session 情况,是不是有消耗资源的 sql 在运行。找出消耗高的 sql,看看执行计划是否准确, index 是否缺失,或者实在是数据量太大造成。一般来说,肯定要 kill 掉这些线程(同时观察 cpu 使用率是否下降),等进行相应的调整(比如说加索引、改 sql、改内存参数)之后,再重新跑这些 SQL。也有可能是每个 sql 消耗资源并不多,但是突然之间,有大量的 session 连进来导致 cpu 飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等

    4、大表怎么优化

    (1)某个表有近千万数据,CRUD比较慢,如何优化?

    当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:

    1. 限定数据的范围: 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。

    2. 读/写分离: 经典的数据库拆分方案,主库负责写,从库负责读;

    3. 缓存: 使用MySQL的缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存;

    4. 通过分库分表的方式进行优化,主要有垂直分表和水平分表

    (2)分库分表了是怎么做的?

    1. 垂直分区:

      根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说大家应该就更容易理解了。

      img

      垂直拆分的优点: 可以使得行数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。

      垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;

      垂直分表

      把主键和一些列放在一个表,然后把主键和另外的列放在另一个表中

      img

      适用场景

      • 1、如果一个表中某些列常用,另外一些列不常用
      • 2、可以使数据行变小,一个数据页能存储更多数据,查询时减少I/O次数

      缺点

      • 有些分表的策略基于应用层的逻辑算法,一旦逻辑算法改变,整个分表逻辑都会改变,扩展性较差
      • 对于应用层来说,逻辑算法增加开发成本
      • 管理冗余列,查询所有数据需要join操作
    2. 水平分区:

      保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。

      数据库水平拆分

      水品拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 水平拆分最好分库 。水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨界点Join性能较差,逻辑复杂。

      尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。

      水平分表:

      表很大,分割后可以降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数,提高查询次数

      img

      适用场景

      • 1、表中的数据本身就有独立性,例如表中分表记录各个地区的数据或者不同时期的数据,特别是有些数据常用,有些不常用。
      • 2、需要把数据存放在多个介质上。

      水平切分的缺点

      • 1、给应用增加复杂度,通常查询时需要多个表名,查询所有数据都需UNION操作
      • 2、在许多数据库应用中,这种复杂度会超过它带来的优点,查询时会增加读一个索引层的磁盘次数

      下面补充一下数据库分片的两种常见方案:

      • 客户端代理: 分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。 当当网的 Sharding-JDBC 、阿里的TDDL是两种比较常用的实现。
      • 中间件代理: 在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。 我们现在谈的 Mycat 、360的Atlas、网易的DDB等等都是这种架构的实现。

    (3)分表分库了有什么问题?

    • 事务支持 分库分表后,就成了分布式事务了。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价; 如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。

    • 跨库join。只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。 

    • 跨节点的count,order by,group by以及聚合函数问题 这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。

    • 数据迁移,容量规划,扩容等问题 来自淘宝综合业务平台团队,它利用对2的倍数取余具有向前兼容的特性(如对4取余得1的数对2取余也是1)来分配数据,避免了行级别的数据迁移,但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制。总得来说,这些方案都不是十分的理想,多多少少都存在一些缺点,这也从一个侧面反映出了Sharding扩容的难度。

    • ID问题

      •   一旦数据库被切分到多个物理结点上,我们将不能再依赖数据库自身的主键生成机制。一方面,某个分区数据库自生成的ID无法保证在全局上是唯一的;另一方面,应用程序在插入数据之前需要先获得ID,以便进行SQL路由. 一些常见的主键生成策略UUID。使用UUID作主键是最简单的方案,但是缺点也是非常明显的。由于UUID非常的长,除占用大量存储空间外,最主要的问题是在索引上,在建立索引和基于索引进行查询时都存在性能问题。 Twitter的分布式自增ID算法Snowflake 在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位 机器ID 10位 毫秒内序列12位。
    • 跨分片的排序分页

      分页时需要按照指定字段进行排序。当排序字段就是分片字段的时候,我们通过分片规则可以比较容易定位到指定的分片,而当排序字段非分片字段的时候,情况就会变得比较复杂了。为了最终结果的准确性,我们需要在不同的分片节点中将数据进行排序并返回,并将不同分片返回的结果集进行汇总和再次排序,最后再返回给用户。如下图所示:

      在这里插入图片描述

    5、MySQL的复制原理以及流程

    主从复制:将主数据库中的DDL和DML操作通过二进制日志(BINLOG)传输到从数据库上,然后将这些日志重新执行(重做);从而使得从数据库的数据与主数据库保持一致。

    (1)主从复制的作用

    1. 主数据库出现问题,可以切换到从数据库。
    2. 可以进行数据库层面的读写分离。
    3. 可以在从数据库上进行日常备份。

    (2)MySQL主从复制解决的问题

    • 数据分布:随意开始或停止复制,并在不同地理位置分布数据备份
    • 负载均衡:降低单个服务器的压力
    • 高可用和故障切换:帮助应用程序避免单点失败
    • 升级测试:可以用更高版本的MySQL作为从库

    (3)MySQL主从复制工作原理

    • 在主库上把数据更高记录到二进制日志
    • 从库将主库的日志复制到自己的中继日志
    • 从库读取中继日志的事件,将其重放到从库数据中

    基本原理流程,3个线程以及之间的关联

    • 主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;
    • 从:io线程——在使用start slave 之后,负责从master上拉取 binlog 内容,放进自己的relay log中;
    • 从:sql执行线程——执行relay log中的语句;

    复制过程

    img

    Binary log:主数据库的二进制日志

    Relay log:从服务器的中继日志

    1. 第一步:master在每个事务更新数据完成之前,将该操作记录串行地写入到binlog文件中。
    2. 第二步:salve开启一个I/O Thread,该线程在master打开一个普通连接,主要工作是binlog dump process。如果读取的进度已经跟上了master,就进入睡眠状态并等待master产生新的事件。I/O线程最终的目的是将这些事件写入到中继日志中。
    3. 第三步:SQL Thread会读取中继日志,并顺序执行该日志中的SQL事件,从而与主数据库中的数据保持一致。

    6、读写分离有哪些解决方案?

    读写分离是依赖于主从复制,而主从复制又是为读写分离服务的。因为主从复制要求slave不能写只能读(如果对slave执行写操作,那么show slave status将会呈现Slave_SQL_Running=NO,此时你需要按照前面提到的手动同步一下slave)。

    (1)方案一:使用mysql-proxy代理

    • 优点:直接实现读写分离和负载均衡,不用修改代码,master和slave用一样的帐号,mysql官方不建议实际生产中使用
    • 缺点:降低性能, 不支持事务

    (2)方案二:使用AbstractRoutingDataSource+aop+annotation在dao层决定数据源。

      如果采用了mybatis, 可以将读写分离放在ORM层,比如mybatis可以通过mybatis plugin拦截sql语句,所有的insert/update/delete都访问master库,所有的select 都访问salve库,这样对于dao层都是透明。 plugin实现时可以通过注解或者分析语句是读写方法来选定主从库。不过这样依然有一个问题, 也就是不支持事务, 所以我们还需要重写一下DataSourceTransactionManager, 将read-only的事务扔进读库, 其余的有读有写的扔进写库。

    (3)方案三:使用AbstractRoutingDataSource+aop+annotation在service层决定数据源,可以支持事务.

      缺点:类内部方法通过this.xx()方式相互调用时,aop不会进行拦截,需进行特殊处理。

    二、Mysql备份

    数据库的主要作用就是对数据进行保存和维护,所以备份数据是数据库管理中最常用的操作。为了防止数据库意外崩溃或硬件损伤而导致的数据丢失,数据库系统提供了备份和恢复策略。保证数据安全的最重要的一个措施就是定期的对数据库进行备份。这样即使发生了意外,也会把损失降到最低。数据库备份是指通过导出数据或者复制表文件的方式来制作数据库的副本。当数据库出现故障或遭到破坏时,将备份的数据库加载到系统,从而使数据库从错误状态恢复到备份时的正确状态。

    MySQL 中提供了两种备份方式,即 mysqldump 命令以及 mysqlhotcopy 脚本。由于 mysqlhotcopy 只能用于 MyISAM 表,所以 MySQL 5.7 移除了 mysqlhotcopy 脚本。

    mysqldump 命令执行时,可以将数据库中的数据备份成一个文本文件。数据表的结构和数据将存储在生成的文本文件中。

    1、备份一个数据库

    使用 mysqldump 命令备份一个数据库的语法格式如下:

    mysqldump -u username -p dbname [tbname ...]> filename.sql

    对上述语法参数说明如下:

    • username:表示用户名称;
    • dbname:表示需要备份的数据库名称;
    • tbname:表示数据库中需要备份的数据表,可以指定多个数据表。省略该参数时,会备份整个数据库;
    • 右箭头“>”:用来告诉 mysqldump 将备份数据表的定义和数据写入备份文件;
    • filename.sql:表示备份文件的名称,文件名前面可以加绝对路径。通常将数据库备份成一个后缀名为.sql的文件。

    注意:mysqldump 命令备份的文件并非一定要求后缀名为.sql,备份成其他格式的文件也是可以的。例如,后缀名为.txt的文件。通常情况下,建议备份成后缀名为.sql 的文件。因为,后缀名为.sql的文件给人第一感觉就是与数据库有关的文件。

    实例

    下面使用 root 用户备份 test 数据库下的 student 表。打开命令行(cmd)窗口,输入备份命令和密码,运行过程如下:

    C:Windowssystem32>mysqldump -uroot -p test student>C:student.sql
    Enter password: ****

    注意:mysqldump 命令必须在 cmd 窗口下执行,不能登录到 MySQL 服务中执行。输入密码后,MySQL 会对 test 数据库下的 student 数据表进行备份。之后就可以在指定路径下查看刚才备份过的文件了。 student.sql 文件中没有创建数据库的语句,因此,student.sql 文件中的所有表和记录必须恢复到一个已经存在的数据库中。恢复数据时,CREATE TABLE 语句会在数据库中创建表,然后执行 INSERT 语句向表中插入记录。

    2、备份多个数据库

    如果要使用 mysqldump 命令备份多个数据库,需要使用 --databases 参数。备份多个数据库的语法格式如下:

    mysqldump -u username -P --databases dbname1 dbname2 ... > filename.sql

    加上“--databases”参数后,必须指定至少一个数据库名称,多个数据库名称之间用空格隔开。

    实例

    下面使用 root 用户备份 test 数据库和 mysql 数据库。命令如下:

    mysqldump -u root -p --databases test mysql>C:	estandmysql.sql

    执行完后,可以在C:下面看到名为 testandmysql.sql 的文件,这个文件中存储着这两个数据库的信息。

    3、备份所有数据库

    mysqldump 命令备份所有数据库的语法格式如下:

    mysqldump -u username -P --all-databases>filename.sql

    使用“--all-databases”参数时,不需要指定数据库名称。

    实例

    下面使用 root 用户备份所有数据库。命令如下:

    mysqldump -u root -p --all-databases > C:all.sql

    执行完后,可以在 C:下面看到名为 all.sql 的文件,这个文件中存储着所有数据库的信息。

    三、MySQL恢复数据库

    系统进行恢复操作时,先执行一些系统安全性的检查,包括检查所要恢复的数据库是否存在、数据库是否变化及数据库文件是否兼容等,然后根据所采用的数据库备份类型采取相应的恢复措施。数据库恢复机制设计的两个关键问题是:第一,如何建立冗余数据;第二,如何利用这些冗余数据实施数据库恢复。建立冗余数据最常用的技术是数据转储和登录日志文件。通常在一个数据库系统中,这两种方法是一起使用的。数据转储是 DBA 定期地将整个数据库复制到磁带或另一个磁盘上保存起来的过程。这些备用的版本成为后备副本或后援副本。可使用 LOAD DATA…INFILE 语句来恢复先前备份的数据。

    实例

    将之前导出的数据备份文件 file.txt 导入数据库 test_db 的表 tb_students_copy 中,其中 tb_students_copy 的表结构和 tb_students_info 相同。首先创建表 tb_students_copy,输入的 SQL 语句和执行结果如下所示。

    mysql> CREATE TABLE tb_students_copy
        -> LIKE tb_students_info;
    Query OK, 0 rows affected (0.52 sec)
    mysql> SELECT * FROM tb_students_copy;
    Empty set (0.00 sec)

    导入数据与查询表 tb_students_copy 的过程如下所示。

    mysql> LOAD DATA INFILE 'C:/ProgramData/MySQL/MySQL Server 5.7/
    Uploads/file.txt'
        -> INTO TABLE test_db.tb_students_copy
        -> FIELDS TERMINATED BY ','
        -> OPTIONALLY ENCLOSED BY '"'
        -> LINES TERMINATED BY '?';
    Query OK, 10 rows affected (0.14 sec)
    Records: 10  Deleted: 0  Skipped: 0  Warnings: 0

    四、MySQL导出表数据

    在 MySQL 中,可以使用 SELECTI...INTO OUTFILE 语句将表的内容导出成一个文本文件。SELECT...INTO OUTFILE 语句基本格式如下:

    SELECT 列名 FROM table [WHERE 语句] INTO OUTFILE '目标文件'[OPTIONS]

    该语句用 SELECT 来查询所需要的数据,用 INTO OUTFILE 来导出数据。其中,目标文件用来指定将查询的记录导出到哪个文件。这里需要注意的是,目标文件不能是一个已经存在的文件。

    [OPTIONS] 为可选参数选项,OPTIONS 部分的语法包括 FIELDS 和 LINES 子句,其常用的取值有:

    • FIELDS TERMINATED BY '字符串':设置字符串为字段之间的分隔符,可以为单个或多个字符,默认情况下为制表符‘ ’。
    • FIELDS [OPTIONALLY] ENCLOSED BY '字符':设置字符来括上 CHAR、VARCHAR 和 TEXT 等字符型字段。如果使用了 OPTIONALLY 则只能用来括上 CHAR 和 VARCHAR 等字符型字段。
    • FIELDS ESCAPED BY '字符':设置如何写入或读取特殊字符,只能为单个字符,即设置转义字符,默认值为‘’。
    • LINES STARTING BY '字符串':设置每行开头的字符,可以为单个或多个字符,默认情况下不使用任何字符。
    • LINES TERMINATED BY '字符串':设置每行结尾的字符,可以为单个或多个字符,默认值为‘ ’ 。

    注意:FIELDS 和 LINES 两个子句都是自选的,但是如果两个都被指定了,FIELDS 必须位于 LINES的前面。

    示例

    下面使用 SELECT...INTO OUTFILE 语句来导出 test 数据库中的 person 表中的记录。SQL 语句和运行结果如下:

    mysql> SELECT * FROM test.person INTO OUTFILE 'C://ProgramData/MySQL/MySQL Server 5.7/Uploads/person.txt';
    Query OK, 5 rows affected (0.05 sec)

    注意:导出时可能会出现下面的错误:

    The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

    这是因为MySQL 限制了数据的导出路径。MySQL 导入导出文件只能在 secure-file-priv 变量的指定路径下的文件才可以导入导出。
    有以下 2 种解决办法:

    • 首先使用show variables like '%secure%';语句查看 secure-file-priv 变量配置。
    mysql> show variables like '%secure%' G
    *************************** 1. row ***************************
    Variable_name: require_secure_transport
            Value: OFF
    *************************** 2. row ***************************
    Variable_name: secure_auth
            Value: ON
    *************************** 3. row ***************************
    Variable_name: secure_file_priv
            Value: C:ProgramDataMySQLMySQL Server 5.7Uploads
    3 rows in set, 1 warning (0.04 sec)

    secure_file_priv 的值指定的是 MySQL 导入导出文件的路径。将 SQL 语句中的导出文件路径修改为该变量的指定路径,再执行导入导出操作即可。也可以在 my.ini 配置文件中修改 secure-file-priv 的值,然后重启服务即可。

    • 如果 secure_file_priv 值为 NULL,则为禁止导出,可以在 MySQL 安装路径下的 my.ini 文件中添加secure_file_priv=设置路径语句,然后重启服务即可。

    示例

    使用 SELECT...INTO OUTFILE 语句将 test 数据库中的 person 表中的记录导出到文本文件,使用 FIELDS 选项和 LINES 选项,要求字段之间用隔开,字符型数据用双引号括起来。每条记录以-开头。SQL 语句如下:

    SELECT * FROM test.person INTO OUTFILE 'C:/person.txt'
        FIELDS TERMINATED BY '' OPTIONALLY ENCLOSED BY '"' LINES STARTING BY '-'
    TERMINATED BY '
    ';

    其中:

    • FIELDS TERMINATED BY '、’:表示字段之间用分隔;
    • ENCLOSED BY '"':表示每个字段都用双引号括起来;
    • LINES STARTING BY '-':表示每行以-开头;
    • TERMINATED BY ' ' 表示每行以回车换行符结尾,保证每一条记录占一行。

    person.txt 文件内容如下:

    -1"Java"12
    -2"MySQL"13
    -3"C"15
    -4"C++"22
    -5"Python"18

     参考:

    http://c.biancheng.net/mysql/

    https://blog.csdn.net/ThinkWon/article/details/104778621/

  • 相关阅读:
    iOS程序-UIScrollView的基本使用
    iOS方法类:CGAffineTransform
    指南针开发
    iOS用if语句判断null
    UIView常用的一些方法
    xcode视图缩短了
    TCP&UDP基础
    朴素贝叶斯方法在乳腺肿块检测中的应用
    进程与线程的相关知识点总结
    C++中sizeof操作符与strlen函数
  • 原文地址:https://www.cnblogs.com/qtiger/p/14379190.html
Copyright © 2011-2022 走看看