是时候对你的数据库做一次全面体检啦!
网易云博客 | 2017-07-27 10:19
【数据猿导读】 数据库作为一个大型的并发存储系统,其内部设计极其复杂。开发者在使用数据库时,面临着可用性、可靠性、性能、安全、扩展性等多重挑战,这就使得数据库的使用具有较高的技术门槛。

数据库的健康检查涉及索引设计、容量规划、服务安全、参数配置、用户访问、集群复制6个方面。
数据库作为一个大型的并发存储系统,其内部设计极其复杂。开发者在使用数据库时,面临着可用性、可靠性、性能、安全、扩展性等多重挑战,这就使得数据库的使用具有较高的技术门槛。
一般大型团队都会雇佣专职的 DBA来维护数据库的日常运行,指导开发者建立正确的使用姿势。但是对于一般的中小型团队,受限于人力成本,这种模式很难实现。
而随着 DevOps理念的流行,开发者开始更多地参与和承担数据库的运维工作,其中就包括对数据库的定期巡检。
对数据库定期进行健康检查是数据库日常维护的重要环节,通过检查数据库的各项运行指标,评估系统的运行风险,提前将风险消灭在摇篮中,能够有效提高数据库的服务质量。
今天我们就来聊聊开发者如何对数据库进行健康检查。
数据库的健康检查涉及索引设计、容量规划、服务安全、参数配置、用户访问、集群复制6个方面。
索引设计
合理的索引设计能够有效加速数据库的访问,提高查询的执行效率,减少用户查询对服务端的资源消耗。
而不合理的、低效的、冗余的甚至无效的索引不仅无法起到加速查询访问的效果,反而会影响数据库的插入、更新性能,甚至是数据库的高可用方案能否生效。 所以针对索引的健康检查主要是发现系统表结构设计中的不合理、低效、冗余、无效的索引。
主键索引缺失
由于 MySQL默认存储引擎 InnoDB(MySQL 5.6 之后)使用的是聚簇索引表设计,这就要求所有的表必须包含一个主键,所有的数据记录需要按照主键顺序构建 B+树,所有数据记录位于叶子节点。
如果用户在创建表时显式指定主键,则数据库会使用用户指定的主键构建B+树,但是如果用户没有显式指定主键,同时也没有指定任何唯一键索引,InnoDB为了确保每张表至少包含一个主键,则默认会为用户生成一个“隐含主键”,该主键对用户不可见,甚至对于 MySQL Server层的 binlog也不可见。
binlog 是连接 MySQL主从复制节点的纽带,所有主节点的更新都是通过 binlog传递给从节点的,一旦 binlog中没有更新记录的主键 ID,就会导致基于 Row格式的 binlog在从节点执行时,无法唯一标识某一条记录,只能通过全表扫描来进行全表匹配,大大降低了从机更新的执行效率,从而造成复制延迟。如果是用于高可用故障切换的从节点,会导致切换的时间大幅增加,甚至会导致高可用机制失效。如果是用于提高读能力搭建的只读从节点,则会导致应用读到的数据可能是很久以前的旧数据。所以我们建议使用 InnoDB存储引擎的 MySQL用户在创建表时,必须显示指定主键。
主键索引与业务相关
如果用户在创建表时指定的主键与业务相关,可能会被频繁地更新,这样会引起MySQL数据库的 InnoDB存储引擎进行频繁的节点合并和分裂,造成大量额外的系统 IO开销,影响数据库的插入和更新性能。
我们推荐开发者在创建表时指定与业务无关的自增字段作为主键,这样不仅会提高按时间序插入的性能(顺序写入硬盘),同时也可以提高按插入时间范围检索的查询效率。
冗余索引
如果一个索引涉及的字段属性包含另外一个索引涉及的字段属性,同时两个索引字段顺序一致,且两个索引的首字段属性相同,则可以认为涉及字段少的索引为冗余索引。
在 MySQL 5.7推出 sys库之前,我们可以通过 percona的工具 pt-duplicate-key-checker来完成对冗余索引的检查,在 MySQL 5.7中,我们可以通过 sys库 schema_redundant_indexes表来完成。
低效索引
索引的作用在于通过索引,查询可以扫描更少的记录。数据库中的记录在索引字段区分度越高,扫描的记录数就越少,执行的效率就越高。如果数据库表中的记录在索引字段区分度不大,索引对记录的筛选结果就不明显,索引就无法起到加速查询的作用。
通过数据库记录在索引字段的区分度,我们可以衡量索引的执行效率。MySQL系统库 mysql库下,innodb_index_stats表的 stat_value字段,记录了某张表在某个索引的不同取值的记录个数, innodb_table_stats表的 n_rows字段记录了某张表的总记录数,二者相除,即可得到数据库记录在某个索引的区分度。越接近1,表示区分度越高;低于0.1,则说明区分度较差。开发者应该重新评估 SQL语句涉及的字段,选择区分度高的多个字段创建索引,通过运行下面的 SQL语句,可以计算每张表的索引区分度。
select t.database_name AS `db`,
t.TABLE_NAME AS `table`,
i.INDEX_NAME AS `index name`,
i.stat_description as cols ,
i.stat_value AS differRows,
t.n_rows as rows,
ROUND(((i.stat_value / IFNULL(IF(t.n_rows < i.stat_value, i.stat_value, t.n_rows),0.01)) ), 2) AS `sel_percent`
from mysql.innodb_index_stats i
INNER JOIN mysql.innodb_table_stats t on i.database_name = t.database_name and i.table_name= t.table_name
where t.table_name= %
AND t.database_name= %
AND i.INDEX_NAME != 'PRIMARY'
AND i.stat_name like '%n_diff_pfx%';
无效索引
如果一个索引始终无法被查询使用,它的存在只能增加数据库的维护开销,开发者应该及时删除这些索引。通过 MySQL 5.7 sys库 schema_unused_indexes视图,可以查看当前实例哪些索引从没有被使用。
容量规划
数据库的运行需要依赖计算、存储、网络等多种资源,对各种资源历史使用情况的分析,对资源配置进行合理的规划,是确保数据库稳定运行的必要条件。
CPU
CPU利用率是衡量 CPU繁忙程度最直观的指标,通过 top命令,开发者可以查看 CPU的使用情况。
CPU 利用率持续超过80%,说明计算资源已经接近饱和,如果开发者已经做过 SQL优化,则需要使用更高配置的 CPU。通过查看7天内 CPU利用率超过80%的时间占整体时间的百分比以及单次持续时间超过的一定的阈值,可清楚 CPU扩容的触发条件。
IO
大部分数据库应用的都是 IO Bound类型,IO处理能力最常用的衡量指标是 IO 利用率。IO 利用率统计的是一秒内 IO请求队列非空的时间比例,IO利用率越高就表示硬盘越繁忙。但是 IO利用率100%并不表示系统已经无法处理更多的 IO请求。
IOPS和每秒 IO字节数可以从存储设备的角度更准确地描述 IO状态。每一个存储设备都有 IOPS和每秒 IO字节数的上限,任意一个达到上限,就会成为 IO处理能力的瓶颈。在传统机械硬盘中,随机 IO主要受到 IOPS的限制,顺序 IO主要受带宽限制。除此之外,我们还可以从应用的角度、使用一次 IO请求的响应时间来描述 IO负载。一次 IO请求的响应时间包括其在队列中的等待时间和实际 IO处理时间之和。
通过 iostats,开发者可以采集到上述指标,如果这些指标在一段时间内持续接近设定上限,则可以认为 IO过载。通过扩大内存,让更多的读写请求命中缓存可以缓解硬盘 IO,因为数据库的应用大部分都属于热点应用。另外,使用更高配置的存储设备,例如固态硬盘,可以大幅提高系统的 IO处理能力。
存储空间
存储空间不足会导致严重的系统故障,数据库可能宕机,更为严重的是数据库进程存活但是无法响应服务,从而造成基于进程的监控失效。根据7天内数据库中存储数据的变化,我们可以估算出未来3天内数据的增长情况,从而判断实例是否存在存储空间不足的风险。
内存
使用 InnoDB存储引擎的 MySQL数据库在实例启动时,就会预分配一块固定大小的内存空间,所有读写请求都会在该空间中完成。如果内存中缓存了用户读写的数据,则直接读取内存,如果内存中没有用户读写的数据,则需要将数据先从硬盘中 load进内存中。由于内存的读写速度远远快于硬盘,这就使得读写请求是否命中内存决定了读写请求的处理速度。内存空间越大,缓存数据越多,命中的几率也就越大。所以我们可以使用缓存命中率来衡量内存空间大小是否满足应用的需求。
在MySQL中,show engine innodb status 命令的 Buffer pool hit rate可以度量从上一次执行 show engine innodb status到本次时间范围内 Buffer pool的命中情况。
网络
网络带宽在数据库返回记录较多的情况下,也可能会成为系统的瓶颈。一般我们使用每秒网络流入和流出字节数来衡量网络流量是否达到带宽限制。在云环境下,每台虚拟机或者容器都是有一定的网络带宽配额,私有网络的配额会比较大,公网配额与用户付费相关;使用 iftop 可以查看当前系统的网络流量。
服务安全
弱密码
MySQL的登陆认证使用的是 IP+账户密码的方式,很多开发者为了方便记忆,习惯将数据库密码设置为弱密码,这实际是非常危险的。数据库中的数据很多涉及敏感业务,弱密码非常容易被破解,对数据库中的数据是一个严重的安全隐患。
MySQL系统库 mysql库下的 user表的 password字段保存了所有用户的密码,MySQL使用的是两次 sha-1的不可逆加密算法,所以我们无法通过 password字段获取用户的密码内容,但是我们可以通过将常见弱密码制成彩虹表,模拟 MySQL的加密算法,匹配 password字段,即可发现数据库中的弱密码账号。
网络安全
在一般的业务架构中,数据库都不会直接服务于终端用户,而是服务于运行业务逻辑的应用程序。所以数据库和业务程序之间出于安全考虑,会选择使用私有网络。即使使用私有网络,为了避免数据库连错,也需要在设置数据库账号时,增加 IP来源限制。
在一些特定的场景下,如果数据访问必须借助公网来实现,就会将数据库暴露在公网上。使用公网数据库实例,必须要配置防火墙,否则存在被攻击的隐患,通过 iptables我们可以控制访问数据库的来源 IP。
权限检查
MySQL提供了多种权限配置,为了方便管理以及避免误操作,一般会将管理权限和访问权限配置成两个不同的账号,禁止使用管理权限作为业务程序访问数据库的账号。通过系统库 mysql库的 user表可以确认各个账号拥有的权限,尽量避免业务账号拥有 super权限。
参数配置
内存相关参数
MySQL数据库的内存使用包括两个部分:共享内存与连接独占内存。
每一个用户新建连接,数据库都要分配一定的内存空间保存用户的临时数据,这些空间为单个连接独占。在 MySQL实例启动时,系统会预先分配一些实例级别的连接共享内存空间,例如 Innodb_buffer_pool,Innodb_log_buffer_pool等。
独占内存空间乘以最大连接数加上共享内存空间,我们可以计算出 MySQL最大可使用的内存空间,如果超过实际物理内存大小,就存在 MySQL进程被操作系统强行 oom kill风险,导致实例宕机。
MySQL的这些内存空间都可以通过配置参数指定大小,如果超过实际内存空间,应该调整相应参数配置,最常见的是调整 Innodb_buffer_pool和最大连接数。
memory = key_buffer_size + query_cache_size + tmp_table_size + innodb_buffer_pool_size + innodb_additional_mem_pool_size +
innodb_log_buffer_size + max_connections * (sort_buffer_size + read_buffer_size + read_rnd_buffer_size + join_buffer_size +
thread_stack + binlog_cache_size)。
Innodb 日志相关参数
innodb_log_file_size 参数定义了 Innodb 重做日志的大小。如果参数设置过小,会导致数据库写操作频繁卡顿,如果设置过大,会导致数据库实例 recovery花费大量的时间。
一般情况下,对于使用固态硬盘等高配置存储设备的数据库,我们可以将重做日志设置大一些;对于使用机械硬盘的数据库,可以设置小一些,一般在512M到4G之间。innodb_flush_log_at_trx_commit定义了重做日志的刷新节奏,如果该参数非1,会导致数据库宕机重启后丢失部分更新数据,对于数据可靠性要求较高的应用造成严重影响。
二进制日志相关参数
binlog 主要在 MySQL集群复制以及故障恢复中担任协调者的作用。binlog_format 定义了 binlog的格式,主要包括ROW、STATEMENT、MIXED三种格式。ROW格式是最安全的一种日志格式,会保证主从数据的严格一致,建议使用 ROW格式。
但是 ROW格式的 binlog会占用更多的存储空间,通过 expire_logs_days可以控制保存 binlog的天数,如果 binlog占用的存储空间比例超过50%,则应考虑适当减少 binlog的保存天数。 sync_binlog 参数定义了 binlog刷新硬盘的节奏,如果非1,会导致宕机重启后部分数据丢失。
连接数相关参数
MySQL有最大连接数限制 max_connections,如果应用连接超过 max_connetions限制,则会得到 out of max connections异常,无法建立连接。
show processlist 可以查看当前的连接数,如果接近最大限制,则存在无法新建连接的风险。通过使用连接池可以控制数据库的连接数。
用户访问
慢连接
慢查询数量是最直观的反映数据库处理能力是否满足业务需求的指标。通过设置 slow_query_log可以开启慢查询日志,MySQL数据库会将执行时间超过 long_query_time的查询记入慢查询日志。如果某个时间段内,慢查询数量急剧增加,则开发者就必须要关注数据库的性能问题,首先就需要进行 SQL优化,其次考虑资源是否需要扩容,最后可能需要数据库水平扩展方案,包括创建只读从节点。
死锁数量
两个事务涉及的数据库记录有重叠,如果 SQL语句的加锁顺序不一致,就会导致事务之间的死锁。虽然 MySQL数据库会自动检测死锁并强制回滚系统认为代价较小的事务,但是死锁的检测与事务回滚都有较大的代价,会严重拖慢数据库的性能,所以当系统中出现大量死锁时,开发者必须引起重视,要分析发生死锁的事务的 SQL语句的加锁规则,调整 SQL语句。通过 show engin innodb status可以查看死锁的相关信息以及系统的处理过程。
集群复制
数据安全
复制是 MySQL多个节点之间实现数据同步的重要机制,主要用于搭建高可用实例主从节点以及提供多个只读从节点提高读扩展能力。节点之间的数据是否最终一致对于高可用方案是否生效、只读实例读取的数据是否正确有着严重影响。
从机执行 show slave status可以获取从机的复制状态,Slave_IO_Running和 Slave_SQL_Running分别表示 IO和 SQL线程是否运行正常,如果不正常,则应及时处理。参数 relay_log_recovery和 relay_log_info_repository影响从节点宕机重启后,与主机的复制位置是否正确,如果位置错误,则可能导致数据错误。
复制性能
复制延迟经常用来评估复制性能是否满足业务需求。Show slave status的 Seconds behind master字段标识了从机落后主机的延迟时间。如果延迟较长,则会影响高可用实例主从切换的时间以及只读从节点是否能够及时读到最新数据。
通过使用并行复制技术可以提高从节点的复制性能。MySQL 5.6 提供了基于 Database级别的并行复制,通过 slave_parallel_workers 设置并行线程数;MySQL 5.7提供了基于 LOGICAL_CLOCK的并行复制, 主机上同一个 Group提交的 binlog中包含事务在从机并行执行,相比 Database,具备更高的并发性,除了设置 slave_parallel_workers,还需要将 slave-parallel-type设置为 LOGICAL_CLOCK。slave_preserve_commit_order=1可以确保从机并行执行的事务按序提交。同时从机的 log_bin和 log_slave_updates参数必须同时开启。
这样繁琐又细致的过程极其考验我们的耐心, 如果你是网易蜂巢的开发者,这些工作都可以一键解决。
网易蜂巢智能数据库健康诊断系统
使用网易蜂巢的开发者,可以使用平台提供的智能健康诊断系统对数据库服务中的关系数据库实例进行自动健康检查。检查内容覆盖6个大类,22个子项,检查结束后根据检查结果,会自动生成健康指数,开发者根据健康指数,可以快速判断系统存在风险的严重程度,同时平台提供了该分数在所有实例的健康检查中的排名。
有风险的项目,平台会使用橙色标识,开发者点击风险项目,会看到系统对该风险的详细描述以及相应的修复建议。针对部分检查内容,系统提供了一键自动修复功能。
来源:网易云博客
我要评论
活动推荐more >
- 2018 上海国际大数据产业高2018-12-03
- 2018上海国际计算机网络及信2018-12-03
- 中国国际信息通信展览会将于2018-09-26
- 第五届FEA消费金融国际峰会62018-06-21
- 第五届FEA消费金融国际峰会2018-06-21
- “无界区块链技术峰会2018”2018-06-14