Linux-mysql-性能优化

压力测试工具

常见 MySQL 压力测试工具

  • mysqlslap
  • Sysbench:功能强大,官网: https://github.com/akopytov/sysbench
  • tpcc-mysql
  • MySQL Benchmark Suite
  • MySQL super-smack
  • MyBench

mysqlslap

mysqlslap:来自于mysql或mariadb包,测试的过程默认生成一个mysqlslap的schema,生成测试表 t1,查询和插入测试数据,mysqlslap库自动生成,如果已经存在则先删除。用--only-print来打印实际 的测试过程,整个测试完成后不会在数据库中留下痕迹

使用格式:

mysqlslap [options]

常用参数 [options] 说明:

--auto-generate-sql, -a        #自动生成测试表和数据,表示用mysqlslap工具自己生成的SQL脚本来测试并发压力
--auto-generate-sql-load-type=type #测试语句的类型。代表要测试的环境是读操作还是写操作还是两者混合的。取值包括:read,key,write,update和mixed(默认)
--auto-generate-sql-add-auto-increment #代表对生成的表自动添加auto_increment列,从5.1.18版本开始支持
--number-char-cols=N, -x N         #自动生成的测试表中包含多少个字符类型的列,默认1
--number-int-cols=N, -y N         #自动生成的测试表中包含多少个数字类型的列,默认1
--number-of-queries=N                  #总的测试查询次数(并发客户数×每客户查询次数)
--query=name,-q                #使用自定义脚本执行测试,例如可以调用自定义的存储过程或者sql语句来执行测试
--create-schema                 #代表自定义的测试库名称,测试的schema
--commint=N                  #多少条DML后提交一次
--compress, -C               #如服务器和客户端都支持压缩,则压缩信息
--concurrency=N, -c N           #表示并发量,即模拟多少个客户端同时执行select。可指定多个值,以逗号或者--delimiter参数指定值做为分隔符,如:--concurrency=100,200,500
--engine=engine_name, -e engine_name #代表要测试的引擎,可以有多个,用分隔符隔开。例如:-
-engines=myisam,innodb
--iterations=N, -i N           #测试执行的迭代次数,代表要在不同并发环境下,各自运行测试多少次
--only-print                #只打印测试语句而不实际执行。
--detach=N                 #执行N条语句后断开重连
--debug-info, -T              #打印内存和CPU的相关信息

mysqlslap示例

#单线程测试
mysqlslap -a -uroot -pmagedu
#多线程测试。使用--concurrency来模拟并发连接
mysqlslap -a -c 100 -uroot -pmagedu
#迭代测试。用于需要多次执行测试得到平均值
mysqlslap -a -i 10 -uroot -pmagedu
mysqlslap ---auto-generate-sql-add-autoincrement -a
mysqlslap -a --auto-generate-sql-load-type=read
mysqlslap -a --auto-generate-secondary-indexes=3
mysqlslap -a --auto-generate-sql-write-number=1000
mysqlslap --create-schema world -q "select count(*) from City"
mysqlslap -a -e innodb -uroot -pmagedu
mysqlslap -a --number-of-queries=10 -uroot -pmagedu


#测试同时不同的存储引擎的性能进行对比
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --iterations=5 --
engine=myisam,innodb --debug-info -uroot -pmagedu
#执行一次测试,分别50和100个并发,执行1000次总查询
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --debug-info -uroot -
pmagedu
#50和100个并发分别得到一次测试结果(Benchmark),并发数越多,执行完所有查询的时间越长。为了准确
起见,可以多迭代测试几次
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --iterations=5 --
debug-info -uroot -pmagedu

 

生产环境 my.cnf 配置案例

参考硬件:内存 32G

#打开独立表空间
innodb_file_per_table = 1
#MySQL 服务所允许的同时会话数的上限,经常出现Too Many Connections的错误提示,则需要增大此值
max_connections = 8000
#所有线程所打开表的数量
open_files_limit = 10240
#back_log 是操作系统在监听队列中所能保持的连接数
back_log = 300
#每个客户端连接最大的错误允许数量,当超过该次数,MYSQL服务器将禁止此主机的连接请求,直到MYSQL服
务器重启或通过flush hosts命令清空此主机的相关信息
max_connect_errors = 1000
#每个连接传输数据大小.最大1G,须是1024的倍数,一般设为最大的BLOB的值
max_allowed_packet = 32M
#指定一个请求的最大连接时间
wait_timeout = 10
# 排序缓冲被用来处理类似ORDER BY以及GROUP BY队列所引起的排序
sort_buffer_size = 16M
#不带索引的全表扫描.使用的buffer的最小值
join_buffer_size = 16M
#查询缓冲大小
query_cache_size = 128M
#指定单个查询能够使用的缓冲区大小,缺省为1M
query_cache_limit = 4M    
# 设定默认的事务隔离级别
transaction_isolation = REPEATABLE-READ
# 线程使用的堆大小. 此值限制内存中能处理的存储过程的递归深度和SQL语句复杂性,此容量的内存在每次
连接时被预留.
thread_stack = 512K
# 二进制日志功能
log-bin=/data/mysqlbinlogs/
#二进制日志格式
binlog_format=row
#InnoDB使用一个缓冲池来保存索引和原始数据, 可设置这个变量到物理内存大小的80%
innodb_buffer_pool_size = 24G
#用来同步IO操作的IO线程的数量
innodb_file_io_threads = 4
#在InnoDb核心内的允许线程数量,建议的设置是CPU数量加上磁盘数量的两倍
innodb_thread_concurrency = 16
# 用来缓冲日志数据的缓冲区的大小
innodb_log_buffer_size = 16M
在日志组中每个日志文件的大小
innodb_log_file_size = 512M
# 在日志组中的文件总数
innodb_log_files_in_group = 3
# SQL语句在被回滚前,InnoDB事务等待InnoDB行锁的时间
innodb_lock_wait_timeout = 120
#慢查询时长
long_query_time = 2
#将没有使用索引的查询也记录下来
log-queries-not-using-indexes

MySQL配置最佳实践

高并发大数据的互联网业务,架构设计思路是"解放数据库CPU,将计算转移到服务层",并发量大的情 况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现"增机器就 加性能"

参考资料:
阿里巴巴Java开发手册:https://developer.aliyun.com/topic/java2020
58到家数据库30条军规解读:http://zhuanlan.51cto.com/art/201702/531364.htm
以下规范适用场景:并发量大、数据量大的互联网业务

 

基础规范

  • 必须使用InnoDB存储引擎
    • 解读:支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高
  • 使用UTF8MB4字符集
    • 解读:万国码,无需转码,无乱码风险,节省空间,支持表情包及生僻字
  • 数据表、数据字段必须加入中文注释
    • 解读:N年后谁知道这个r1,r2,r3字段是干嘛的
  • 禁止使用存储过程、视图、触发器、Event
    • 解读:高并发大数据的互联网业务,架构设计思路是"解放数据库CPU,将计算转移到服务层",并发量 大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现"增 机器就加性能"。数据库擅长存储与索引,CPU计算还是上移吧!
  • 禁止存储大文件或者大照片
    • 解读:为何要让数据库做它不擅长的事情?大文件和照片存储在文件系统,数据库里存URI多好。

命名规范

只允许使用内网域名,而不是ip连接数据库

线上环境、开发环境、测试环境数据库内网域名遵循命名规范

业务名称:xxx

线上环境:xxx.db

开发环境:xxx.rdb

测试环境:xxx.tdb

从库在名称后加-s标识,备库在名称后加-ss标识

线上从库:xxx-s.d

线上备库:xxx-sss.db

库名、表名、字段名:小写,下划线风格,不超过32个字符,必须见名知意,禁止拼音英文混用

库名与应用名称尽量一致,表名:t_业务名称_表的作用,主键名:pk_xxx,非唯一索引名:idx_xxx,唯 一键索引名:uk_xxx

 

 

表设计规范

必须把字段定义为NOT NULL并且提供默认值

解读:
a)null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化
b)null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多
c)null值需要更多的存储空,无论是表还是索引中每行中的null的列都需要额外的空间来标识
d)对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name!='shenjian',如果存在name为null值的记录,查询结果就不会包含name为null值的记录

禁止使用TEXT、BLOB类型

解读:会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急剧降低,影响数据库性能

禁止使用小数存储货币

解读:使用整数吧,小数容易导致钱对不上

必须使用varchar(20)存储手机号

解读:
a)涉及到区号或者国家代号,可能出现+-()
b)手机号会去做数学运算么?
c)varchar可以支持模糊查询,例如:like"138%"

禁止使用ENUM,可使用TINYINT代替

解读:
a)增加新的ENUM值要做DDL操作
b)ENUM的内部实际存储就是整数,你以为自己定义的是字符串?

 

 

索引设计规范

 

  • 单表索引建议控制在5个以内
  • 单索引字段数不允许超过5个
  • 解读:字段超过5个时,实际已经起不到有效过滤数据的作用了
  • 禁止在更新十分频繁、区分度不高的属性上建立索引
    • 解读:a)更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能
    • b)"性别"这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类 似
  • 建立组合索引,必须把区分度高的字段放在前面
  • 解读:能够更加有效的过滤数据

SQL使用规范

禁止使用SELECT *,只获取必要的字段,需要显示说明列属性

解读:
a)读取不需要的列会增加CPU、IO、NET消耗
b)不能有效的利用覆盖索引
c)使用SELECT *容易在增加或者删除字段后出现程序BUG

禁止使用INSERT INTO t_xxx VALUES(xxx),必须显示指定插入的列属性

解读:容易在增加或者删除字段后出现程序BUG

禁止使用属性隐式转换

解读:SELECT uid FROM t_user WHERE phone=13812345678 会导致全表扫描,而不能命中phone索引,猜猜为什么?(这个线上问题不止出现过一次)

禁止在WHERE条件的属性上使用函数或者表达式

解读:SELECT uid FROM t_user WHERE from_unixtime(day)>='2017-02-15' 会导致全表扫描
正确的写法是:SELECT uid FROM t_user WHERE day>= unix_timestamp('2017-02-15 00:00:00')

禁止负向查询,以及%开头的模糊查询

解读:
a)负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描
b)%开头的模糊查询,会导致全表扫描

禁止大表使用JOIN查询,禁止大表使用子查询

解读:会产生临时表,消耗较多内存与CPU,极大影响数据库性能

禁止使用OR条件,必须改为IN查询

解读:旧版本Mysql的OR查询是不能命中索引的,即使能命中索引,为何要让数据库耗费更多的CPU帮助实施查询优化呢?

应用程序必须捕获SQL异常,并有相应处理

posted @ 2022-06-20 10:32  goodbay说拜拜  阅读(425)  评论(0编辑  收藏  举报