数据库服务衡量标准
Qps:query per secondTps:transaction per second
1. 压力测试工具
1.1 常见MySQL压力测试工具
mysqlslapSysbench: 功能强大, 官网: akopytov/sysbench: Scriptable database and system performance benchmarktpcc-mysqlMySQL Benchmark SuiteMySQL super-smackMyBench
1.2 mysqlslap
mysqlslap : 来自于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 -pmagedu2. 生产环境 my.cnf 配置案例
配置文件生成工具参考链接 : https://imysql.com/my_cnf_generator
参考硬件 : 内存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-indexes3. MySQL配置最佳实践
高并发大数据的互联网业务 , 架构设计思路是“解放数据库CPU , 将计算转移到服务层” , 并发量大的情况下 , 这些功能很可能将数据库拖死 , 业务逻辑放到服务层具备更好的扩展性 , 能够轻易实现“增机器 就加性能”
参考资料 :
阿里巴巴
Java开发手册: 泰山版Java开发手册-阿里云开发者社区58到家数据库30条军规解读 : 58到家数据库30条军规解读-58到家集团简介
以下规范适用场景: 并发量大、数据量大的互联网业务
3.1 基础规范
必须使用
InnoDB存储引擎解读: 支持事务、行级锁、并发性能更好、
CPU及内存缓存页优化使得资源利用率更高使用
UTF8MB4字符集解读: 万国码 , 无需转码 , 无乱码风险 , 节省空间 , 支持表情包及生僻字
数据表、数据字段必须加入中文注释
解读: N年后谁知道这个
r1,r2,r3字段是干嘛的禁止使用存储过程、视图、触发器、
Event解读: 高并发大数据的互联网业务 , 架构设计思路是“解放数据库CPU , 将计算转移到服务层” , 并发量 大的情况下 , 这些功能很可能将数据库拖死 , 业务逻辑放到服务层具备更好的扩展性 , 能够轻易实现 “增机器就加性能”。数据库擅长存储与索引 , CPU计算还是上移吧!
禁止存储大文件或者大照片
解读: 为何要让数据库做它不擅长的事情?大文件和照片存储在文件系统 , 数据库里存URI多好。
3.2 命名规范
只允许使用内网域名 , 而不是
ip连接数据库线上环境、开发环境、测试环境数据库内网域名遵循命名规范
业务名称:
xxx线上环境:
xxx.db开发环境:
xxx.rdb测试环境:
xxx.tdb从库在名称后加
-s标识 , 备库在名称后加-ss标识线上从库:
xxx-s.db线上备库:
xxx-sss.db库名、表名、字段名: 小写 , 下划线风格 , 不超过
32个字符 , 必须见名知意 , 禁止拼音英文混用库名与应用名称尽量一致 , 表名:
t_业务名称_表的作用, 主键名:pk_xxx, 非唯一索引名:idx_xxx, 唯一键索引名:uk_xxx
3.3 表设计规范
单实例表数目必须小于
500单表行数超过
500万行或者单表容量超过2GB, 才推荐进行分库分表。说明: 如果预计三年后的数据量根本达不到这个级别 , 请不要在创建表时就分库分表
单表列数目必须小于
30表必须有主键 , 例如自增主键
解读:
主键递增 , 数据行写入可以提高插入性能 , 可以避免
page分裂 , 减少表碎片提升空间和内存的使用主键要选择较短的数据类型 ,
Innodb引擎普通索引都会保存主键的值 , 较短的数据类型可以有效的 减少索引的磁盘空间 , 提高索引的缓存效率无主键的表删除 , 在
row模式的主从架构 , 会导致备库夯住
禁止使用外键 , 如果有外键完整性约束 , 需要应用程序控制
解读: 外键会导致表与表之间耦合 ,
update与delete操作都会涉及相关联的表 , 十分影响sql 的性能 , 甚至会造成死锁。高并发情况下容易造成数据库性能 , 大数据高并发业务场景数据库使用以性能优先
3.4 字段设计规范
必须把字段定义为
NOT NULL并且提供默认值解读:
null的列使索引/索引统计/值比较都更加复杂 , 对MySQL来说更难优化null这种类型MySQL内部需要进行特殊处理 , 增加数据库处理记录的复杂性;同等条件下 , 表中有较 多空字段的时候 , 数据库的处理性能会降低很多null值需要更多的存储空 , 无论是表还是索引中每行中的null的列都需要额外的空间来标识对
null的处理时候 , 只能采用is null或is not null, 而不能采用=、in、<、<>、!=、not in这些操作符 号。如:where name!=’shenjian’, 如果存在name为null值的记录 , 查询结果就不会包含name为null值的记录
禁止使用
TEXT、BLOB类型 解读: 会浪费更多的磁盘和内存空间 , 非必要的大量的大字段查询会淘汰掉热数据 , 导致内存命中率急 剧降低 , 影响数据库性能禁止使用小数存储货币 解读: 使用整数吧 , 小数容易导致钱对不上
必须使用
varchar(20)存储手机号解读:
涉及到区号或者国家代号 , 可能出现
+-()手机号会去做数学运算么?
varchar可以支持模糊查询 , 例如:like“138%”
禁止使用
ENUM, 可使用TINYINT代替解读:
增加新的
ENUM值要做DDL操作ENUM的内部实际存储就是整数 , 你以为自己定义的是字符串?
3.5 索引设计规范
单表索引建议控制在
5个以内单索引字段数不允许超过
5个 解读: 字段超过5个时 , 实际已经起不到有效过滤数据的作用了禁止在更新十分频繁、区分度不高的属性上建立索引
解读:
更新会变更
B+树 , 更新频繁的字段建立索引会大大降低数据库性能“性别”这种区分度不大的属性 , 建立索引是没有什么意义的 , 不能有效过滤数据 , 性能与全表扫描类似
建立组合索引 , 必须把区分度高的字段放在前面
解读: 能够更加有效的过滤数据
3.6 SQL使用规范
禁止使用
SELECT *, 只获取必要的字段 , 需要显示说明列属性解读:
读取不需要的列会增加
CPU、IO、NET消耗不能有效的利用覆盖索引
使用
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')禁止负向查询 , 以及
%开头的模糊查询解读:
负向查询条件:
NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等 , 会导致全表扫描%开头的模糊查询 , 会导致全表扫描
禁止大表使用
JOIN查询 , 禁止大表使用子查询解读: 会产生临时表 , 消耗较多内存与CPU , 极大影响数据库性能
禁止使用
OR条件 , 必须改为IN查询解读: 旧版本
Mysql的OR查询是不能命中索引的 , 即使能命中索引 , 为何要让数据库耗费更多的CPU帮助实施查询优化呢?应用程序必须捕获
SQL异常 , 并有相应处理