Skip to content

Instantly share code, notes, and snippets.

@dispensable
Last active October 25, 2022 02:40
Show Gist options
  • Save dispensable/69eabca61ecd26936bd46ead3f1d1465 to your computer and use it in GitHub Desktop.
Save dispensable/69eabca61ecd26936bd46ead3f1d1465 to your computer and use it in GitHub Desktop.
《高性能MySQL》读书笔记

选择优化的数据类型

选择的原则

  1. 更小的通常更好,但是不要低估了数据的使用范围
  2. 简单就好
    • 整型比字符操作代价更低(字符集的排序和校对很复杂)
    • 使用mysql内建的类型而不是字符串来存储时间
    • 用整数来存储IP
  3. 尽量避免NULL
    • NULL列很难优化,索引,索引统计和值比较都很复杂
    • NULL列会使用更多的空间
    • 当可为NULL的列被索引时,每个索引需要记录一个额外的字节,MyISAM还可能导致索引从固定大小的索引变成可变大小的索引
    • 如果列要建索引,尽量不用使用NULL值
    • Innodb使用单独的bit来存储NULL值

整数类型

  1. 类型 TINYINT(8), SMALLINT(16), MEDIUMINT(24), INT(32), BIGINT(64)
  2. 属性 可选的UNSIGNED属性 TINYINT UNSIGNED
  3. 陷阱
    1. 整数运算一般使用64位的整数
    2. mysql可以为整数指定宽度,int(11),但是不会限制整数的合法值,只是规定了一些交互工具显示的个数,对于存储和计算来说都是一样的

实数类型

  1. 类型 FLOAT/DOUBLE/DECIMAL
  2. 陷阱
    • 只在需要很精确的数据的时候才使用decimal
    • 可以使用bigint来存decimal的数字,通过乘以对应的倍数来实现,这样效率好而且避免了不精确的问题

字符串类型

1. 类型
    - VARCHAR/CHAR
    - BINARY/VARBINARY
    - BLOG(TINYBLOB, SMALLBLOB, BLOB, MEDIUMBLOB, LONGBLOB)/TEXT(同样前缀)
    
2. 细节
    - varchar
        1. 存储空间占用较少,因为是定长的。需要额外的字节来存储元信息;
        2. 如果update频繁,会导致片段分裂,页分裂
        3. 下面情况使用varchar
            - 字符串的列的最大长度比平均的长度大很多
            - 列的更新更少,所以碎片不是问题
            - 使用了像utf-8这样的字符集,每个字符都使用不同的字节数进行存储
        4. 老版本的mysql会剔除末尾空格
        5. innodb会把很长的字符串转成blob存储
        6. 慷慨是不明智的,只分配自己需要的空间,MySQL会分配定长的内存块来存字符,更长的字符但是空闲会导致排序和其他操作效率降低
    - char
        1. 根据定义的长度分配足够的空间,末尾空格会被剔除
        2. 适合存密码之类的定长字符,不容易碎片
        3. binary/varbinary存储字节码而不是字符
    - blob/text
        1. 一个是二进制一个是字符串
        2. 排序只能排序一小部分(`ORDER BY SUBSTRING(col, len)`)
        3. 索引只能索引固定长度不能全部
        4. 磁盘临时表和排序
            blob和text不被memory引擎支持,如果大量的需要排序的情况就只能使用临时磁盘表,但是可以使用substring来转成字符串使用内存表
            如果explain执行计划的extra列包含 using temporary则说明查询使用了临时表
    - ENUM替代字符串
        1. enum实际存储成了整数
        2. 枚举字段是按照内部的整数而不是定义的字符串来排序的
        3. 避免varchar和enum相关联
        4. 在查找表的时候采用整数主键而避免采用基于字符串的值进行关联

日期和时间类型

1. DATETIME/TIMESTAMP
2. datetime
    - 8字节存储
    - 范围从0000-9999年
    - 精度为秒
3. timestamp
    - 时间戳
    - 1970 - 2038
    - 4字节存储
    - 时间戳和存储的时区密切相关
    - 如果插入的时候没有指定第一个timestamp的值服务器会默认给当前时间
    - not null
    - 插入一行记录的时候也会自动更新第一个timestamp的值
4. 如果需要存储比s更精细的时间戳可以使用BIGINT或者DOUBLE存储

位数据类型

1. BIT
    - BIT是字符串类型而不是数字类型
    - 检索BIT将会得到一个字符码对应的字符串
    - 如果想在一个BIT中存储true/false最好使用char(0)
    - MYISAM打包所有的BIT列,innodb则使用的是最小的整数,所以还是不能节约空间
2. SET
    - find_in_set() field() 函数
    - 可以有效的节约空间
    - 但是扩展需要alter table
    - 无法使用索引查找
    - 可以使用整数列来包装一系列的位

选择标识符

  1. 整数类型通常是标识符最好的选择,很快而且可以自增;
  2. ENUM SET对于标识符很糟糕
  3. 字符串类型应该避免使用,很消耗空间而且比数字类型慢,myisam还是压缩字符导致很慢
  4. 避免使用完全随机的字符串,这些函数生成的新值在很大的空间内,会导致页分裂,聚簇索引碎片,磁盘随机访问,select慢因为逻辑上相邻的行结果在磁盘和内存的不同位置。局部访问性原理失效
  5. 当心自动生成的scheme

特殊数据类型

  • 使用无符号整数存IP 然后使用INET_ATON INET_NTOA来转化

常见陷阱

  • 太多的列(数千个字段会导致转换的代价非常的高)
  • 太多的关联
    1. 最多可以关联61各表
    2. 建议在12个表以内做关联
  • 过分使用枚举
  • 区分枚举和集合
  • 尽量避免使用NULL但是该用还是要用(mysql会在索引中存储NULL值)

范式化的优点和缺点

  1. 范式化的更新操作通常比反范式化要快
  2. 当数据较好的范式化时就只有很少或者没有重复数据,所示一需要修改很少的数据
  3. 范式化的表通常更小,可以放到内存里,所以执行操作会快
  4. 很少有多余的数据意味着更少需要group或者distinct
  5. 但是需要关联,反范式化可以避免关联最大限度的使用顺序IO

缓存和汇总表

  • 可以加快性能
  • 实时维护和定期维护
  • 影子表切换原数据和影子数据加快数据重建 先建设一张空的表然后填充数据然后对调两个表
  • 物化视图- flexviews
  • 计数器表的优化
    1. 单纯的使用一个计数器会导致锁定
    2. 可以重复插入很多行,更新的时候随机选择一行更新,聚合的时候汇总所有的行
    3. 如果需要每个日期一个计数可以加字段然后定期汇总字段减少行的使用
  • 代价: 增加读写的开发难度,需要维护额外的数据,写更慢了

加快alter table的速度

  • facebook - online schema change
  • open toolkit
  • percona toolkit
  • fixviews - CDC工具(捕获服务器的二进制日志)
  • 主库备库切换,备库先执行修改操作,然后切换
  • 影子拷贝 通过重命名和删表操作交换两个表
  • 修改frm文件,先锁定表,然后建设一个需要的字段的表,然后替换frm文件,然后释放锁定
  • 先关闭索引然后载入大量数据然后重新打开索引

可扩展性

  • 定义: 当增加资源以处理负载和增加容量时系统能够获得的投资产出率
  • USL可扩展性定律(线性扩展的偏差可以通过两个因素来建立模型)
    1. 无法并行执行的一部分工作 - Amdahl定律
    2. 需要交互的另外一部分工作 - 内部节点和进程间通信+amdahl定律
  • 约束理论 通过减少以来时间和统计变化来改进系统的吞吐量和性能

扩展mysql

  1. 规划可扩展性 - 在项目开始的时候就预先规划容量
    • 应用的功能完成了多少
    • 预期的最大负载是多少
    • 如果以来系统的每个部分来分担负载那么失效的情况如何处理,是否需要预留容量
  2. 为扩展赢得时间 - 可以推迟具体的扩展计划的方式
    • 优化性能 - 建立索引或者优化查询等
    • 购买性能更好的硬件
  3. 向上扩展/垂直扩展 - 购买更好的性能更强悍的机器
  4. 向外扩展/水平扩展 - 将任务分配到多台机器上
    • 复制拆分和数据分片
    • mysql节点的可能性
      1. 一个主主复制双机结构,拥有一个主动和一个被动服务器
      2. 一个主库和多个备库
      3. 一个主动服务器并使用块复制设备(DRBD)作为备用服务器
      4. 一个基于存储区域网络(SAN)的集群
    • 按功能划分
      • 不同的功能划分到不到的实例上或者单个实例的不同库对应不同的功能不做关联
      • 单个功能的单个实例还是有性能瓶颈
    • 数据分片
      • 数据分割成多片存在不同的节点
      • 过程: 复制分担读查询 -> 按功能拆分到不同实例 -> 单一功能的数据分片
      • 需要数据抽象层
      • 如果不用分尽量部分,通过优化的方式来完成
      • 选择分区键
        1. 分区键决定了在哪里存和哪里找数据
        2. 尽量减少重要且频繁查询的数据的分片
        3. 使用实体关系图来分片,找到关键节点
        4. 尽可能选择那些能够避免跨分片查询的
        5. 让分片尽可能小和公平
      • 多个分区键(需要冗余存储)
      • 跨分片查询
        1. 使用中间件来查询
        2. 使用汇总表和缓存冗余保存查询
        3. 只能应用来检查一致性
        4. 使用定期执行的任务来检查一致性清除数据等
      • 分配数据分片和节点
        1. 分片和节点不是一对一的关系
        2. 保持分片小尽量一个节点可以存多个分片
        3. 小片有利于分片的平衡和转移
        4. 大小是能够在5-10分钟能提供日常的维护工作的程度,太小的话会引发其他问题
      • 在节点上部署分片
        • 每个分片使用单一数据库而且库名相同
        • 讲过个分片的表放到一个数据库中,每个表明上包含分片号
        • 为每个分片使用一个数据库,并在数据库中包含所有应用需要的表,在数据库名中包含分片号,但是表名没有分片号
        • 每个分片使用一个数据库,并在数据库名和表明中包含分片号
        • 在每个节点是运行多个mysql实例,每个实例上一个或者多个分片
      • 固定分配
        • 实现简单
        • 如果分片很大数量不多很难均衡不能分片间的负载
        • 固定分片的方式无法自定义数据放到哪个分片上,如果分片间负载不均衡会有问题 - 所以分片尽可能小
        • 修改分片策略通常比较困难
      • 动态分配
        • 使用一个额外的表来存储具体的数据行和shardid的对应关系
        • 对数据存储位置精确控制
        • 灵活,减少跨分片查询
      • 混合动态和固定分配
      • 显式分配
      • 重新均衡分片数据
        • 使用动态分配策略,当一个分片快满的时候可以设置一个标志告诉应用不要再放数据了
        • 为每个分片设置两台备库,然后使用归档工具来剔除不要的数据,可以不停机的分开两片数据
      • 生成全局唯一ID
        • 使用auto_increment_increment和auto_increment_offset - 一个奇数一个偶数一个增加1一个增加2
        • 全局节点中创建表 - 从一个全局的表里申请ID
        • 使用memcached的incr数据,使用redis来申请ID
        • 批量分配数字 - 从全局节点里批量申请ID 不够再获取
        • 使用复合值 - 分片ID+自增ID
        • 使用GUID - 不过不适合作为INNODB的主键
        • snowflake算法/美团leave
      • 分片工具
        • hibernate shards
        • shared-query
        • sphinx
  5. 通过多实例扩展
    • 每个mysql实例绑定到单个CPU核心上
    • 在性能强悍的服务器上最好运行多个服务
  6. 通过集群扩展
    • mysql cluster / NDB
    • clustrix
    • scalebase
    • geniedb
    • akiban
  7. 向内扩展 - 归档一些很少或者从不使用的数据
    • 对应用的影响 - 逐步剔除 不要影响事务处理
    • 要归档的行
    • 维护数据的一致性
    • 避免数据丢失
    • 解除归档
    • 保持活跃数据独立
      • 将表划分为几个部分
      • mysql分区
      • 基于时间的数据分区

负载均衡

  • 可扩展、高效性、可用性、透明性、一致性 (wackamole/dns/lvs/f5等hardware/tcp代理/mysql proxy/应用负载均衡)
  1. 直接链接

    • 复制上的读写分离
      • 基于查询分离 - 能容忍脏数据的查询分到备库或者被动服务器上
      • 基于脏数据分离 - 应用检查复制延迟
      • 基于会话分离 - 如果用户自己做了更新那么标记成已经更新,这段时间的查询都指向主库而不是备库
      • 基于版本分离 - 使用版本号如果用户更新了自己的数据那么版本可以更新然后去主库查
      • 基于全局版本/会话分离 - 使用show master/slave status操作来检查是否已经更新到了备库
    • 修改应用的配置 - 修改配置来使用不同的机器处理不同的程序
    • 修改DNS名
    • 转移IP地址 pacemaker
  2. 引入中间件

    • 负载均衡器 - 注意mysql的一些特定的点是否负载均衡支持
    • 负载均衡算法 - 随机/轮询/最少连接数/最快响应/哈希/权重
    • 在服务器池中添加或者删除服务器
  3. 一主多备间的负载均衡

    • 功能分区 - 不同功能的备库
    • 过滤和数据分区 - 使用复制过滤的功能来分区
    • 将部分写操作转移到备库
    • 保证备库跟上主库
    • 同步写操作 - 确保一定写到了至少一个备库上才算成功 半同步复制

mysql复制方式

  • 基于行的复制

    • 好处:
      1. 实现简单
      2. 在多种方式下都能够正常工作,因为本质是重放sql语句
      3. 更加灵活,例如可以在备库上修改schema然后提升为主库
      4. 更容易debug,因为执行的语句是人可以看懂的
    • 坏处:
      1. 很多是不能重放得,使用触发器和存储过程无法正常运行,一些和当时的环境相关的函数无法正常运行
      2. 运行只能串行,需要更多的锁,性能不好
  • 基于语句的复制

    • 好处
      1. 几乎没有行不能适应的模式,但是修改schema的时候可能失效
      2. 性能好,锁的使用少,无需串行化
      3. 数据更新记录更加彻底,可以看到sql语句不知道的更新细节
      4. 占用很少的CPU相比逻辑复制
      5. 基于行的复制能够更快的找到数据不一致的情况
    • 坏处
      1. 全表更新的数据会出发大量的行,增加复制的负担
      2. 无法判断执行了哪些sql,执行方式是黑盒的
      3. 无法处理在备库修改schema的情况,但是逻辑复制可以

MySQL自动切换两种方式的使用,默认的使用基于语句的方式,如果无法正确的复制的话,就切换到基于行的模式 可以自己控制两种方式,使用binglog_format变量来控制

复制解决的问题

  • 数据分布
  • 负载均衡
  • 备份
  • 高可用和故障切换
  • Mysql升级测试

复制的工作方式

  1. 主库把数据变更记录到二进制日志中(在事务提交之前) --> binlog
  2. 备库将主库上的日志复制到自己的中继日志中(备库启用一个IO线程,连接到主库然后读取二进制日志并转储到中继日志) --> Relay log
  3. 备库读取中继日志中的事件,将其重放到备库数据之上(备库的SQL线程读取自己的中继日志并运行SQL)

优点和缺点

  1. 实现了获取和重放事件的解耦,允许这两个过程异步运行;
  2. 限制了复制的过程,主库上并发运行的事件在从库上只能串行化执行,因为只有一个线程来重放中继日志的事件

配置复制

完全从头配置

  1. 在每台服务器上创建复制账号;
  2. 配置主库和备库;
  3. 通知备库链接到主库并从主库复制数据

从另一个服务器开始复制(已经运行了一段时间)

  1. 使用冷备份(关闭服务器,复制数据文件然后启用从库)
  2. 使用热备份(MyISAM -- 使用mysqlhotcopy或者rsync来复制数据)
  3. 使用mysqldump
mysqldump --single-transaction --all-databases --master-data=1 --host=server1 | mysql --host=server2
  1. 使用快照或者备份(知道对应的二进制坐标就可以使用备份+二进制坐标的方式来重放)
  2. 使用percona xtrabackup
  3. 使用其他的备库

不要使用load data from master / load table from master,过时、缓慢、容易出错而且只能用于MyISAM

推荐的配置

  1. sync_binlog=1 开启这个选项,在每次提交事务之前将二进制日志同步到磁盘,如果不开启的话,可能在崩溃的时候二进制日志损坏导致不能正确的复制数据(适用于二进制日志,中继日志没必要);
  2. innodb_flush_logs_at_trx_commit提交事务之前刷新日志到磁盘
  3. innodb_support_xa=1 or innodb_safe_binlog
  4. log_bin=/xx/xx/mysql-bin 强制指定二进制日志的名字,不然是用服务器的名字来命名二进制日志的,在迁移和备份的时候会有很多问题;
  5. relay_log=/xx/xx/relay-bin 强制指定中继日志的名字,防止很多问题
  6. skip_slave_start 在崩溃后不要自己启动复制,因为这时候数据已经不一致了,强制启动只会导致更复杂的结果
  7. read_only 关闭写权限,防止意外的修改从库导致的数据不一致
  8. sync_master_info=1 sync_relay_log=1 sync_relay_log_info=1 防止msterinfo和中继日志在服务器崩溃时候的损坏(有fsync()开销
  9. relay_log_space_limit (如果主库和备库的差距非常大中继日志可能会很大,大到磁盘爆满,所以可以开启这个来防止备库的磁盘爆满)
  10. expire_log_days 过期二进制日志(不要手工删除日志,不然可能清理日志的命令不能正常工作)

复制的原理

在主库上记录二进制日志,在从库上重放日志的方式来实现异步的数据复制 ==> 意味着从库的数据可能和主库不一致,存在延迟

复制文件

  • mysql-bin.index 记录了所有的二进制文件
  • mysql-relay-bin-index 记录了所有中继日志文件
  • master.info 保存备库连接到主库的所有信息,密码之类的
  • relay-log.info 记录了当前备库复制的二进制日志和中继日志的坐标

发送复制事件到其他服务器

log_slave_updates让备库变成其他服务器的主库 注意所有服务器必须有一个唯一的ID,不然会导致循环复制

复制过滤

主库过滤: 不用使用binlog_do_db和binlog_ingore_db来过滤,他们只会在当前数据库上执行过滤 备库过滤: 设置replicate_*选项来过滤配置

复制拓扑

基本原则

  1. 一个mysql备库实例只能有一个主库
  2. 每个备库必须有一个唯一的id
  3. 一个主库可以有多个备库
  4. 如果打开了log_slave_updates选项,一个备库可以把其他主库上的数据变化传播到其他备库

一个主库多个备库

用途

  • 为不同的角色使用不同的备库(例如添加不同的索引或者使用不同的存储引擎)
  • 把一台备库当作待用的主库,处理复制没有其他数据传输
  • 将一台备库放到远程数据中心,用作灾难恢复
  • 使用其他一个备库作为备份,培训,开发或者测试使用的服务器

主动主动模式下的主主复制

冲突难以解决

主动-被动下的主主复制

互相都是对方的备库,但是只有一个服务器是可写的,避免冲突的同时,可以快速切换主库备库的读写

拥有备库的主主结构

环形复制

分发主库

主库的分发压力大的时候,可以设置一个备库,然后多个备库链接到这个备库,这个备库可以使用blackhole引擎,从而降低主库的负载 但是分发主库会导致二进制日志的位置不一样,备库无法简单的提升为主库,因为不知道是否已经同步完毕且数据一致

树或者金字塔型

定制的复制方案

  • 选择性复制(划分数据到不同的数据库然后复制到不同的备库上 | 通过分发主库和过滤规则来分发
  • 分离功能(OLTP和OLAP使用不同的数据库
  • 数据归档(PT-archieve 选择性的禁用主库的二进制日志然后清理数据或者使用一个空的清理库,备库使用ignore来忽略
  • 将备库用作全文检索
  • 只读备库(设置read_only
  • 模拟多主库备份(将需要复制的备库轮流指向需要复制的主库源
  • 创建日志服务器(binlog重放的使用使用空的数据库实例而不是mysqlbinlog 更清晰好用而且bug少

复制只能扩展读操作,无法扩展写操作

备库的管理和维护

show master status/logs;
show binlog events in 'mysql-bin.00000223' from 11111\G;
show slave status;
  • 备库的延迟报告不是准确的,seconds_behind_master的值只有在执行事件的时候才会得到报告
  • 如果备库线程没有运行,延迟为NULL
  • 一些错误可能中断复制,但是seconds_behind_master的显示是0
  • 备库线程在运行但无法计算延时的时候,显示的是0或者NULL
  • 一个大事务会导致延迟波动
  • 如果分发主库落后了,备库延迟也还是0其实和源头的主库是有差距的
  • 使用heartbeat record来记录延迟- pt-heartbeat的复制心跳

使用pt-table-checksum工具来检查主备是否数据一致(使用复制实现)

pt-table-checksum --replicate=xx.checksum <master_host>

使用pt-table-sync来同步有差别的表

改变主库

计划内

  1. 停止当前主库上的所有写操作
  2. flush tables with read lock锁定所有的写入
  3. 选择一个备库作为新的主库,并确保已经完全跟上
  4. 确保数据是一致的
  5. 在新主库上执行stop slave
  6. 在新主库执行change master t o master_host=''然后reset salve使其断开和看主库的链接,并丢弃master.info中的消息
  7. 执行show master status 新主库的二进制坐标日志
  8. 确保其他备库已经追赶上
  9. 关闭旧的主库
  10. 在mysql5.1+如果需要的激活新主库的事件
  11. 将客户端连接到新主库
  12. 在每台备库上执行chage master to命令使用之前获取到的二进制日志的位置,指向新的主库

计划外

  1. 确定那台备库的数据最新 - 在所有备库执行show slave status 命令然后查看master_log_file/read)master_log_pos值最新的那个
  2. 让所有备库执行完毕从主库中获得的事件
  3. 执行刚才的5-7
  4. 比较每台备库和新主库上的master_log_file/read_master_log_pos的值
  5. 执行前一小节的10-12步

确定期望的日志位置

备库和主库的位置不一样的时候,需要找到备库最后一条执行的事件在新主库的二进制日志中的相应的位置,然后再执行change master to

  • 通过mysqlbinlog从二进制日志或者中继日志中解析出每台备库上执行的最后一个事件然后同样使用这个命令解析新主库上的二进制日志,找到相同的查询,mysqlbinlog会打印出该事件的偏移量,再change_master to中使用这个量
  • 把新主库和停止的备库上的偏移量相减,然后把这个差值和新主库当前的二进制日志的位置详见就可以得到期望查询的位置,通过刚的命令了简单验证一下就可以启动备库了

在主主配置中交换角色

  1. 停止主动服务器上的所有写入
  2. 在主动服务器上执行set global read_only=1 同时配置文件里也配置,但是这不会阻止超级权限用户的修改,如果要阻止flush table with read lock,或者kill所有的写入链接
  3. 在主动服务器执行show master status 并记录二进制日志坐标
  4. 使用主动服务器上的二进制日志坐标在被动服务器上执行select master_pos_wait(),该语句会阻塞知道复制跟上主动服务器;
  5. 在被动服务器执行set global read_only=1这样就成了主服务器;
  6. 修改应用的配置,使其写入到新的主服务器中

复制的问题和解决方案

  1. 数据损坏或者丢失

    • 主库意外关闭 开启sync_binlog,使用pt-checktablesum工具来检查主备一致性便于恢复
    • 备库意外关闭 如果master.info没有写入磁盘,自动的同步可能位置都是错误的,只能忽略这些错误 pt-slave-restart工具可以使用 innodb可以在重启后观察mysql的错误日志会打印出他的回复点的二进制日志坐标,可以使用这个值来决定备库指向主库的偏移量
    • 主库的二进制日志损坏 使用set global sql_salve_skip_counter =1 来忽略一个损坏的事件知道找到没损坏的地方或者手工修复
    • 备库的中继日志损坏 如果主库没问题,直接用change master to 指定一个新的中继日志
    • 二进制日志和innodb的事务日志不同步 sync_binlog safe_binlog
  2. 使用非事务型表 非事务型表在更新时候kill查询会导致复制失败或者不一致,在myisam关闭的时候一定要执行stop slave,否则会kill所有正在运行的查询导致复制失败

  3. 混合事务型和非事务型表 基于语句的复制会导致混合使用两种类型的引擎发生问题,基于行的不会受到影响

  4. 不确定语句 有些语句limit依赖查询的顺序来update可能会导致不一致

  5. 主库备库使用不同的引擎

  6. 备库发生数据变更

  7. 不唯一的服务器ID 会观察到备库和主库不停的断开和链接,复制可能正确也可能错误,要小心的配置服务器的id,最好和某些地方有所关联

  8. 未定义的服务器id

  9. 对未复制数据的依赖性(不要创建主库备库没有的库)

  10. 丢失的临时表

  11. 不复制所有的更新

  12. innodb加锁引读引起的锁争用

  13. 在主主复制结构中写入两台从库

  14. 过大的复制延迟

    • 单线程的设计很难调优
    • 如果备库用于查询,锁定的时候还不能执行写入
    • 不要重复写操作中代价比较高的部分
    • 在复制之外并行写入
    • 为复制线程预取缓存
  15. 来自主库的过大的包 max_allowed_packet需要主备匹配

  16. 受限制的复制带宽

  17. 磁盘空间不足 relay_log_space

  18. 复制的局限性

复制有多快

高级复制特性

  • 半同步复制
  • 并行复制(基于schema)
  • 复制心跳,防止悄无声息的复制中断
  • 二进制日志的checksum
  • 备库延迟复制
  • 允许基于行的二进制日志事件也包含在主库执行的sql
  • 解决group commit问题

其他复制技术

  • tungsten

引擎

  • innodb
  • myisam
  • archive
  • blackhole
  • csv
  • federated 远程访问mysql(可以用来查询复制的执行速度)
  • memeory
  • merge
  • ndb
  • xtrradb
  • tokudb
  • rethinkdb
  • falcon
  • infobriight 面向列的存储引擎-数据分析和数据仓库-块数据级别的索引
  • aria maria前身
  • groonga 全文索引
  • oqgraph 图操作
  • spinxse 全文检索
  • spider 透明分片
  • vpformysql 垂直分区
  • q4m 队列操作

索引类型

  • B-Tree索引
  • 哈希索引/自适应hash索引
  • 空间数据索引
  • 全文索引
  • 分形树索引
  • 聚簇索引
  • 覆盖索引 - 索引包含了需要查询的所有列的值

索引优点

  • 索引大大减少了服务器需要扫描的数据量
  • 索引可以帮助服务器避免排序和临时表
  • 索引可以将随机IO变为顺序IO

索引评价

  • 是否将相关记录放在一起
  • 索引中的数据顺序是否和查找中的顺序一致
  • 索引列是否包含了查询中需要的全部列

索引替代

  • 小表全表扫描更快
  • 中到大型表索引很有效
  • 特大型表-分区表
  • 表数量很多的时候-建立元信息数据表
  • TB级别数据-块级别元数据来代替索引

高性能的索引策略

  • 独立的列不能是表达式的一部分, eg: select * from xxx where xxx + 1 = 5; / 索引不命中
  • 前缀索引和索引选择性-太长的字符或数据可以使用前缀来索引,提升前缀索引的选择性/模拟hash索引/后缀索引(字符反向存储然后索引前缀-触发器或者手动维护)
  • 多列索引-每个列建立索引不好,即使是触发了索引合并也证明这个索引很糟糕
  • 注意多列索引的顺序性,选择性高的列放在前面/但是也要考虑数据分布-比如guest用户
  • 聚簇索引-innodb的主键是聚簇索引,没主键会自己选唯一字段,没有的话就隐式的主键 好处: 数据在一起,如果使用索引来获取的话,快,IO少 | 数据访问更快,从索引里直接拿到数据 | 覆盖索引扫描的查询可以直接使用叶节点中的主键值 坏: 如果数据都在内存,那没意义了 | 插入速度依赖与插入顺序 | 更新聚簇索引的代价很高 | 插入新行的时候面临页分裂的问题 | 可能导致全表扫描慢 | 二级索引可能比想的大 | 二级索引需要两次索引查找(一次找到主键,一次按主键找到数据)
  • innodb要按顺序插入数据,主键最好给个字自增id(顺序插入页分裂少)
  • 顺序插入数据的时候又可能导致auto_increment和间隙锁(主键上界)的争用需要考虑重新设计表和应用或者设置innodb_autoinc_lock_mode设置
  • 使用索引扫描排序
  • 如果不能覆盖可以在where中尽量使用索引过滤尽可能多的行然后回表取数据匹配
  • like在通配符开头的地方不能使用索引(只能匹配最左前缀
  • 尽量删除冗余重复的索引
  • 索引可以减少锁的使用
  • 找到并修复损坏的表/更新索引统计信息/减少索引和数据的碎片
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment