mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-25 04:36:41 +08:00
392 lines
32 KiB
HTML
392 lines
32 KiB
HTML
<!DOCTYPE html>
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
<link rel="icon" href="/static/favicon.png">
|
||
<title>38 数据库参数设置优化,失之毫厘差之千里.md.html</title>
|
||
<!-- Spectre.css framework -->
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
<!-- theme css & js -->
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
</head>
|
||
<body>
|
||
<div class="book-container">
|
||
<div class="book-sidebar">
|
||
<div class="book-brand">
|
||
<a href="/">
|
||
<img src="/static/favicon.png">
|
||
<span>技术文章摘抄</span>
|
||
</a>
|
||
</div>
|
||
<div class="book-menu uncollapsible">
|
||
<ul class="uncollapsible">
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
<li><a href="../">上一级</a></li>
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/00 开篇词你为什么需要学习并发编程?.md.html">00 开篇词你为什么需要学习并发编程?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/01 如何制定性能调优标准?.md.html">01 如何制定性能调优标准?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/02 如何制定性能调优策略?.md.html">02 如何制定性能调优策略?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/03 字符串性能优化不容小觑,百M内存轻松存储几十G数据.md.html">03 字符串性能优化不容小觑,百M内存轻松存储几十G数据</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/04 慎重使用正则表达式.md.html">04 慎重使用正则表达式</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/05 ArrayList还是LinkedList?使用不当性能差千倍.md.html">05 ArrayList还是LinkedList?使用不当性能差千倍</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/06 Stream如何提高遍历集合效率?.md.html">06 Stream如何提高遍历集合效率?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/07 深入浅出HashMap的设计与优化.md.html">07 深入浅出HashMap的设计与优化</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/08 网络通信优化之IO模型:如何解决高并发下IO瓶颈?.md.html">08 网络通信优化之IO模型:如何解决高并发下IO瓶颈?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/09 网络通信优化之序列化:避免使用Java序列化.md.html">09 网络通信优化之序列化:避免使用Java序列化</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/10 网络通信优化之通信协议:如何优化RPC网络通信?.md.html">10 网络通信优化之通信协议:如何优化RPC网络通信?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/11 答疑课堂:深入了解NIO的优化实现原理.md.html">11 答疑课堂:深入了解NIO的优化实现原理</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/12 多线程之锁优化(上):深入了解Synchronized同步锁的优化方法.md.html">12 多线程之锁优化(上):深入了解Synchronized同步锁的优化方法</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/13 多线程之锁优化(中):深入了解Lock同步锁的优化方法.md.html">13 多线程之锁优化(中):深入了解Lock同步锁的优化方法</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/14 多线程之锁优化(下):使用乐观锁优化并行操作.md.html">14 多线程之锁优化(下):使用乐观锁优化并行操作</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/15 多线程调优(上):哪些操作导致了上下文切换?.md.html">15 多线程调优(上):哪些操作导致了上下文切换?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/16 多线程调优(下):如何优化多线程上下文切换?.md.html">16 多线程调优(下):如何优化多线程上下文切换?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/17 并发容器的使用:识别不同场景下最优容器.md.html">17 并发容器的使用:识别不同场景下最优容器</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/18 如何设置线程池大小?.md.html">18 如何设置线程池大小?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/19 如何用协程来优化多线程业务?.md.html">19 如何用协程来优化多线程业务?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/20 磨刀不误砍柴工:欲知JVM调优先了解JVM内存模型.md.html">20 磨刀不误砍柴工:欲知JVM调优先了解JVM内存模型</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/21 深入JVM即时编译器JIT,优化Java编译.md.html">21 深入JVM即时编译器JIT,优化Java编译</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/22 如何优化垃圾回收机制?.md.html">22 如何优化垃圾回收机制?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/23 如何优化JVM内存分配?.md.html">23 如何优化JVM内存分配?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/24 内存持续上升,我该如何排查问题?.md.html">24 内存持续上升,我该如何排查问题?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/25 答疑课堂:模块四热点问题解答.md.html">25 答疑课堂:模块四热点问题解答</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/26 单例模式:如何创建单一对象优化系统性能?.md.html">26 单例模式:如何创建单一对象优化系统性能?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/27 原型模式与享元模式:提升系统性能的利器.md.html">27 原型模式与享元模式:提升系统性能的利器</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/28 如何使用设计模式优化并发编程?.md.html">28 如何使用设计模式优化并发编程?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/29 生产者消费者模式:电商库存设计优化.md.html">29 生产者消费者模式:电商库存设计优化</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/30 装饰器模式:如何优化电商系统中复杂的商品价格策略?.md.html">30 装饰器模式:如何优化电商系统中复杂的商品价格策略?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/31 答疑课堂:模块五思考题集锦.md.html">31 答疑课堂:模块五思考题集锦</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/32 MySQL调优之SQL语句:如何写出高性能SQL语句?.md.html">32 MySQL调优之SQL语句:如何写出高性能SQL语句?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/33 MySQL调优之事务:高并发场景下的数据库事务调优.md.html">33 MySQL调优之事务:高并发场景下的数据库事务调优</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/34 MySQL调优之索引:索引的失效与优化.md.html">34 MySQL调优之索引:索引的失效与优化</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/35 记一次线上SQL死锁事故:如何避免死锁?.md.html">35 记一次线上SQL死锁事故:如何避免死锁?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/36 什么时候需要分表分库?.md.html">36 什么时候需要分表分库?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/37 电商系统表设计优化案例分析.md.html">37 电商系统表设计优化案例分析</a>
|
||
</li>
|
||
<li>
|
||
<a class="current-tab" href="/专栏/Java并发编程实战/38 数据库参数设置优化,失之毫厘差之千里.md.html">38 数据库参数设置优化,失之毫厘差之千里</a>
|
||
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/39 答疑课堂:MySQL中InnoDB的知识点串讲.md.html">39 答疑课堂:MySQL中InnoDB的知识点串讲</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/41 如何设计更优的分布式锁?.md.html">41 如何设计更优的分布式锁?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/42 电商系统的分布式事务调优.md.html">42 电商系统的分布式事务调优</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/43 如何使用缓存优化系统性能?.md.html">43 如何使用缓存优化系统性能?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/44 记一次双十一抢购性能瓶颈调优.md.html">44 记一次双十一抢购性能瓶颈调优</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/加餐 什么是数据的强、弱一致性?.md.html">加餐 什么是数据的强、弱一致性?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/加餐 推荐几款常用的性能测试工具.md.html">加餐 推荐几款常用的性能测试工具</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/答疑课堂:模块三热点问题解答.md.html">答疑课堂:模块三热点问题解答</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/Java并发编程实战/结束语 栉风沐雨,砥砺前行!.md.html">结束语 栉风沐雨,砥砺前行!</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
<div class="sidebar-toggle-inner"></div>
|
||
</div>
|
||
<script>
|
||
function add_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.add('show')
|
||
}
|
||
function remove_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.remove('show')
|
||
}
|
||
function sidebar_toggle() {
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let content = document.querySelector('.off-canvas-content')
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
sidebar_toggle.classList.remove('extend')
|
||
sidebar.classList.remove('hide')
|
||
content.classList.remove('extend')
|
||
} else { // hide
|
||
sidebar_toggle.classList.add('extend')
|
||
sidebar.classList.add('hide')
|
||
content.classList.add('extend')
|
||
}
|
||
}
|
||
function open_sidebar() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.add('show')
|
||
overlay.classList.add('show')
|
||
}
|
||
function hide_canvas() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.remove('show')
|
||
overlay.classList.remove('show')
|
||
}
|
||
</script>
|
||
<div class="off-canvas-content">
|
||
<div class="columns">
|
||
<div class="column col-12 col-lg-12">
|
||
<div class="book-navbar">
|
||
<!-- For Responsive Layout -->
|
||
<header class="navbar">
|
||
<section class="navbar-section">
|
||
<a onclick="open_sidebar()">
|
||
<i class="icon icon-menu"></i>
|
||
</a>
|
||
</section>
|
||
</header>
|
||
</div>
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;">
|
||
<div class="book-post">
|
||
<p id="tip" align="center"></p>
|
||
<div><h1>38 数据库参数设置优化,失之毫厘差之千里</h1>
|
||
<p>你好,我是刘超。</p>
|
||
<p>MySQL 是一个灵活性比较强的数据库系统,提供了很多可配置参数,便于我们根据应用和服务器硬件来做定制化数据库服务。如果现在让你回想,你可能觉得在开发的过程中很少去调整 MySQL 的配置参数,但我今天想说的是我们很有必要去深入了解它们。</p>
|
||
<p>我们知道,数据库主要是用来存取数据的,而存取数据涉及到了磁盘 I/O 的读写操作,所以数据库系统主要的性能瓶颈就是 I/O 读写的瓶颈了。MySQL 数据库为了减少磁盘 I/O 的读写操作,应用了大量内存管理来优化数据库操作,包括内存优化查询、排序以及写入操作。</p>
|
||
<p>也许你会想,我们把内存设置得越大越好,数据刷新到磁盘越快越好,不就对了吗?其实不然,内存设置过大,同样会带来新的问题。例如,InnoDB 中的数据和索引缓存,如果设置过大,就会引发 SWAP 页交换。还有数据写入到磁盘也不是越快越好,我们期望的是在高并发时,数据能均匀地写入到磁盘中,从而避免 I/O 性能瓶颈。</p>
|
||
<blockquote>
|
||
<p>SWAP 页交换:SWAP 分区在系统的物理内存不够用的时候,就会把物理内存中的一部分空间释放出来,以供当前运行的程序使用。被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间的数据被临时保存到 SWAP 分区中,等到那些程序要运行时,再从 SWAP 分区中恢复保存的数据到内存中。</p>
|
||
</blockquote>
|
||
<p>所以,这些参数的设置跟我们的应用服务特性以及服务器硬件有很大的关系。MySQL 是一个高定制化的数据库,我们可以根据需求来调整参数,定制性能最优的数据库。</p>
|
||
<p>不过想要了解这些参数的具体作用,我们先得了解数据库的结构以及不同存储引擎的工作原理。</p>
|
||
<h2>MySQL 体系结构</h2>
|
||
<p>我们一般可以将 MySQL 的结构分为四层,最上层为客户端连接器,主要包括了数据库连接、授权认证、安全管理等,该层引用了线程池,为接入的连接请求提高线程处理效率。</p>
|
||
<p>第二层是 Server 层,主要实现 SQL 的一些基础功能,包括 SQL 解析、优化、执行以及缓存等,其中与我们这一讲主要相关的就是缓存。</p>
|
||
<p>第三层包括了各种存储引擎,主要负责数据的存取,这一层涉及到的 Buffer 缓存,也和这一讲密切相关。</p>
|
||
<p>最下面一层是数据存储层,主要负责将数据存储在文件系统中,并完成与存储引擎的交互。</p>
|
||
<p><img src="assets/5297b9556d527dec788b5298d4810fa5.jpg" alt="img" /></p>
|
||
<p>接下来我们再来了解下,当数据接收到一个 SQL 语句时,是如何处理的。</p>
|
||
<h3>1. 查询语句</h3>
|
||
<p>一个应用服务需要通过第一层的连接和授权认证,再将 SQL 请求发送至 SQL 接口。SQL 接口接收到请求之后,会先检查查询 SQL 是否命中 Cache 缓存中的数据,如果命中,则直接返回缓存中的结果;否则,需要进入解析器。</p>
|
||
<p>解析器主要对 SQL 进行语法以及词法分析,之后,便会进入到优化器中,优化器会生成多种执行计划方案,并选择最优方案执行。</p>
|
||
<p>确定了最优执行计划方案之后,执行器会检查连接用户是否有该表的执行权限,有则查看 Buffer 中是否存在该缓存,存在则获取锁,查询表数据;否则重新打开表文件,通过接口调用相应的存储引擎处理,这时存储引擎就会进入到存储文件系统中获取相应的数据,并返回结果集。</p>
|
||
<h3>2. 更新语句</h3>
|
||
<p>数据库更新 SQL 的执行流程其实跟查询 SQL 差不多,只不过执行更新操作的时候多了记录日志的步骤。在执行更新操作时 MySQL 会将操作的日志记录到 binlog(归档日志)中,这个步骤所有的存储引擎都有。而 InnoDB 除了要记录 binlog 之外,还需要多记录一个 redo log(重做日志)。</p>
|
||
<p>redo log 主要是为了解决 crash-safe 问题而引入的。我们知道,当数据库在存储数据时发生异常重启,我们需要保证存储的数据要么存储成功,要么存储失败,也就是不会出现数据丢失的情况,这就是 crash-safe 了。</p>
|
||
<p>我们在执行更新操作时,首先会查询相关的数据,之后通过执行器执行更新操作,并将执行结果写入到内存中,同时记录更新操作到 redo log 的缓存中,此时 redo log 中的记录状态为 prepare,并通知执行器更新完成,随时可以提交事务。执行器收到通知后会执行 binlog 的写入操作,此时的 binlog 是记录在缓存中的,写入成功后会调用引擎的提交事务接口,更新记录状态为 commit。之后,内存中的 redo log 以及 binlog 都会刷新到磁盘文件中。</p>
|
||
<h2>内存调优</h2>
|
||
<p>基于以上两个 SQL 执行过程,我们可以发现,在执行查询 SQL 语句时,会涉及到两个缓存。第一个缓存是刚进来时的 Query Cache,它缓存的是 SQL 语句和对应的结果集。这里的缓存是以查询 SQL 的 Hash 值为 key,返回结果集为 value 的键值对,判断一条 SQL 是否命中缓存,是通过匹配查询 SQL 的 Hash 值来实现的。</p>
|
||
<p>很明显,Query Cache 可以优化查询 SQL 语句,减少大量工作,特别是减少了 I/O 读取操作。我们可以通过以下几个主要的设置参数来优化查询操作:</p>
|
||
<p><img src="assets/db97c34b74f0903673badc256ba46cdd.jpg" alt="img" /></p>
|
||
<p>我们可以通过设置合适的 query_cache_min_res_unit 来减少碎片,这个参数最合适的大小和应用程序查询结果的平均大小直接相关,可以通过以下公式计算所得:</p>
|
||
<p>(query_cache_size - Qcache_free_memory)/ Qcache_queries_in_cache</p>
|
||
<p>Qcache_free_memory 和 Qcache_queries_in_cache 的值可以通过以下命令查询:</p>
|
||
<pre><code>show status like 'Qcache%'
|
||
</code></pre>
|
||
<p>Query Cache 虽然可以优化查询操作,但也仅限于不常修改的数据,如果一张表数据经常进行新增、更新和删除操作,则会造成 Query Cache 的失效率非常高,从而导致频繁地清除 Cache 中的数据,给系统增加额外的性能开销。</p>
|
||
<p>这也会导致缓存命中率非常低,我们可以通过以上查询状态的命令查看 Qcache_hits,该值表示缓存命中率。如果缓存命中率特别低的话,我们还可以通过 query_cache_size = 0 或者 query_cache_type 来关闭查询缓存。</p>
|
||
<p>经过了 Query Cache 缓存之后,还会使用到存储引擎中的 Buffer 缓存。不同的存储引擎,使用的 Buffer 也是不一样的。这里我们主要讲解两种常用的存储引擎。</p>
|
||
<h3>1. MyISAM 存储引擎参数设置调优</h3>
|
||
<p>MyISAM 存储引擎使用 key buffer 缓存索引块,MyISAM 表的数据块则没有缓存,它是直接存储在磁盘文件中的。</p>
|
||
<p>我们可以通过 key_buffer_size 设置 key buffer 缓存的大小,而它的大小并不是越大越好。正如我前面所讲的,key buffer 缓存设置过大,实际应用却不大的话,就容易造成内存浪费,而且系统也容易发生 SWAP 页交换,一般我是建议将服务器内存中可用内存的 1/4 分配给 key buffer。</p>
|
||
<p>如果要更准确地评估 key buffer 的设置是否合理,我们还可以通过缓存使用率公式来计算:</p>
|
||
<p>1-((key_blocks_unused*key_cache_block_size)/key_buffer_size)</p>
|
||
<blockquote>
|
||
<p>key_blocks_unused 表示未使用的缓存簇(blocks)数
|
||
key_cache_block_size 表示 key_buffer_size 被分割的区域大小 key_blocks_unused*key_cache_block_size 则表示剩余的可用缓存空间(一般来说,缓存使用率在 80% 作用比较合适)。</p>
|
||
</blockquote>
|
||
<h3>2. InnoDB 存储引擎参数设置调优</h3>
|
||
<p>InnoDB Buffer Pool(简称 IBP)是 InnoDB 存储引擎的一个缓冲池,与 MyISAM 存储引擎使用 key buffer 缓存不同,它不仅存储了表索引块,还存储了表数据。查询数据时,IBP 允许快速返回频繁访问的数据,而无需访问磁盘文件。InnoDB 表空间缓存越多,MySQL 访问物理磁盘的频率就越低,这表示查询响应时间更快,系统的整体性能也有所提高。</p>
|
||
<p>我们一般可以通过多个设置参数来调整 IBP,优化 InnoDB 表性能。</p>
|
||
<ul>
|
||
<li><strong>innodb_buffer_pool_size</strong></li>
|
||
</ul>
|
||
<p>IBP 默认的内存大小是 128M,我们可以通过参数 innodb_buffer_pool_size 来设置 IBP 的大小,IBP 设置得越大,InnoDB 表性能就越好。但是,将 IBP 大小设置得过大也不好,可能会导致系统发生 SWAP 页交换。所以我们需要在 IBP 大小和其它系统服务所需内存大小之间取得平衡。MySQL 推荐配置 IBP 的大小为服务器物理内存的 80%。</p>
|
||
<p>我们也可以通过计算 InnoDB 缓冲池的命中率来调整 IBP 大小:</p>
|
||
<p>(1-innodb_buffer_pool_reads/innodb_buffer_pool_read_request)*100</p>
|
||
<p>但如果我们将 IBP 的大小设置为物理内存的 80% 以后,发现命中率还是很低,此时我们就应该考虑扩充内存来增加 IBP 的大小。</p>
|
||
<ul>
|
||
<li><strong>innodb_buffer_pool_instances</strong></li>
|
||
</ul>
|
||
<p>InnoDB 中的 IBP 缓冲池被划分为了多个实例,对于具有数千兆字节的缓冲池的系统来说,将缓冲池划分为单独的实例可以减少不同线程读取和写入缓存页面时的争用,从而提高系统的并发性。该参数项仅在将 innodb_buffer_pool_size 设置为 1GB 或更大时才会生效。</p>
|
||
<p>在 windows 32 位操作系统中,如果 innodb_buffer_pool_size 的大小超过 1.3GB,innodb_buffer_pool_instances 默认大小就为 innodb_buffer_pool_size/128MB;否则,默认为 1。</p>
|
||
<p>而在其它操作系统中,如果 innodb_buffer_pool_size 大小超过 1GB,innodb_buffer_pool_instances 值就默认为 8;否则,默认为 1。</p>
|
||
<p>为了获取最佳效率,建议指定 innodb_buffer_pool_instances 的大小,并保证每个缓冲池实例至少有 1GB 内存。通常,建议 innodb_buffer_pool_instances 的大小不超过 innodb_read_io_threads + innodb_write_io_threads 之和,建议实例和线程数量比例为 1:1。</p>
|
||
<ul>
|
||
<li><strong>innodb_read_io_threads</strong> / <strong>innodb_write_io_threads</strong></li>
|
||
</ul>
|
||
<p>在默认情况下,MySQL 后台线程包括了主线程、IO 线程、锁线程以及监控线程等,其中读写线程属于 IO 线程,主要负责数据库的读取和写入操作,这些线程分别读取和写入 innodb_buffer_pool_instances 创建的各个内存页面。MySQL 支持配置多个读写线程,即通过 innodb_read_io_threads 和 innodb_write_io_threads 设置读写线程数量。</p>
|
||
<p>读写线程数量值默认为 4,也就是总共有 8 个线程同时在后台运行。innodb_read_io_threads 和 innodb_write_io_threads 设置的读写线程数量,与 innodb_buffer_pool_instances 的大小有关,两者的协同优化是提高系统性能的一个关键因素。</p>
|
||
<p>在一些内存以及 CPU 内核超大型的数据库服务器上,我们可以在保证足够大的 IBP 内存的前提下,通过以下公式,协同增加缓存实例数量以及读写线程。</p>
|
||
<p>( innodb_read_io_threads + innodb_write_io_threads ) = innodb_buffe_pool_instances</p>
|
||
<p>如果我们仅仅是将读写线程根据缓存实例数量对半来分,即读线程和写线程各为实例大小的一半,肯定是不合理的。例如我们的应用服务读取数据库的数据多于写入数据库的数据,那么增加写入线程反而没有优化效果。我们一般可以通过 MySQL 服务器保存的全局统计信息,来确定系统的读取和写入比率。</p>
|
||
<p>我们可以通过以下查询来确定读写比率:</p>
|
||
<pre><code>SHOW GLOBAL STATUS LIKE 'Com_select';// 读取数量
|
||
|
||
SHOW GLOBAL STATUS WHERE Variable_name IN ('Com_insert', 'Com_update', 'Com_replace', 'Com_delete');// 写入数量
|
||
</code></pre>
|
||
<p>如果读大于写,我们应该考虑将读线程的数量设置得大一些,写线程数量小一些;否则,反之。</p>
|
||
<ul>
|
||
<li><strong>innodb_log_file_size</strong></li>
|
||
</ul>
|
||
<p>除了以上 InnoDB 缓存等因素之外,InnoDB 的日志缓存大小、日志文件大小以及日志文件持久化到磁盘的策略都影响着 InnnoDB 的性能。 InnoDB 中有一个 redo log 文件,InnoDB 用它来存储服务器处理的每个写请求的重做活动。执行的每个写入查询都会在日志文件中获得重做条目,以便在发生崩溃时可以恢复更改。</p>
|
||
<p>当日志文件大小已经超过我们参数设置的日志文件大小时,InnoDB 会自动切换到另外一个日志文件,由于重做日志是一个循环使用的环,在切换时,就需要将新的日志文件脏页的缓存数据刷新到磁盘中(触发检查点)。</p>
|
||
<p>理论上来说,innodb_log_file_size 设置得越大,缓冲池中需要的检查点刷新活动就越少,从而节省磁盘 I/O。那是不是将这个日志文件设置得越大越好呢?如果日志文件设置得太大,恢复时间就会变长,这样不便于 DBA 管理。在大多数情况下,我们将日志文件大小设置为 1GB 就足够了。</p>
|
||
<ul>
|
||
<li><strong>innodb_log_buffer_size</strong></li>
|
||
</ul>
|
||
<p>这个参数决定了 InnoDB 重做日志缓冲池的大小,默认值为 8MB。如果高并发中存在大量的事务,该值设置得太小,就会增加写入磁盘的 I/O 操作。我们可以通过增大该参数来减少写入磁盘操作,从而提高并发时的事务性能。</p>
|
||
<ul>
|
||
<li><strong>innodb_flush_log_at_trx_commit</strong></li>
|
||
</ul>
|
||
<p>这个参数可以控制重做日志从缓存写入文件刷新到磁盘中的策略,默认值为 1。</p>
|
||
<p>当设置该参数为 0 时,InnoDB 每秒种就会触发一次缓存日志写入到文件中并刷新到磁盘的操作,这有可能在数据库崩溃后,丢失 1s 的数据。</p>
|
||
<p>当设置该参数为 1 时,则表示每次事务的 redo log 都会直接持久化到磁盘中,这样可以保证 MySQL 异常重启之后数据不会丢失。</p>
|
||
<p>当设置该参数为 2 时,每次事务的 redo log 都会直接写入到文件中,再将文件刷新到磁盘。</p>
|
||
<p>在一些对数据安全性要求比较高的场景中,显然该值需要设置为 1;而在一些可以容忍数据库崩溃时丢失 1s 数据的场景中,我们可以将该值设置为 0 或 2,这样可以明显地减少日志同步到磁盘的 I/O 操作。</p>
|
||
<h2>总结</h2>
|
||
<p>MySQL 数据库的参数设置非常多,今天我们仅仅是了解了与内存优化相关的参数设置。除了这些参数设置,我们还有一些常用的提高 MySQL 并发的相关参数设置,总结如下:</p>
|
||
<p><img src="assets/be83083a261bf1302aca81c122b0ac4c.jpg" alt="img" /></p>
|
||
<h2>思考题</h2>
|
||
<p>我们知道,InnoDB 的 IBP 的内存大小是有限的,你知道 InnoDB 是如何将热点数据留在内存中,淘汰非热点数据的吗?</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="float: left">
|
||
<a href="/专栏/Java并发编程实战/37 电商系统表设计优化案例分析.md.html">上一页</a>
|
||
</div>
|
||
<div style="float: right">
|
||
<a href="/专栏/Java并发编程实战/39 答疑课堂:MySQL中InnoDB的知识点串讲.md.html">下一页</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
</div>
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"709971b2beac3d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
</body>
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag() {
|
||
dataLayer.push(arguments);
|
||
}
|
||
gtag('js', new Date());
|
||
gtag('config', 'G-NPSEEVD756');
|
||
var path = window.location.pathname
|
||
var cookie = getCookie("lastPath");
|
||
console.log(path)
|
||
if (path.replace("/", "") === "") {
|
||
if (cookie.replace("/", "") !== "") {
|
||
console.log(cookie)
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
}
|
||
} else {
|
||
setCookie("lastPath", path)
|
||
}
|
||
function setCookie(cname, cvalue) {
|
||
var d = new Date();
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
var expires = "expires=" + d.toGMTString();
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
}
|
||
function getCookie(cname) {
|
||
var name = cname + "=";
|
||
var ca = document.cookie.split(';');
|
||
for (var i = 0; i < ca.length; i++) {
|
||
var c = ca[i].trim();
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
}
|
||
return "";
|
||
}
|
||
</script>
|
||
</html>
|