60分钟

第3章 数据读写分类与数据库集群

【学习目标】

知识目标

了解分布式数据库的特点。

了解分布式数据库的分类。

理解数据架构。

理解数据库的应用场景。

理解数据库的性能衡量指标。

掌握数据库分离实现的方法。

掌握MySQL Proxy搭建MySQL数据库的读写分离的方法。

掌握Redis数据库集群的设计与部署。

掌握MySQL调优的方法。

技能目标

数据库分离实现的方法。

MySQL Proxy搭建MySQL数据库的读写分离的方法。

Redis数据库集群的设计与部署。

MySQL调优的方法。

【认证考点】

理解分布式数据库的特点与分类。

理解数据架构的原理。

掌握数据读写分离的原理与方法。

MySQL数据读写分离实现的方法。

Redis数据库集群的设计与部署。

MySQL的调优方法。

项目引导:数据读写分离与数据库集群设计与搭建

【项目描述】

小A的论坛在早期使用一台数据库就已经足够了,但随着用户访问量和数据的增多,论坛访问的速度越来越慢并存在单点故障的危险。小A准备对论坛进行升级,重新构建数据库的架构,采用主从架构来实现数据读写分离,采用数据库集群来存储越来越多的数据,从而提高了数据读写的速度和避免了单点故障的危险。

知识储备

3.1 分布式数据库系统概述

分布式数据库系统(Distributed Database System,DDBS)源于20世纪70年代中期。随着数据库应用需求的移动通信扩展和信息技术特别是计算机网络与数字通信技术的飞速发展,卫星通信、局域网、广域网、Internet等得到了广泛的应用。显然集中式数据库系统已经不能适应这样的环境,存在的不足有:①数据按实际需要已经在网络上分布存储,如果采用集中式处理,势必造成附加成本和通信开销;②应用程序集中在一台计算机运行,一旦计算机发生故障,将会影响整个系统的运行、可靠性不高;③集中式处理导致系统的规模和配置不够灵活,系统的可扩展性差。在这种情况下,数据库的应用普遍是构建在计算机网络上,这时分布式数据库系统应运而生。

1.分布式数据库系统的定义

分布式数据库系统通俗来讲是物理上分散而逻辑上集中的数据库系统。分布式数据库系统使用计算机网络将地理位置分散而管理和控制又需要不同程度集中的逻辑单位连接起来,共同组成一个统一的数据库系统。因此,分布式数据库系统可以看成是计算机网络与数据库系统的有机结合。

2.分布式数据系统的特点

(1)物理分布式

分布式数据系统的数据不是存储在一个站点上,而是分散存储在计算机网络连接起来的多个站点上,所以分布式数据库具有物理性特征,这是与集中式数据库系统的最大区别之一。

(2)逻辑整体性

分布式数据系统中的数据物理上是分散在各个站点中,但这些分散的数据逻辑上却构成一个整体,它们被分布式数据库系统的所有用户共享,并由一个分布式数据库管理系统统一管理,它使得“分布”对用户来说是投影的,这是分布式数据库系统逻辑整体性的特点。

(3)站点自治性

站点自治性也称为场地自治性,各站点上的数据由本地的DBMS管理,具有自治处理能力,完成本站点的应用,这是分布式数据库系统与多处理机系统的区别。

3.分布式数据库系统的体系架构

分布式数据库系统是在集中式数据库系统的基础上发展起来的,是数据库技术和网络技术结合的产物。分布式数据库是基于计算机网络连接的集中式数据库的逻辑集合,它既保留了集中式数据库系统的特色又比集中式数据库模式结构复杂。分布式数据库是多层模式结构,层次的划分尚无统一的标准,在国内分布式数据库划分为四层,分别为全局外层:全局模式;全局概念层:全局概念模式、分片模式、分配模式;局部概念层:局部概念模式;局部内存:局部内模式。

(1)全局模式

全局模式是全局应用的用户视图,分布式数据库的全局视图与集中式数据库视图有同样的概念,不同的只是分布式数据库的全局视图不是从某一个具体的局部数据库中抽取,而是从一个由局部数据库组成的逻辑集合中抽取,即全局模式是全局概念模型的子集。

(2)全局概念模式

全局概念模式描述分布式数据库中全局数据的逻辑结构和数据特征。从用户或应用程序角度来看,分布式数据库与集中式数据库应用没有什么区别。分布式数据库中全局概念模式所用的数据模型是方便定义该分布式数据库其他各层的映射,因此采用关系数据模式。

(3)分片模式

分片模式描述全局数据的逻辑划分,在关系型分布式数据库中,每个全局关系可以通过选择和投影这样的关系操作被逻辑划分为若干个片段,这就是“数据分片”。分片模式描述数据分片或定义片段,以及全局关系与片段之间的映射,这种映射是一对多的,即一个全局关系可对应多个片段,而一个片段只能来自一个全局关系。

(4)分配模式

由数据分片得到的片段仍然是分布式数据库的全局数据,每一片段在物理上可定位于计算机网络中的一个或多个站点上。分配模式就是根据选定的数据分布策略,定义各片段的物理存放站点,即定义片段映射的类型,确定分布式数据库是冗余的还是非冗余的,以及冗余的程度。如果一个片段分配在多个站点上,则片段的映射是一对多的,分布式数据库是冗余的。冗余的程度描述片段映射到站点的个数,一般根据需要而定,每个片段映射的冗余程度不比相同。

(5)局部概念模型

局部概念模型是全局概念模型的子集,对每个站点来说,在该站点上的全部物理映射集合称为该站点上的局部概念模式。

(6)局部内模式

局部内模式是分布式数据库中关于物理数据库的描述,与集中式数据库中的内模式相似,但描述的内容不仅包含本站点局部数据的存储描述,还包括全局数据在本站点的描述。

4.分布式数据库系统的主要技术

分布式数据库系统涉及的主要技术有:分布式数据库设计、分布式查询与优化、分布式事务管理与恢复、分布式并发控制、分布式数据库的可靠性、分布式数据库的安全性等。

(1)分布式数据库设计

分布式数据库系统的设计远比集中式数据库系统的设计困难,因为设计单个站点上数据库系统的许多关键技术问题和组织问题,在多个站点的分布式数据库系统中变得更加的复杂。分布式数据库设计方法主要有两种分别为自顶向下方法和自底向上。前者是从头开始设计分布式数据库,而后者则通过集成现有数据库来设计分布式数据库。

(2)分布式查询和优化

分布式查询和集中式查询大不相同。集中式环境中的查询只需要考虑CPU和I/O代价,而分布式环境下影响查询效率的因素有很多,除了CPU和I/O代价外,数据在网络站点之间的传输、数据冗余和分布都会对查询的效率产生影响。分布式查询的准则是使通信费最低和响应时间最短,即以最小的总代价在最短的响应时间内获得需要的数据。

(3)分布式事务管理与恢复

在分布式数据库管理系统中,事务管理会遇到在集中式数据库管理系统中遇不到的难题,这些难题主要有同一数据对个别副本一致性保证、单点故障的恢复管理问题、通信网络故障时恢复管理问题等。分布式数据库关系系统中的分布式事务管理程序必须既要保证本地事务的ACID特性又要保证分布式事务的ACID特性。事务管理程序的本地事务管理程序负载保证本地事务的ACID特性,而分布式事务的ACID特性则由两阶段提交协议来保证。在发生故障时,要使得分布式数据库恢复到一个正确的、一致的转态。

(4)分布式并发控制

分布式数据库中的并发控制是以集中式数据库中的并发控制技术为基础的,主要解决多个分布式事务对数据并发执行的正确性。分布式数据库系统并发控制的主要内容包括分布式数据库系统并发控制的封锁技术、分布式数据库系统的死锁处理、分布式数据库系统并发控制的时标技术、分布式数据库系统并发控制的多版本技术、以及分布式数据库系统并发控制的乐观方法等。

(5)分布式数据库的可靠性

分布式数据库的可靠性是指分布式数据库在一个给定的事件间隔内不产生任何失败的概率。它强调分布式数据库的正确性,要求分布式数据库在符合某种要求情况下正确地运行。一个可靠性高的系统要求故障少、容易修复或者修复得快。分布式数据库失败的主要原因有错误的设计、不稳定或临界的组件、不稳定的外部环境和操作者的过失等。分布式数据库的可靠性协议可以保证在分布式数据库上执行的分布式事务的原子性和持久性。

(6)分布式数据库的安全性

分布式数据库系统具有物理上分散而逻辑上集中的特征。计算机网络是实现地理分散而需要集中管理和控制的多个逻辑单位连接起来的途径。网络安全因素同时也成为分布式数据库的安全因素。如何保证开放网络环境中分布式数据库的安全,成为设计、使用分布式数据库时应考虑的一个重要问题。分布式数据库面临的安全问题主要有:单站点故障、网络故障、各类管理制度的不完善、人为攻击、内部人员泄露密码数据、程序内嵌的不安全因素等引起的安全问题等。保证数据库安全的措施主要有建立安全数据模型、设定有效的访问控制机制、建立多级安全数据库、数据加密等。

5.分布式数据库系统的主要应用

分布式数据库系统已经在各个行业有了大量的应用,如移动通信行业、零售业、银行行业、交通运输业、互联网行业等。

(1)移动分布式数据库系统

随着无线技术和分布式数据库技术的发展,在移动通信领域中出现了一种新技术移动计算。移动计算环境促进了无线技术和分布式数据库应用的融合,形成了移动分布式数据库系统。移动数据库技术配合无线定位技术,可以用于智能交通管理、大宗货物运输管理和消防现场作业等。移动数据库还在零售业、制造业、金融业、医疗卫生、军事等领域展现了广阔的应用前景。

移动数据库系统由两部分组成:一部分称为移动部分或客户机,运行在嵌入式移动设备上,包括嵌入式移动应用、移动数据库管理系统及其同步接口、移动数据库;另一部分称为固定部分或服务器,在主机或基站上运行,包括主数据库及其数据库管理系统、其他易购数据库及ODBC、同步服务器及同步协同器。这两部分之间通过网络进行通信,嵌入式移动设备与网络的连接通过无线连接。

(2)超市连锁分布式数据库系统

随着城市建设的发展和现在物流技术的完善,超市业务不断扩大,大型连锁超市越来越普及。连锁超市运营企业要有一个结合地理位置和资源数据的连锁超市管理系统,连锁超市管理系统的后台数据就是一个分布式数据库,只有分布式数据库才能支持业务数据的管理和业务系统的运行。

(3)火车订票分布式数据库系统

随着我国铁路运输的发展,高铁线路的陆续开通,出现了网络售票、电话售票等新形式。为适应铁路客运快速网的建设与发展,满足铁路大提速以及客运新产品销售的需求,以往火车售票系统采用的集中式客户机/服务器体系已经不能满足需要。在当前的情况下,分布式或者分布式与集中式结合的客户机/服务器体系结构就显出它的优势。在中国铁路客票发售方面,数据全部集中到各铁路局数据中心,车站可以不设置服务器。可以按3级体系结构组织,由铁道部数据中心、铁路局数据中心和所辖车站系统组成,从而构成了火车订票分布式数据库系统。

(4)GSP药品管理分布式数据库系统

对药品经营企业来说,实施GSP将成为一种强制性的法律义务。在药品商业企业中,大多存在营业结构分散而管理相对集中的情况,各个营业机构在业务上需要处理各自的数据,同时也需要彼此之间数据的交换和集中管理,以供政府药品监督管理机构以及总公司进行监管和分析。这种既集中又分散的数据管理模式非常符合分布式数据库的特点,因此采用分布式数据库的管理方式对药品商业企业以及下属分公司进行管理是十分合适的。

(5)银行管理分布式数据库系统

对于现在银行运营,信息在运营中的地位尤为关键,如何以较低的成本为代价,更好地存储、保护、处理这些关键的数据信息,就成为了各银行数据中心最为关心的问题。银行地理位置的分散造成了业务数据的分散,这就需要采用分布式数据库技术来解决各类数据信息的存储和管理问题。

3.2数据库架构及数据库应用场景

随着业务规模增大,数据库存储的数据量和承载的业务压力也不断增加,数据库的架构需要随之变化,为上层应用提供稳定和高效的数据服务。

3.2.1数据库架构

数据架构主要分为单机架构和多机架构,其中单机架构有单主机和独立主机,多机架构分为分组和分片,其中分组又分为主备、主从、多主等架构。数据库架构如图3-2-1所示。

图3-2-1数据库架构图

1.单机架构

为了避免应用服务和数据库服务对资源的竞争,单机架构也从早期的单主机模式发展到数据库独立主机模式,把应用和数据服务分开。应用服务可以增加服务器数量,进行负载均衡,增大系统并发能力。单机架构如图3-2-2所示。

图3-2- 2 单机架构

(1)单机架构的优点

单机架构的优点主要体现在能部署集中,运维方便。

(2)单机架构的缺点

在可扩展性方面,数据库单机架构扩展性只有纵向扩展,通过增加硬件配置来提升性能,但单台主机的硬件可配置的资源会遇到上限。单机架构存在单点故障,表现在单机架构在扩容的时候往往需要停机扩容,服务停止,硬件故障导致整个服务不可用,甚至数据丢失。在性能方面上,单机架构会遇到性能的瓶颈。

2.分组架构-主备架构

主备架构,其中主表示主机,备表示备机。数据库部署在两台服务器,其中承担数据读写服务的服务器称为“主机”,另外一台服务器利用数据同步机制把主机的数据复制过来,称为“备机”。在同一时刻,只有一台服务器对外提供数据服务。主备架构如图3-2-3所示。

图3-2- 3主备结构

(1)主备结构的优点

主备结构的优点,应用不需要因为数据库故障来增加开发量,相对单机架构提升了数据容错性。

(2)主备结构的缺点

主备结构的缺点有资源浪费,备机和主机同等配置,备机长期范围内基本上资源限制,无法利用;在性能压力方面还是集中在单机上,无法解决性能瓶颈问题;当出现故障时候,主备机切换需要一定的人工干预或者监控。

3.分组架构-主从架构

主从式架构的部署模式和主备机模式相似,备机上升为从机(Slave),对外提供一定的数据服务。主从架构可以通过读写分离方式分散压力,将写入、修改、删除操作,在主机(写库)上完成;把查询请求,分配到从机(读库)上完成。主从架构如图3-2-4所示。

图3-2- 4主从架构

(1)主从架构的优点

主从架构的优点:提升资源利用率,适合读多写少的应用场景;在大并发读的使用场景,可以使用负载均衡在多个从机间进行平衡;从机的扩展性比较灵活,扩容操作不会影响到业务进行。

(2)主从架构的缺点

主从机构的缺点:延迟问题,数据同步到从机数据库时会有延迟,所以应用必须能够容忍短暂的不一致性;对于一致性要求非常高的场景是不适合的;写操作的性能压力还是集中在主机上;主机出现故障,需要实现主从切换,人工干预需要响应时间,自动切换复杂度较高。

4.分组架构-多主架构

多主架构,数据库服务器互为主从,同时对外提供完整的数据服务。多主架构如图3-2-5所示。

图3-2- 5多主架构

(1)多主架构的优点

多主架构的优点资源利用率较高的同时降低了单点故障的风险。

(2)多主架构的缺点

多主架构的缺点是双主机都接受读写数据,要实现数据双向同步,双向复制同样会带来延迟问题,极端情况下有可能数据丢失;数据库数量增加会导致数据同步问题变得极为复杂,实际应用中多见双机模式。

5.共享存储多活架构

共享存储的多活架构是一种特殊的多主架构,数据库服务器共享数据存储,而多个服务器实现均衡负载。共享存储多活架构如图3-2-6所示。

图3-2- 6 共享存储多活架构

(1)共享存储多活架构的优点

共享存储多活架构的优点是多个计算服务器提供高可用服务,提供了高级别的可用性;可伸缩性,避免了服务器集群的单点故障问题;比较方便的横向扩展能够增加整体系统并行处理能力。

(2)共享存储多活架构的缺点

共享存储多活架构的缺点有实现技术难度大;当存储器接口带宽达到饱和的时候,增加节点并不能获得更高的性能;存储IO容易成为整个系统的性能瓶颈。

6.分片架构

分片架构主要表现形式就是水平数据分片架构。把数据分散在多个节点上的分片方案,每一个分片包括数据库的一部分,称为一个shard,多个节点都拥有相同的数据库结构,但不同分片的数据之间没有交集,所有分区数据的并集构成数据总体。分片后,多个数据库实例也会构成一个数据库集群。常见的分片算法有根据列表值、范围取值和Hash值进行数据分片。分配架构如图3-2-7所示。

图3-2- 7分片架构

分片架构的优点,数据分散在集群内的各个节点上,所有节点可以独立性工作。大部分互联网业务数据量很大,单库容量容易成为瓶颈,通过分片可以线性提升数据库写性能,降低单库数据容量。

7.无共享架构

无共享架构表现为集群中每一个节点(处理单元)都完全拥有自己独立的CPU/内存/存储,不存在共享资源;各节点处理自己本地的数据,处理结果可以向上层汇总或者通过通信协议在节点间流转;节点是相互独立的,扩展能力强;整个集群拥有强大的并行处理能力。无共享架构如图3-2-8所示。

图3-2- 8 无共享架构

8.MPP架构

MPP(Massively Parallel Processing),即大规模并行处理,在数据库非共享集群中,每个节点都有独立的磁盘存储系统和内存系统,业务数据根据数据库模型和应用特点划分到各个节点上,每台数据节点通过专用网络或者商业通用网络互相连接,彼此协同计算,作为整体提供数据库服务。非共享数据库集群有完全的可伸缩性、高可用、高性能、优秀的性价比、资源共享等优势。

简单来说,MPP是将任务并行的分散到多个服务器和节点上,在每个节点上计算完成后,将各自部分的结果汇总在一起得到最终的结果。

MPP架构的特点任务并行执行、分布式计算。常见的MPP产品有无共享Master(Vertica、Teradata)和共享Master(Greenplum、Netezza)。

图3-2- 9无共享Master架构

无共享Master架构的特点,所有节点对等,可以通过任意节点查询或加载数据,不存在性能瓶颈和单点风险,如图3-2-9所示。共享Master架构存在单点故障,如图3-2-10所示。

图3-2- 10 共享Master架构

9.数据库架构特点对比

数据库架构类型较多,他们各自有自己的特点,见表3-1所示。

表3-1数据库架构特点

3.2.2数据库的应用场景

数据库的应用场景,这里主要讲解关系数据库的应用场景。关系数据库的应用场景主要分为联机事务处理(OnLine Transaction Processing,OLTP)和联机分析处理(OnLine Analytical Processing,OLAP)。

1.OLTP

OLTP是传统关系数据库的主要应用,主要面向基本的、日常的事务处理,例如银行储蓄业务的存取交易、转账交易等。

(1)OLTP的特点

大吞吐量,大量的短在线事务(插入、更新、删除),非常快速的查询处理和高并发及实时响应。

(2)典型的OLTP场景

典型的OLTP的场景有零售系统、金融交易系统、火车票销售系统、秒杀活动等。

2.OLAP

OLAP联机分析处理的概念最早是E.F.Codd于1993年相对于OLTP系统而提出的。它是指对数据的查询和分析操作,通常对大量的历史数据查询和分析。OLAP涉及到数据的历史周期比较长,数据量大,在不同层级上的汇总,OLAP的聚合操作使得事务处理操作比较复杂。

(1)OLAP的特点

OLAP的特点主要面向侧重于复杂查询,回答一些“战略性”的问题;在数据处理方面聚焦于数据的聚合、汇总、分组计算、窗口计算等“分析型”数据加工和操作;能从多维度去使用和分析数据。

(2)典型的OLAP场景

OLAP的应用场景主要有报表系统、CRM系统、金融风险预测预警系统、反洗钱系统、数据集市、数据仓库等。

3.2.3数据库性能衡量指标

事务处理性能委员会(Transaction Processing Performance Council,TPC)是由数10家会员公司创建的非盈利组织,总部设在美国。该组织对全世界开放,但迄今为止,绝大多数会员都是美、日、西欧的大公司。TPC的成员主要是计算机软硬件厂家,而非计算机用户,它的功能是制定商务应用基准程序(Benchmark)的标准规范、性能和价格度量,并管理测试结果的发布。

TPC的出版物是开放的,可以通过网络获取。TPC不给出基准程序的代码,而只给出基准程序的标准规范。任何厂家或其它测试者都可以根据规范,最优地构造出自己的系统(测试平台和测试程序)。为保证测试结果的客观性,被测试者必须提交给TPC一套完整的报告,包括被测系统的详细配置、分类价格和包含五年维护费用在内的总价格。该报告必须由TPC授权的审核员核实。现在全球只有几个审核员,全部在美国。

TPC已经推出了几套基准程序,被称为TPC-A、TPC-B、TPC-C、TPC-D、TPC-E、TPC-H。其中A和B已经过时,不再使用了。TPC-C是在线事务处理(OLTP)的基准程序,TPC-D是决策支持(Decision Support)的基准程序。

TPC-C规范是面向OLTP系统,主要包括两个指标,分别为流量指标和性价指标。流量指标即每分钟测试系统处理的事务数量(tpm–transactions per minuete,tpmC),性价比指标为测试系统价格(Price)/tmpC。

TPC-H规范是面向OLAP类系统。流量指标(qphH–Query per hour)即每小时处理的复杂查询数量,查询数量需要考虑测试数据集合大小,分为不同的测试数据集,指定了22个查询语句,可以根据产品微调。测试场景如数据加载、Power能力测试和Througput测试等。

项目实施

小A的论坛在早期使用一台MySQL数据库,随着用户访问量和数据的增多,论坛访问的速度越来越慢并还存在单点故障的危险。小A采用MySQL-Proxy实现MySQL数据的读写分离,设计与部署了Redis数据库集群,优化了MySQL数据库,从而提高数据的访问速度和避免单点故障的危险,实现了论坛的升级改造。

需要完成的任务:

设计与部署MySQL数据读写分离。

数据库集群设计与部署。

MySQL调优。

3.3任务1:MySQL数据读写分离的设计与实现

数据读写分离的基本原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。

3.3.1数据读写分离概述

1.读写分离的原因

为什么要进行数据读写分离?因为数据库的“写”(如写10000条数据到Oracle可能要3分钟)操作是比较耗时的,数据库的“读”(如从Oracle读10000条数据可能只要5秒钟)操作相对耗时少。数据库的写入影响了查询的效率,基于这个原因数据要进行读写分离。

2.数据读写分类场景

数据库不一定要读写分离,什么时候数据要进行读写分离操作呢?如果程序使用数据库较多时,而数据更新少,查询多的情况下会考虑使用数据读写分析,利用数据库的主从同步,可以减少数据库压力,提高性能。

3.主从复制与读写分离

在实际的生产环境中,对数据库的读和写都在同一个数据库服务器中,无论是在安全性、高可用性还是高并发等各个方面都是完全不能满足实际需求的。因此,数据库的架构采用主从架构,如图3-3-1所示,通过主从复制的方式来同步数据,再通过读写分离来提升数据库的并发负载能力。在MySQL数据库中主从复制是对数据库中的数据、语句做备份。

图3-3- 1 主从架构

(1)MySQL的复制类型

①基于语句的复制,在服务器上执行SQL语句,在从服务器上执行同样的语句,MySQL默认采用基于语句的复制,执行效率高。

②基于行的复制,把改变的内容复制过去,而不是把命令再从服务器上执行一遍。

③混合类型的复制,默认采用基于语句的复制,一旦发现基于语句无法精确复制时,就会采用基于行的复制。

(2)复制的工作过程

在每个事务更新数据完成之前,Master服务器在二进制日志记录这些改变。写入二进制日志完成后,Master通知存储引擎提交事务。

Slave服务器将Master服务器的Binary Log复制到其中继日志。首先Slave开始一个工作线程(I/O),I/O线程在Master上打开一个普通的连接,然后开始Binlog Dump Process进程,Binlog Dump Process进程从Master的二进制日志中读取事件,如果已经跟上Master服务器,它会睡眠并等待Master服务器产生新的事件,I/O线程将这些事件写入中继日志。

Sql slave thread(sql从线程)处理该过程的最后一步,sql线程从中继日志读取事件,并重放其中的事件而更新Slave服务器中数据,使其与Master服务器中的数据一致,只要该线程与I/O线程保持一致,中继日志通常会位于OS缓存中,所以中继日志的开销很小。

4.MySQL读写分离原理

读写分离就是在主服务器上修改,数据会同步到从服务器,从服务器只能提供读取数据,不能写入,实现备份的同时也实现了数据库性能的优化,以及提升了服务器安全。

5.较为常见的MySQL读写分离方式

较为常见的MySQL的读写分离方式主要有基于程序代码内部实现和基于中间代理层实现。

(1)基于程序代码内部实现

在基于代码内部实现数据读写分离,在代码中根据select、insert进行路由分类,这类方法也是目前生产环境下应用最广泛的。优点是性能较好,因为程序在代码中实现,不需要增加额外的硬件开支,缺点是需要开发人员来实现,运维人员无从下手。

(2)基于中间代理层实现

代理一般介于应用服务器和数据库服务器之间,代理数据库服务器接收到应用服务器的请求后根据判断后转发到后端数据库。中间代理主要有:

①mysql_proxy。mysql_proxy是MySQL的一个开源项目,通过其自带的lua脚本进行sql判断。

②Atlas。是一个基于MySQL协议的数据中间层项目。它是在mysql-proxy 0.8.2版本的基础上,对其进行了优化,增加了一些新的功能特性。360企业内部使用Atlas运行的mysql业务,每天承载的读写请求数达几十亿条,支持事物以及存储过程。

③Amoeba。由阿里巴巴集团使用序Java语言进行开发,但是他并不支持事物以及存储过程。

3.3.2 MySQL Proxy搭建数据读写分离

为了确保数据库产品的稳定性,很多数据库拥有双机热备功能。也就是,第一台数据库服务器,是对外提供增删改业务的生产服务器;第二台数据库服务器,主要进行读的操作。本节采用MySQL Proxy来实现MySQL数据库的读写分离。

一、实验环境与实验思路

1.实验环境搭建

(1)操作系统:CentOS,关闭防火墙和SELinux。

(2)数据架构:采用主从架构,如图3-3-2所示。

(3)IP规划:主数据库服务器Master的IP地址为192.168.1.100,角色主库,server-id:1,由于服务器有限,将Proxy搭建在主数据库服务器上;从数据库服务器Slave的IP地址为192.168.1.200,角色从库,server-id:2。

(4)软件工具:MySQL,mysql-proxy-0.8.5-linux-el6-x86-64bit。

图3-3- 2 数据库架构

2.实验思路

(1)下载MySQL Proxy,在代理服务器Proxy上安装lua语言。

(2)在代理服务器Proxy安装Proxy,添加/etc/profile的环境变量参数。

(3)搭建主从服务器,安装MySQL数据库。

(4)在代理服务器上修改Proxy配置文件参数,测试读写分离。

(5)创建测试数据库表并授权用户访问权限。

(6)启动Mysql-Proxy测试读写分离。

二、实验过程

1.MySQL Proxy的下载,在代理服务器Proxy上安装lua语言

MySQL Proxy是MySQL官方提供的MySQL中间件服务,上游可接入若干个MySQL-Client,后端可连接若干个MySQL-Server。它使用MySQL协议,任何使用MySQL-Client的上游无需修改任何代码,即可迁移至MySQL Proxy上。

MySQL Proxy是一个官方提供的框架,具备良好的扩展性,它的主要功能有:SQL拦截与修改、性能分析与监控、读写分离、请求路由等。

(1)MySQL Proxy的下载

进入MySQL Proxy官网网站下载,找到对应操作系统版本的MySQL Proxy安装包,点击“Download”进行下载,如图3-3-3所示。

图3-3- 3 下载MySQL Proxy

(2)Liunx安装lua语言解析器

【步骤1】到官网中lua下载源码包,如图3-3-4所示。

图3-3- 4下载lua

【步骤2】安装lua。用tar命令解压lua,对加压的目录用mv命令重新命名,如解压目录为/usr/soft/lua,对解压后的文件名重命名为lua-5.4,如图3-3-5所示。

图3-3- 5加压lua

【步骤3】修改Makefile文件,用vim文本编辑器,修改INSTALL_TOP=安装目录,如安装目录为/usr/soft/lua/lua-5.4,如图3-3-6所示。

图3-3- 6 修改Makefile文件

【步骤4】make安装。在make前要先安装gcc编辑工具,通过yum –y install gcc命令进行安装,安装好gcc后,再使用make命令进行安装,如图3-3-7所示,代码如下:

yum -y install gcc
make
图3-3- 7 make安装

【步骤4】lua安装成功。进入lua安装路径中src目录,输入lua命令,进入lua界面,到这里说明lua已经安装成功,如图3-3-8所示。

图3-3- 8 lua安装成功

2.在代理服务器Proxy安装Proxy

【步骤1】下载MySQL Proxy,在官网上下载。

【步骤2】解压安装包。用tar命令解压,用mv命令对解压的文件重新命名为proxy,如图3-3-9所示。

图3-3- 9解压lua安装包

【步骤3】修改环境变量。用vim文件编辑器修改/etc/profile文件,添加mysql-proxy安装路径下的bin目录,代码如下:

vi /etc/profile
#或者最后添加
PATH=$PATH:/usr/soft/mysql-proxy/proxy/bin
PATH=$PATH:/usr/soft/mysql/mysql8.0/bin

配置文件生效,代码如下:

source /etc/profile 

【步骤4】修改Proxy代理的lua脚本文件。lua脚本文件是位于mysql-proxy安装目录下share/doc/mysql-proxy目录中rw-splitting.lua文件。如安装路径为mysql-proxy,进入share/doc/mysql-proxy目录,找到rw-splitting.lua文件,如图3-3-10所示。

图3-3- 10 找到rw-splitting.lua

用vim文本编辑rw-splitting.lua脚本文件,修改min_idle_connetions=1,表示超过一个连接时,实现读写分离,修改max_idle_connections=1,如图3-3-11所示。

图3-3- 11修改配置lua脚本文件

【备注】官方的rw-splitting.lua是限制4个以下的连接读写分离是不起作用的。多余四个连接才会启到读写分离的作用,所以需要修改min_idle_connetions和max_idle_connections的值。

【备注】在Proxy服务器中要管理防火墙和SELinux,代码如下:

systemctl stop firewalld  #关闭防火墙
systemctl status firewalld   #查看防火墙状态
getenforce      #设置SELinux状态
Disabled
setenforce 0       #临时关闭SELinux状态

3.在Master主服务器上安装MySQL数据库

在安装MySQL数据库前先关闭防火墙和SELinux。安装MySQL数据库可以采用源码安装也可以采用yum命令来安装,如在CentOS使用yum命令安装MySQL5.7版本,代码如下:

yum install -y mysql-community-server

在CentOS中安装MySQL5.7,采用二进制的安装方法,步骤如下:

【步骤1】检查是否安装MySQL数据库。在CentOS系统中安装MySQL数据库,有的Linux版本中自带MySQL数据库,在安装之前,检查MySQL数据库是否安装,代码如下:

rpm -qa | grep mysql

【备注】执行rpm -qa | grep mysql 命令来查看当前系统是否自带有MySQL数据库,若有则会显示MySQL软件名称。

如果有自带的MySQL数据库,则卸载自带的MySQL数据库,命令rpm -e --nodeps 要卸载的软件名或者yum remove命令用于卸载软件。

【步骤2】上传MySQL至Linux,可以使用很多种方式进行文件的上传,如使用WinSCP软件上传到Linux系统,如图3-3-12所示。

图3-3- 12使用WinScp上传MySQL安装包

【备注】MySQL版本采用的是MySQL-8.0.20(Generic版本)。

【步骤3】解压MySQL软件包。在/usr/目录下使用mkdir命令新建soft目录,在该目录下新建mysql目录,用于存放MySQL文件,通过tar -xvf命令,将MySQL安装包解压到该目录下,如图3-3-13所示。解压后的目录用mv命令修改为mysql8.0。

图3-3- 13解压MySQL安装包

在/usr/soft/mysql/mysql5.7目录下创建到/usr/soft/mysql/mysql8.0的软链接,建立一个mysql的软连接,是为了以后方便配置与操作,代码如下:

cd /usr/soft/mysql/mysql8.0
ln -s /usr/soft/mysql/mysql8.0 mysql

【步骤4】创建mysql用户,代码如下:

groupadd -r mysql
useradd -r -g mysql -s /bin/false mysql

【步骤5】创建数据库文件,并修改其权限。二进制安装包经解压后得到的MySQL目录,内部并未包含数据文件存放的目录,所以需要单独再创建一个子目录用于存放数据文件。在MySQL解压目录如mysql8.0中创建数据库文件为data,用chown –R指定其用户,用chmod命令修改权限,如图3-3-14所示,代码如下:

cd mysql
mkdir -p /usr/soft/mysql/mysql8.0/data/
chown -R mysql:mysql /usr/soft/mysql/mysql8.0/data/
chmod 750 /usr/soft/mysql/mysql8.0/data/
图3-3- 14 创建数据库并修改权限

【步骤6】初始化MySQL。从版本5.7.6开始,MySQL初始化使用mysqld --initialize命令,不再使用mysql_install_db命令了。进入到MySQL安装目录下的bin目录,执行mysqld–initialize命令,如图3-3-15所示。执行初始化后生成初始化密码,注意要保存初始化密码,登录MySQL数据库时要使用初始化密码。代码如下:

./mysqld --initialize --user=mysql --basedir=/usr/soft/mysql/mysql8.0
--datadir=/usr/soft/mysql/mysql8.0/data
图3-3- 15初始化MySQL

【步骤7】查看生成的数据库文件。用cd命令进入到data文件夹中,用ll令查看生成的数据库文件,如图3-3-16所示。

图3-3- 16 查看生成的数据库文件

【步骤8】创建RSA private key。进入到MySQL的bin目录中,代码如下:

./mysql_ssl_rsa_setup --datadir=/usr/soft/mysql/mysql8.0/data

【步骤9】生成配置文件my.cnf。采用touch命令生成配置文件my.cnf,用vim文本编译配置文件,代码如下:

#touch /etc/my.cnf(注意备份原来的my.cnf)
#vim /etc/my.cnf

my.cnf配置文件参考代码如下:

[client]
port = 3306
default-character-set=utf8
[mysqld]
character_set_server=utf8
init_connect='SET NAMES utf8'
#配置安装的mysql目录
basedir=/usr/soft/mysql/mysql8.0
#配置安装的mysql目录下的data目录
datadir=/usr/local/mysql/data
socket=/tmp/mysql.sock
[mysqld_safe]
lower_case_table_names = 1
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

【步骤10】设置启动服务。将support-files目录的mysql.server文件复制到/etc/init.d目录中,并取名为mysqld文件,如图3-3-17所示,代码如下:

cp support-files/mysql.server /etc/init.d/mysqld
图3-3- 17复制mysql.server文件

修改mysqld配置文件,修改其中basedir和datadir中的值,分别为MySQL的安装目录和MySQL安装目录下的data目录,如图3-3-18所示。

图3-3- 18 修改mysqld配置文件

【步骤11】添加启动服务,如图3-3-19所示,代码如下:

#chkconfig --add mysqld
图3-3- 19 添加服务

启动服务,如图3-3-20所示,代码如下:

#service mysqld start
图3-3- 20 启动mysqld服务

查看服务状态,代码如下:

systemctl status mysqld

【步骤12】登录MySQL数据库。在本机登录MySQL服务,从5.7.10版本开始不允许root空密码登录了。其实在执行mysqld --initialize后系统就会为root生成一个初始化密码,并在屏幕标准输出时显示出来,用这个密码第一次登录后,才能再修改密码,登录MySQL命令如下:

mysql  -u root –p 

【步骤13】修改root账户的密码。安装好MySQL8.0后,root账号登录采用mysql -uroot –p命令和初始密码登录很不方便,需要修改初始密码。

(1)修改my.cnf配置文件。进入配置文件vim /etc/my.cnf添加代码skip-grant-tables即可跳过mysql密码验证进行登录。

(2)重启mysqld服务,命令如下:

service mysqld restart

(3)重新登录MySQL数据库,无密码登录,密码可以随便填写。

(4)进入MySQL数据库,修改user表,将root账户密码置空,如图3-3-21所示,代码如下:

use mysql;
update user set authentication_string='' where user = 'root'; 
图3-3- 21 设置root账户代码为空

(5)修改root账户密码。此时输入“ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码'; ”修改密码,代码如下:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';

【注意】密码设置要复杂一些,像123456这样的会提示重新设置复杂的密码。密码修改成功如图3-3-22所示。

图3-3- 22设置密码

(6)刷新MySQL相关系统权限表,退出MySQL数据库。

FLUSH privileges;
quit;

(7)进入配置文件vim /etc/my.cnf将skip-grant-tables这行注释,在前面加#即可注释。

(8)重新启动mysql,输入刚才设置的密码进入mysql,这样root账户的密码就修改成功了。

【备注】在从服务器上安装MySQL数据库与在主服务器上安装MySQL数据库操作过程类似。

4.启动代理服务器,测试MySQL授权用户登录

(1)启动代理服务器

实验中的代理服务器与主数据库服务器Master是用的一台服务器,在配置mysql-proxy配置文件参数之前,先要清楚主从数据库服务器的MySQL数据库的端口和IP地址。

(2)MySQL端口,通过SQL命令“show variables like ‘port’;”查看,如图3-3-23所示。

图3-3- 23 查看MySQL端口

(3)主从数据库服务器的IP地址。主数据库服务器Master的IP地址为192.168.1.100,从数据库服务器Slave的IP地址为192.168.1.200。

(4)启动代理服务器。进入到MySQL-Proxy的安装路径下的bin目录中,启动mysql-proxy,如图3-3-24所示,代码如下:

./mysql-proxy
--proxy-read-only-backend-addresses=192.168.1.100:3306
--proxy-backend-addresses=192.168.1.200:3306
--proxy-lua-script=/usr/soft/mysql-proxy/proxy/share/doc/mysql-proxy/rw-splitting.lua&
图3-3- 24启动mysql-proxy

【备注】--proxy-read-only-backend-addresses=192.168.1.100:3306表示定义后端只读服务器;--proxy-backend-addresses=192.168.1.200:3306 #定义后端MySQL主服务器地址,指定MySQL写主服务器的端口;/usr/local/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua &表示指定lua脚本,在这里使用的是rw-splitting脚本,用于读写分离。

启动mysql-proxy还可以使用如下命令:

./mysql-proxy -P 192.168.1.100:4040 -r 192.168.1.200:3306
 -b 192.168.1.100:3306 
-s /usr/soft/mysql-proxy/proxy/share/doc/mysql-proxy/rw-splitting.lua &

【备注】-P后面跟代理服务器IP及端口号默认端口4040;-r后面跟从服务器IP及端口号,可以指定多个从服务器;-b后面跟主服务器IP和端口号,-s后面跟调用的脚本。

检查mysql-proxy是否后台启动,命令如下:

ps -ef | grep mysql-proxy
kill -9 进程号 #停止进程

(5)MySQL数据库授权命令

MySQL授权命令,新版的MySQL8.0版本已经将创建账户和赋予权限的方式分开了,创建用户代码如下:

CREATE USER 'username'@'%' IDENTIFIED BY 'yourpassword';

赋予权限:

GRANT ALL PRIVILEGES ON *.* TO 'username'@'%';

刷新权限:

mysql>flush privileges; 

(6)主从服务器增加数据库用户

主数据库服务器Master的IP地址为192.168.1.100登录,创建user1用户,并授权,执行命令:

CREATE USER 'user1'@'%' IDENTIFIED BY '123456';
GRANT ALL PRIVILEGES ON *.* TO 'user1'@'%';

从数据库服务器Slave的IP地址为192.168.1.200登录,执行命令:

CREATE USER 'user1'@'%' IDENTIFIED BY '123456';
GRANT ALL PRIVILEGES ON *.* TO 'user1'@'%';

(7)测试主从数据库用user1账户登录

用MySQL的界面工具,连接主数据库服务器Master的IP地址为192.168.1.100用授权用户登录和从数据库服务器Slave的IP地址为192.168.1.200用授权用户登录,如授权用户为user1,如图3-3-25所示。

图3-3- 25 界面工具

5.配置MySQL主从数据库服务器并实现读写分离

(1)Master主服务器配置

修改/etc/my.cnf配置文件,并授权user1用户为同步用户。

vi /etc/my.cnf
#有两个关键配置项:
[mysqld]
server-id = 1
log-bin =mysql-bin

【备注】主库的server-id要和从库的server-id区分开,主库要做log-bin,二进制日志用来供主从复制时使用。在mysql8中,binlog_format变量的默认值是row,不再手动声明这个配置变量。

登录MySQL数据库,设置授权user1用户,代码如下:

CREATE USER 'user1'@'%' IDENTIFIED BY '123456';
GRANT ALL PRIVILEGES ON *.* TO 'user1'@'%';
#刷新权限
FLUSH PRIVILEGES;

重启数据库。

(2)Slave从服务器配置

Slave从服务器配置有两种方法,方法一是修改/etc/my.cnf配置文件,方法二是登录MySQL数据库进行设置。

方法一:修改/etc/my.cnf配置文件

 vi /etc/my.cnf 
[mysqld]
master_host='192.168.1.100'   #Master主服务器的ip
master_user='user1'      #授权用户名
master_password='123456'  #授权用户的密码
server-id=2

确定当前二进制日志文件的名称和位置。查看Master的日志文件,代码如下:

show master status;

方法二:登录MySQL数据库,代码如下:

mysql> CHANGE MASTER TO
MASTER_HOST='192.168.1.100',  # 主节点地址
MASTER_USER='user1',      # 主节点用户
MASTER_PASSWORD='123456',    # 主节点密码
MASTER_LOG_FILE='binlog.000002', # 二进制日志文件
MASTER_LOG_POS=866;   # 二进制日志位置
Query OK, 0 rows affected, 2 warnings (0.05 sec)
mysql> START SLAVE;
Query OK, 0 rows affected (0.04 sec)
mysql> SHOW SLAVE STATUS\G
图3-3- 26 配置slave从服务器

在配置Slave从服务器时,通过show salve status\G命令,如果发现Slave_IO_Running:no或者Slave_SQL_Runing:no,则说明主从服务器读写配置不成功。出现这种错误需要一一排查错误的原因,如主从服务器的my.cnf配置文件中sercer-id值相同的问题、主从服务器的auto.cnf值相同问题、用户授权问题、二进制文件是否一致等问题、主从服务器是够关闭防火墙或SELinux。参考命令:

mysql>show variables like ‘server_id’;  #查看server_id
mysql>show master status;   #查看主从服务器的二进制文件
find / -name 'auto.cnf'   #查看主从服务器的auto.cnf文件
vi /etc/my.cnf       #编辑my.cnf配置文件
service mysqld restart    #重启mysql服务
systemctl stop firewalld.service  #关闭防火墙
systemctl status firewalld.service  #查看防火墙状态
getenforce          #当前SELinux的状态
setenforce 0         #设置SELinux临时关闭

6.测试主从节点

在Master主服务器中,登录MySQL数据库,创建数据库da3数据库,SQL语句如下:

create database da3;
create table users(id int(4),
  -> name varchar(50));
insert into users values(1,'data1');

在Slave从服务器中,登录MySQL数据库,测试数据库da3是否存在,users表是否存在,用select语句查询表中数据,SQL语句如下:

show databases;
use da3;
show tables;
select * from users;

SQL语句在Master主服务器和Slave服务器中执行情况如图3-3-27所示,则基于主从架构的MySQL-Proxy的MySQL数据读写分离成功。

图3-3- 27 测试成功

3.4 任务2:数据库集群设计与部署

数据库集群是利用至少两台或者多台数据库服务器,构成一个虚拟单一数据库逻辑映像,像单数据库系统那样,向客户端提供透明的数据服务。

3.4.1数据库集群的简介

数据库集群需要至少两台以上的数据库服务器,提供透明服务。透明服务时指向客户端提供的服务与单机系统向客户端提供的服务,从通讯协议上保持二进制兼容。

1.相关概念

(1)同步

数据库集群的同步是指数据库客户端发出数据更新请求后,要等集群的每个节点全部更新后,才给客户端返回结果。

(2)异步

数据库客户端发出数据更新请求后,接受请求的节点主要是指主节点立马给客户端返回结果,被更新的数据则会在接下来的某个时间里被复制传输到集群的其它节点上。

(3)基于连接的负载均衡

基于连接的负载均衡实现技术比较简单,就是在客户端发起登陆的时候,按照某种负载均衡算法,选择登陆到集群某台数据库,此后所有客户端的请求全部会发送到此数据库上。

(4)基于请求的负载均衡

基于请求的负载均衡实现技术比较复杂,但是功能强大,就是在客户端发起登陆的时候,集群网关会同时登录到集群各节点数据库,此后所有的客户端请求,经过集群网关的分析被分成两类,查询请求根据负载均衡算法挑选一个节点执行,数据更新请求则有主机执行并实时同步数据到集群各节点。

2.数据库集群与分布式数据库的区别

数据库集群有的具有单份数据集,有的具有两份或多份相似的数据集,有的具有两份或多份实时一致的数据集;而分布式数据库系统往往具有完全不同的数据集。

数据库集群往往是同构的系统,要求集群各节点都具有相同的操作系统和数据库系统版本,甚至补丁包的版本也要求保持一致;而分布式数据库系统可以是异构系统,包含不同的操作系统和不同的数据库系统。

数据库集群往往建立在高速局域网内;而分布式数据库系统既可以是高速局域网,也可以是跨部门、跨单位的异地远程网络。

3.数据库集群的分类

(1)基于串行数据复制技术

串行复制技术是用于数据传送和数据备份的,但是由于计算机软硬件技术和网络通讯技术的快速发展,使得利用这种概念和技术构成的“数据库集群”有了一定的可行性。此类集群,又可以分两类串行异步复制和串行同步复制。

①串行同步复制是基于专用软硬件来实现,是采用专用的高速网络和软件技术,将每个数据库的请求,通过同步复制的方式,同步在主备两台数据库服务器上执行正确后,才将结果返回给数据库客户。

串行同步复制技术的特点:主数据库被强迫与备份数据库同步串行处理,因此性能会受到限制;主备数据库中任意一个出现问题,都会迫使事务处理交易回滚,因此整个系统的可靠性比单机系统降低了一半;适用于近距离光纤网络(如5英里内);专用系统造价昂贵,又加上述明显缺陷,因此市场上很少被采用。

②串行异步复制是数据的异步串行复制。主要采用数据库事务日志传送或者硬盘数据块传送技术来实现,SQL Server自带的复制、镜像和SQL2012以及第三方的一些镜像技术都是属于此类产品,此类技术和产品本质上就是数据备份技术和产品。以事务日志传送为例来说明,主数据库完成事务处理后,生成事务处理日志,日志记录通过FIFO队列,进入备份数据库处理,从而得到备份数据。

(2)基于共享存储的双机容错技术

基于共享存储的双机容错技术是两个服务器共享一个磁盘阵列,两个服务器共享一个虚拟的IP供数据库客户使用,形成一个单一的逻辑数据库映象。这种数据库集群的目的是,一旦主机系统出现问题,备份系统通过心跳机制的检测,完成从主机系统到备份系统的切换。这种方案在市场上被称为“双机集群”或者“双机热备”。

基于共享存储的双机容错技术的特点:它的应用主要在无状态系统(如典型的Web服务器);此系统本身只有一个单一的数据映象,数据储存在共享的磁盘阵例上,因此共享的磁盘阵列成为了整个系统的单点错误源;由于是单一数据映象,因此必须采用通常的复制或备份方法获取第二份数据,以保证数据的安全性;主机系统和备份系统之间是没有任何负载均衡关系的,在正常情况下,备份系统是闲置在那里,因此对用户来说是一种投资浪费;在错误切换的时候,往往存在切换时间长,而且更严重的是可能会存在丢失用户交易数据的现象,结果导致系统被迫停止服务,或者需要人工修复数据,或者数据永远找不回来。

3.4.2基于Redis数据库集群设计与实现

Redis能在服务器重启的情况下也能保障数据的存储,如果数据存储在一台服务器上,会出现单点故障问题。为了避免单点故障,通过的方法是将数据库复制多个副本部署到其他的服务器上,这样如果一台服务器出现挂载,其他服务器依然可以是继续提供服务。Redis提供了复制的功能,当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库服务器中。

1.数据集群架构设计

在数据库复制的概念中,数据库分为两种类型,一类是主数据库(Master),另一类是从数据库(Slave)。主数据库可以进行读写操作,当操作导致数据变化时会自动将数据同步给从数据库。从数据库一般是只读的,并接受主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能拥有一个主数据库,如图3-4-1所示。

图3-4- 1 主从架构

2.Redis数据库简介

Redis是一个开源的使用ANSIC语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value型数据库,并提供多种语言的API。

(1)存储结构

Redis数据库是以字典结构存储数据,如dict“key”=“value”中,其中dict就是一个字典结构,字符串“key”表示键名,字符串“value”表示键值,在字典结构中通过键名来获取键值。在Redis数据库中,字典的键值可以设置为字符串,也可以设置为其他数据类型,目前Redis支持的键值的数据类型可以设置为字符串、散列、列表、集合和有序集合等类型。

如在程序中使用post存储一篇文章的数据(包括标题、正文、阅读量和标签):

post[“title”]= “hello”;
post[“content”]= “新年好新年好”;
post[“views]= 5;
post[“tags”]= [“php”, “HTML”, “Node.js”, “HTML”];

(2)内存存储与持久化

Redis数据库中的所有数据都存储在内存中,内存的访问速度要远高于硬盘,因此Redis在性能上比基于硬盘存储的数据库中在读写数据速度上有明显的优势。一台普通的电脑,Redis可以在1秒读写超过10万个键值。

将数据存在内存也会存在如程序退出后内存中的数据就会丢失的危险,Redis数据库提供了持久化的存储,可以同步将内存中的数据异步写入硬盘中,不影响后续的服务。

(3)功能丰富

Redis虽然是作为数据库进行开发,但其提供了丰富的功能,越来越多的人将其用于缓存、队列系统等。

Redis为每一个键设置了生存时间,生存时间到期后,键就会自动被删除,这一功能使得Redis可以作为缓存系统来使用。作为存储系统,Redis可以限定数据占用的最大的内存空间,数据在空间限制后可以按照一定的规则自动淘汰不需要的键。Redis的列表类型键可以用来实现消息队列,并且支持阻塞式读取,可以很容易实现一个高性能的优先队列,Redis还支持“发布/订阅”的消息模式。

(4)使用简单

Redis直观的存储结构使得通过程序与Redis交互十分容易,在Redis中使用命令来读写数据。如在Redis中读取键名为post:1的散列类型键的title字段值,可以使用如下命令:

HGET post:1 title

HGET是一个读取命令。Redis提供了100多个命令,但是常用的命令却只有十几个。

3.在主从数据库服务器中Redis数据库的安装

以CentOS系统为例,安装Redis数据库,Redis版本为6.0.6版本。

【步骤1】下载Redis数据库,可以用wget命令下载。

【步骤2】解压安装包,用tar –zxf命令解压,用mv命令重命名redis-6.0.6,如图3-4-2所示。

图3-4- 2 加压安装包

【步骤3】升级gcc编译工具。用make命令编译Redis,需要用gcc工具,由于gcc工具用yum命令来安装的,yum命令安装的gcc版本为4.8.5版本。用make编译Redis时由于gcc版本过低会导致编译不成功,需要对gcc进行升级,gcc升级版本为9.0。

gcc的升级可以使用scl软件集方式升级,命令如下:

#安装scl源
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
#这句是临时的
scl enable devtoolset-9 bash
#修改环境变量
echo "source /opt/rh/devtoolset-9/enable" >> /etc/profile
gcc -v

gcc的升级也可以使用源码安装方式,先下载gcc9.0,然后用tar –zxf解压,用mv重新命名解压后的文件夹名字。如图3-4-3所示。

图3-4- 3 解压gcc

运行./contrib/download_prerequisites,这个脚本会自动下载所需要的依赖文件和库,这个过程需要点时间。如图3-4-4所示。

图3-4- 4 下载所需要的依赖文件和库

【备注】如果下载依赖包失败,可以自行下载gmp、mpfr、mpc分别去编译安装。

用mkdir创建build目录,用cd命令进入build目录,执行…/configure -enable-checking=release -enable-languages=c,c++ -disable-multilib,这时编译生成的Makefile文件就该目录中。

进入创建build目录,用make命令编译安装,编译过程也要一段时间。安装好后,最后用gcc –v命令查看gcc的版本。

【步骤4】用make编译,这时Redis数据库就安装成功了。在Redis文件夹的src目录下存放着redis服务程序redis-server,用于测试的客户端程序redis-cli,如图3-4-5所示。

图3-4- 5 Redis安装后的src目录

用make命令编译,然后再make instal安装,最后用gcc –v命令查看gcc的版本。

【步骤5】启动Redis服务。安装好Redis后,就可以启动它。先了解Redis的可执行文件,见表3-2所示。

表3-2 Redis可执行文件

启动Redis服务就需要运行redis-server程序,redis-cli是自带的客户端命令行工具,是学习Redis数据库重要的工具。

进入安装目录下的src文件夹,执行redis-server可执行文件,就可以启动Redis服务了,命令如下:

$ cd src
$ ./redis-server

启动Redis服务,界面如图3-4-6所示。

图3-4- 6启动Redis服务

【步骤6】启动Redis的客户端程序,就可以使用测试客户端程序redis-cli,代码如下:

$ cd src
$ ./redis-cli

服务器地址为127.0.0.1,默认端口号为6379,通过-h和-p参数可以自定义地址和端口号,如图3-4-7所示

图3-4- 7启动客户端程序

【步骤7】停止Redis。在停止Redis服务时,需要将内存中的数据同步到硬盘中,强行终止Redis进程可能会导致数据丢失,正确停止Redis的方式为,先向Redis发送“SHUTDOWN”命令,命令如下:

$redis-cli SHUTDOWN

当Redis收到“SHUTDOWN”命令后,会先断开所有客户端连接,如图3-4-8所示。然后根据配置执行持久化,最后完成退出。

图3-4- 8停止Redis服务

4.Redis复制功能的配置

在Redis中使用复制功能比较容易,只要在从数据库的配置文件中加入“slaveof 主数据库地址 主数据库端口”即可,主数据库无需进行任何配置。

操作过程,在一台主服务器上启动两个Redis实例,监听不同的端口,其中一个作为主数据库,另一个作为从数据库,首先不加任何参数来启动一个Redis实例作为主数据库,代码如下:

$redis-server

该实例的默认监听端口为6379。然后加上slaveof参数启动另一个Redis实例作为从数据库,并让其监听6380端口,代码如下:

$redis-server –port 6380 --salveof 127.0.0.1 6379

此时在主数据库中的任何数据变化都会自动地同步到从数据库中,通过打开redis-cli主数据库,代码如下:

$redis-cli -p 6379

在打开redis-cli连接从数据库,代码如下:

$redis-cli -p 6380

使用INFO命令来分别从主数据库实例和从数据库实例中获取Replication节的相关信息,主数据库代码如下:

INFO replication
Role:master
Connected_slaves:1
Salve0:ip=127.0.0.1,port=6380,state=online,offset=1,lag=1
Master_rep1_offset:1

可以看到,角色为master,为主数据库,同时连接的从数据库信息。

在从数据库代码,输入INFO repliaction,代码如下:

INFO replication
Role:slave
master_host:127.0.0.1
master_host:6379

【实例3-4-1】测试主从数据同步。

在主数据库中,使用set命令设置一个键值,代码如下:

set foo bar
ok

在从数据库中,用get命令获取该值,代码如下:

get foo
“bar”

【备注】在默认的情况下,从数据库是只读的,如果直接修改从数据库的数据,则会出现错误。

如在从数据库中,使用set命令设置键,代码如下:

set fa sa
(error)READONLY You can’t write against a read only slave

可以通过设置从数据库的配置文件中的slave-read-only的值为no,以使得从数据库是可以写的。但是对从数据库的更改都不会同步给其他任何数据库,并且一旦主数据库中更新了对应的数据就会覆盖从数据库中的改动,所以通常情况下,不应该设置从数据库可写,以免导致被忽略的潜在应用逻辑错误。

配置多台从数据库的方法也是一样的,在所有的从数据库的配置文件中都加上slaveof参数指向同一个主数据库即可。

除了用slavof参数设置外,还可以使用SLAVEOF命令来修改,代码如下:

SLAVEOF 127.0.0.1 6379

如果该数据库已经是其他主数据库的从数据库了,SLAVEOF命令会停止和原来数据库的同步转为和新数据库同步。对从数据库而言,可以使用SLAVEOF NO ONE命令来使当前数据库停止接收其他数据库同步转换成为主数据库。

3.5任务3:MySQL调优

影响数据库服务器性能的因素有很多,其中主要因素有如超高的每秒查询率(Query Per SecondQPS,QPS)和每秒传输的事物处理个数(Transactions Per Second,TPS)导致SQL处理效率下降、大量的并发导致的数据库连接数被占满和超高的CPU占用率导致资源耗尽服务器宕机、磁盘IO性能瓶颈导致数据传输效率下降、计划任务导致磁盘IO下降、网卡IO性能瓶颈、减少从服务器数量、缓存分级以及避免使用select *这样的查询等。

MySQL数据库性能优化维度主要从硬件层、系统层、软件层、表结构、SQL语句、索引、以及架构层进行优化。

3.5.1 MySQL硬件层的优化

MySQL硬件层的优化采用磁盘阵列来替代单块磁盘,这是最基本的硬件层改造方式,目前大多数厂商提供的PC基本上都集成了RAID控制器。所以这样做一般不需要额外增加购买硬件设备的费用。在MySQL官网上并没有明确推荐使用哪一种磁盘阵列模式,但是从搭建磁盘阵列支持MySQL的实际引用情况来看,更多是使用RAID 10阵列模式,RAID 10阵列模式可以在提升了整个系统I/O性能的基础上兼顾了存储的安全性。使用RAID 10磁盘阵列模式,至少需要准备4块磁盘,其中两块的磁盘容量用来存储数据冗余,另外两块的磁盘容量用来分散存储数据。

3.5.2 MySQL数据库的引擎

MySQL数据库中最重要的一个概念就是数据库引擎,不同的数据库引擎的工作原理存在很大差异最终造成MySQL数据库服务的性能差异。如数据库引擎需要支持事务,就必须满足事务的基本特性,那么就需要一定处理机制来实现这些特性。这样做的现实效果就是在写入同样数据量的情况下,支持事务的数据库引擎比不支持事务的数据库引擎耗费更多的时间。

1.MEMORY引擎

MEMORY存储引擎是将表的数据完全存放在内存中,如果MySQL或者服务器重新启动,数据库引擎中保存的数据将会丢失。

2.BLACKHOLE引擎

BLACKHOLE引擎中文名“黑洞”,使用BLACKHOLE引擎的数据表不存储任何数据,只根据数据库操作过程记录二进制日志。它的主要作用是作为MySQL主从复制的中继器,并且可以在其上面添加业务过滤机制。

3.MyISAM引擎

MyISAM数据库引擎是MySQL数据库默认的数据库引擎。MyISAM使用一种表格锁定的机制,来优化多个并发的读写操作,但是这种机制对存储空间的使用有一定的浪费。MyISAM还有一些有用的扩展,例如用来修复数据库文件的MYISAMCHK工具和用来恢复浪费空间的MYISAMPACK工具。

4. InnoDB引擎

InnoDB数据库引擎是在各种版本的MySQL数据库中使用最广泛的一种数据库引擎,它是使用日志机制提供事务的支持。

InnoDB数据库引擎采用日志来解决这个问题,它还有另外一个名字叫重做日志(redo log),这是因为这部分日志主要的作用就是在数据库异常崩溃并重启后进行InnoDB引擎中数据的恢复。

为了提高MySQL数据库的性能,InnoDB数据库引擎的数据操作过程基本上都在内存中完成,然后通过一定的策略将InnoDB Log Buffer内存区域中的日志数据同步到磁盘上的InnoDB File Log Group区域。InnoDB File Log Group区域主要用于存储InnoDB数据库引擎的日志文件,它由多个大小相同的日志文件构成并且这些文件都采用顺序读写。innodb_log_file_size参数将决定每个文件的大小,而innodb_log_files_in_group参数将决定整个日志组中有多少个日志文件。

MySQL数据库完成初始化过程后这些日志文件将会按照参数的设置值,在磁盘上预占一个连续的磁盘空间。虽然数据库中还没有任何数据,但是日志文件的总大小就已经是innodb_log_file_size * innodb_log_files_in_group所得到的数值了:

# InnoDB数据库引擎 日志文件示例....total 1.0G
-rw-rw---- 1 mysql mysql 500M May 4 06:09 ib_logfile0
-rw-rw---- 1 mysql mysql 500M May 4 06:09 ib_logfile1

采用日志文件方式保证了后续同步日志数据的操作都是按顺序来写,而不是随机的写。当日志数据写到最后一个文件的末尾时,下一条日志数据又会重新从第一个日志文件的开始位置进行写入。

3.5.3 MySQL表结构、SQL语句优化

1. 开启查询缓存

大多数的MySQL服务器都开启了查询缓存,这是提高性能有效的方法之一。当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操作表而直接访问缓存结果了。对于程序员来说,这个事情是很容易被忽略的。开启查询缓存,代码如下:

// 查询缓存不开启
 $r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()"); 
// 开启查询缓存 
$today = date("Y-m-d"); 
$r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'"); 

上面两条SQL语句的差别为curdate(),MySQL的查询缓存对这个函数不起作用。所以如now()和rand()或是其它的诸如此类的SQL函数都不会开启查询缓存,因为这些函数的返回是不定的易变的。这时采用一个变量来代替MySQL的函数,从而开启缓存。

2. 使用explain的select查询

使用explain关键字可以知道MySQL是如何处理SQL语句的,能分析查询语句或是表结构的性能瓶颈。explain的查询结果告诉索引主键被如何利用的,数据表是如何被搜索和排序的等。

3.查询一行数据使用limit 1

当查询表时结果只有一条结果,只是需要取fetch游标,或是去检查返回的记录数。在这种情况下加上limit 1可以增加性能,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查下一条符合记录的数据。

// 没有效率的:
 $r = mysql_query("SELECT * FROM user WHERE country = 'China'");
 if (mysql_num_rows($r) > 0) { // ... } 
// 有效率的:
 $r = mysql_query("SELECT 1 FROM user WHERE country = 'China' LIMIT 1"); 
if (mysql_num_rows($r) > 0) { // ...
}

4. 建立搜索字段为索引

索引并不一定就是给主键或是唯一的字段。如果表中有某个字段会经常用来做搜索,那么就为其建立索引。如需要在一篇大的文章中搜索一个词时,如“where post_content like ‘%apple%'”,这时需要使用MySQL全文索引或是自己做一个索引。

5.设置Join表相同的数据类型为索引

如果应用程序有很多Join查询,确认两个表中Join的字段是否建过索引,这时MySQL内部会启动优化Join的SQL语句的机制。用来Join的字段应该是相同的数据类型。如把decimal字段和一个int字段Join在一起,MySQL就无法使用它们的索引。Join的字段有相同的数据类型,代码如下:

// 在state中查找
company $r = mysql_query("SELECT company_name FROM users 
LEFT JOIN companies ON (users.state = companies.state) 
WHERE users.id = $user_id"); 
// 两个 state 字段应该是被建过索引的,而且应该是相当的类型,相同的字符集。

6.不要使用order by rand()函数

order by rand会打乱返回的数据行,采用order by rand会带来很多可怕的性能问题。如果想把返回的数据行打乱,可以有N种方法可以达到这个目的,使用order by randD会让数据库的性能呈指数级的下降。原因是MySQL会不得不去执行rand()函数并且很耗CPU时间,它会每一行进行记录,然后再对其排序,就算是用limit 1也无济于事。

// 千万不要这样做
 $r=mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1"); 
// 这要会更好:
   $r=mysql_query("SELECT count(*) FROM user");
$d = mysql_fetch_row($r); 
$rand = mt_rand(0,$d[0] - 1); 
$r=mysql_query("SELECT username FROM user LIMIT $rand, 1");

7.避免使用SELECT *语句

从数据库里读出越多的数据,那么查询就会变得越慢,如果数据库服务器和WEB服务器是两台独立的服务器的话,这还会增加网络传输的负载。

// 不推荐 
$r=mysql_query("SELECT * FROM user WHERE user_id = 1"); 
$d = mysql_fetch_assoc($r); 
echo "Welcome {$d['username']}"; 
// 推荐 
$r = mysql_query("SELECT username FROM user WHERE user_id = 1"); 
$d = mysql_fetch_assoc($r);
 echo "Welcome {$d['username']}";

8.设置每张表的id

为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个int型的(推荐使用unsigned),并设置上自动增加的auto_increment标志,就算表有一个主键的字段,也别设置成为主键。如果使用VarChar类型来当主键则会使得性能下降。

只有一个情况是例外,那就是“关联表”的“外键”,也就是说,这个表的主键,通过若干个别的表的主键构成。如有一个“学生表”有学生的id,有一个“课程表”有课程ID,那么,“成绩表”就是“关联表”了,其关联了学生表和课程表,在成绩表中,学生id和课程id叫“外键”其共同组成主键。

9.使用enum代替Varchar类型

enum类型是非常快和紧凑的。如“性别”、“国家”、“民族”、“状态”、“部门”等字段,这些字段的取值是有限而且固定的,数据类型使用 ENUM 而不是VARCHAR。

10.procedure analyse()的建议

procedure analyse()会让MySQL分析字段和其实际的数据,并会给一些有用的建议。只有表中有实际的数据,这些建议才会变得有用,因为要做一些大的决定是需要有数据作为基础的。如创建了一个int字段作为主键,然而并没有太多的数据,那么procedure analyse()会建议把这个字段的类型改成mediumint

11. 使用not null

设置字段保持not null的原因,首先要清楚Empty和null的区别,如果它们之间没有什么区别,那么就不要使用null,如在Oracle里,null和Empty的字符串是一样的,不要以为null不需要空间,其需要额外的空间。

12.使用prepared statements

prepared statements类似于存储过程,是一种运行在后台的SQL语句集合。prepared statements可以检查一些绑定好的变量,这样可以保护程序不会受到“SQL注入式”攻击。在性能方面,当一个相同的查询被使用多次的时候,这会带来可观的性能优势。给这些prepared statements定义一些参数,而MySQL则只会解析一次。

// 创建 
prepared statement if ($stmt = $mysqli->prepare("SELECT username FROM user WHERE state=?")) { 
// 绑定参数
 $stmt->bind_param("s", $state); 
// 执行
 $stmt->execute(); 
// 绑定结果
 $stmt->bind_result($username); 
// 移动游标 
$stmt->fetch(); 
printf("%s is from %s\n", $username, $state); 
$stmt->close(); 
}

13.无缓冲的查询

正常情况下,在脚本中执行一个SQL语句的时候,程序会停在那里直到没这个SQL语句返回,然后程序再往下继续执行,可以使用无缓冲查询来改变这个行为。

mysql_unbuffered_query()函数,发送一个SQL语句到MySQL,不像mysql_query()函数一样去自动fethch,这样会节约内存。尤其是产生大量结果的查询语句并且需要等待所有的结果都返回的情况下,mysql_unbuffered_query()会节大量的内存,它只需要第一行数据返回的时候,就可以开始工作了。

14. IP地址存储设置为unsigned int数据类型

很多程序员都会创建一个varchar (15)字段来存放字符串形式的IP而不是整型的IP。如果用整型来存放IP地址,只需要4个字节,并且有定长的字段,采用这种方式在查询上也有优势,尤其是当需要使用where条件时,如IP between ip1 and ip2,这种查询优势更加明显。

IP地址建议使用unsigned intT类型,因为IP地址会使用整个32位的无符号整形。在查询时使用inet_aton()函数时,可以将字符串IP转成一个整型,使用inet_ntoa()函数时将整型转成一个字符串IP。

15.固定长度的表查询更快

如果表中的所有字段都是“固定长度”的,整个表会被认为是“static”或“fixed-length”。如表中没有varchar、text、blob等数据类型的字段,那么这个表就是“固定长度静态表”。固定长度的表会提高性能,因为MySQL搜寻得会更快一些,固定的长度是很容易计算下一个数据的偏移量的,所以读取的自然也会很快,除此固定长度的表也更容易被缓存和重建。如果字段不是定长的,那么每一次要找下一条的话,需要程序找到主键。固定长度的表也有副作用,固定长度的字段会浪费一些空间,因为定长的字段无论用不用,它都是要分配那么多的空间。

16.垂直分割

垂直分割是一种把数据库中的表按列变成几张表的方法,这样可以降低表的复杂度和字段的数目,从而达到优化的目的。需要注意的是,这些被分出去的字段所形成的表,不要经常性地去Join他们,不然的话,这样的性能会比不分割时还要差,而且,会是极数级的下降。

17.拆分大的delete和insert语句

如果在一个在线的网站上去执行一个大的delete和insert查询,避免操作让整个网站停止。因为这两个操作是会锁表的,表一锁住了,别的操作都进不来了。

Apache服务器会有很多的子进程或线程,所以工作起来相当有效率,而服务器不希望有太多的子进程,线程和数据库链接,因为这会极大的占服务器资源,尤其是内存资源。如果把表锁上一段时间,如30秒钟,对于一个有高访问量的站点来说,这30秒所积累的访问进程/线程,数据库链接,打开的文件数,可能不仅仅会让停止Web服务,还可能会让整台服务器宕机。

如果一个大的处理把其拆分,使用limit条件是一个好的方法,代码如下:

while (1) { 
//每次只做1000条
 mysql_query("DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000"); 
if (mysql_affected_rows() == 0) { 
// 没得可删了,退出!
 break; 
} 
// 每次都要休息一会儿 
usleep(50000); 
}

本章小结

本章主要讲解了分布式数据库系统概述、数据库架构与数据库应用场景、MySQL数据读写分离、数据库集群设计与部署、MySQL的调优等内容。通过本章的学习,读者应了解分布式数据库系统的特点与分类、理解数据库架构原理、了解数据库的应用场景和性能衡量指标、掌握数据库分离实现方法、能采用MySQL Proxy搭建MySQL数据库的读写分离、能设计与部署Redis数据库的集群、能掌握MySQL数据库的调优的方法。

本章习题

一、单项选择题

1.数据库架构单机架构下列说法正确的是( )。

A.能部署集中,运维方便

B.能实现读写分离

C.能实现分片

D.能搭建数据库集群

2.下列对数据库主从架构说法正确的是( )。

A.能实现分片

B.能实现读写分离

C.Master节点主要读读操作

D.Slave节点主要完成写操作

3.在数据库主从架构中,Master节点主要完成( )。

A.读操作

B.写操作

C.查询

D.授权

4.通过()启动Redis服务

A.redis-server

B.redis-cli

C.redis

D.Server

5.Redis的测试客户端程序()

A.redis-server

B.redis-cli

C.redis

D.Server

6.数据架构中的分片架构采用的( )方式进行分片。

A.垂直

B.水平

7.OLTP指的是( )。

A.数据分析

B.联机事务处理

C.联机分析处理

D.联机数据分析

8.OLAP指的是( )。

A.数据分析

B.联机事务处理

C.联机分析处理

D.联机数据分析

二、多选题

1.分布式数据库的模式结构有( )。

A. 全局模式

B. 分片模式

C. 局部内模式

D. 集中式模式

\2. 分布式数据库系统的主要应用( )。

A. 移动分布数数据库系统

B. 超市连锁分布式数据库系统

C. 火车订票分布式数据库系统

D. 药品管理分布式数据库系统

3.集中式数据库存在的不足( )。

A. 单点故障

B.数据按实际需要已经在网络上分布存储,如果采用集中式处理,势必造成附加成本和通信开销

C.系统的可扩展性差

D.不是实现读写分离

\4. 关于数据库多主架构描述正确的是( )

A. 双主机都接受读写数据,要实现数据双向同步,双向复制同样会带来延迟问题

B.降低单点故障的风险

C.适合读多写少的场景

D.备机好主机需要同等配置