mysql***能调优
使用memcache,redis作为缓存,使用mariaDB 的XtraDB引擎
数据库垂直切分,水平切分,主从复制,读写分离
服务化+消息中间件,实现异步,解耦,并行,写合并....
降级
1,硬件
采用高***价比PC服务器,大内存,强劲CPU
采用高***能PCI-E Flash卡作为cache, 提高系统的IO***能
充分利用系统各部件的cache, 大胆采用新技术
充分考虑容灾,在各个层面考虑数据的安全***
Cache分配:
MySQL内部cache
匿名页面/文件页面
Flashcache 混合存储
Raid卡内部cache
数据访问规律导向,随机数据和顺序数据尽量分离
尽量提高IO的利用率,减少无谓的IO能力浪费
在安全***的前提下,尽可能的利用好系统各个层次cache
2,mysql
使用独享表空间不用考虑 innodb_file_per_table
关闭自动提交
设置隔离级别为读提交
set tx_isolation='read-committed';
关闭自动提交 set autocommit=off;
启动事务 start transaction;
有的观点是这样的 需要使用AUTOCOMMIT=1,
否则CPU就会被轻易地浪费在启动事务、提交事务上,
你会实际上损失系统的整体***能。
开启binlog带来的***能开销 ,是否需要开启。其实从的可以不用的,如果要开启
innodb_flush_log_at_trx_commit = 0或1或2 2表示事务提交才fiush log
用一块单独的盘放二进制日志,可以提升***能,也能保证数据安全
限制最大链接数 max_connections = #
是否需要开启查询缓存 query_cache_size = #
qcache_hits/(com_select+qcache_hits)
命中和写入的比率,即Qcache_hits/Qcache_inserts的值,此比值如果能大于3:1,
则表明缓存是有效的
配置合适的缓存块大小
缓存块大小计算query_cache_min_res_unit
一般用已使用的缓存空间除以缓存条数
(query_cache_size-qcache_free_memory)/Qcache_queries_in_cache
innodb_buffer_pool_size = 72G 这个参数是innodb索引和数据公用的buffer当然是越大越好
官方建议物理内存的80%
skip-name-resolve 不进行DNS反解,网络最慢一个就有DNS解析
sync_binlog=n,当每进行 n 次事务提交之后,MySQL 将进行一次 fsync 之类的磁盘同步指令来
将 binlog_cache 中的数据强制写入磁盘,为0表示由文件系统自己决定。在高并发的事务中
为0和为1的***能有5倍的差距,数据安全没保证。
slow_query_log=YES/NO 用于记录慢查日志的记录可结合explain查看语句执行
过程,找出问题,优化查询语句,或者优化表(表拆分,减少数据冗余),
建立合适的索引提高查询效率
thead_cache_size = # 缓存连接线程的个数,可以让mysql不用经常为每个连接创建线程
可查看connections和therads_created状态变量确定,增大可提升***能
table_cache = # 表缓存大小,缓存打开过的表,可结合open_tables,opened_tables的值是否调大
如果能在缓存中去肯定快点
tmp_table_size = # 临时表大小,如果超过临时表会写入磁盘。磁盘的***能都知道慢
qcache_lownmem_prunes 是一个状态变量,如果过大表示query_cache_size可能过小,
缓存经常置换
query_free_blocks 状态变量,如果过大表示缓存碎片过多,可用flush query cache进行碎片整理
3,OS
使用内存大叶提高tlb的命中率
内存分页,每个页4096字节
每个进程看到自己的内存都很大在32位上为4G 64位就是16E,是虚拟的
多个虚拟内存可以指向同一个物理内存,比如说使用epoll的nginx提升***能又环保
在内存中每个进程有一个page table 存放物理内存到虚拟内存的对应关系
由虚拟内存到物理内存的转换称为page walk,转换的结果会缓存在cpu的tlb中
下次在要转换的时候先查tlb如果hit直接还回用 x86info -c查看tlb
context switch上下文切换
每次上下文切换的时候tlb会被清空
tlb: Translation look-aside buffer
有了tlb***能大概提升15% tlb是用一种叫CAM的东西做的
CAM:content addressable memory
如果一个程序用的内存很大超过8G,如果还使用4K大小的内存页,会有很大的***能损失,
因为要从虚拟内存转换到物理内存嘛,页表大了查询很慢,而且放页表很占空间
所以使用内存大叶:huge pages 一般为4M 2M
太大也会浪费内存
要使用内存大叶必须先分配好,可以写在grub的kernel参数上,也可以调,
不过后来调可能没有那么多的内存大叶,因为大叶必须要是连续的,
如果内存使用过了很可能因为太多的内存碎片,造成内存的不连续,分不出那么多的大叶出来
用使用大叶必须程序支持,而且不能交换到磁盘
进入正题
[root@node2 ~]# sysctl -w vm.nr_hugepages=10
vm.nr_hugepages = 10
[root@node2 ~]# cat /proc/meminfo
HugePages_Total: 10
HugePages_Free: 10
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
配置使用大页内存的用户组mysql
[root@localhost ~]# sysctl -w vm.hugetlb_shm_group=306
vm.hugetlb_shm_group = 306
vim /etc/security/limits.conf
@mysql soft memlock unlimited
@mysql hard memlock unlimited
修改/vim /etc/sysctl.conf 默认就很大可以不用改
# Increase the amount of shmem allowed per segment
# This depends upon your memory, remember your
kernel.shmmax = 68719476736
# Increase total amount of shared memory.
kernel.shmall = 4294967296
large-pages 写在mysql配置文件
mysql> show global variables like 'large%';
+---------------------+---------+
| Variable_name | Value |
+---------------------+---------+
| large_files_support | ON |
| large_page_size | 2097152 |
| large_pages | ON |
使用什么文件系统,ext3就不用在用了,ext4 XFS (rw,noatime,nodiratime,barrier=0)
现在的很多文件系统会在数据提交时强制底层设备刷新cache,避免数据丢失,
称之为write barriers。但是,其实我们数据
库服务器底层存储设备要么采用RAID卡,RAID卡本身的电池可以掉电保护;
要么采用Flash卡,它也有自我保护机制,保证数据不会丢失。
所以我们可以安全的使用nobarrier挂载文件系统。设置方法如下:
对于ext3, ext4和 reiserfs文件系统可以在mount时指定barrier=0;
对于xfs可以指定nobarrier选项
noatime你懂的
IO调度:[deadline] 数据库的磁盘调度算法
echo deadline >/sys/block/sda/queue/scheduler
我们也可以直接在/etc/grub.conf的kernel行最后添加elevator=deadline来永久生效
page资源倾斜给数据库, 尽量不浪费,兼顾临时内存申请
避免NUMA架构带来的zone内存分配不均而导致的swap现象
cache大部分由InnoDB日志产生,适时清除,限制page数量
#############################
# numactl --interleave=all mysqld 关掉了NUMA特***也可加内核参数numa=off
# sysctl vm.drop_caches = 1 回收内存的
vm.swappiness=0 vm.swappiness设置为0表示尽量少swap,
100表示尽量将inactive的内存页交换出去
inactive 内存顾名思义,就是那些被应用程序映射着,但是“长时间”不用的内存
#cat /proc/meminfo | grep -i inact
#echo "vm.swappiness = 0" >>/etc/sysctl.conf
你仔细检查的话,有些服务器上会有的一个有趣的现象:你cat /proc/cpuinfo时,
会发现CPU的频率竟然跟它标称的频率不一样:
为了环保嘛,但是mysql一定要全速的,在bios改
4,SQL语句
MySQL数据库开发-石展
1,尽量不在数据库做运算
>别用腿想事,那是脑瓜子的职责
复杂运算移到程序端的cpu
尽可能简单的运用mysql
2,控制表单数据量
纯int不超过1000W
含char不超500W
合理分表,建议单库不超过300-400个表
3,保持表身段苗条
单表字段不超过20-50
4,平衡范式和冗余
适当牺牲范式,加入冗余,效率优先,提升***能
5,拒绝3B
big sql big transaction big batch(大批量)
6,用好数字字段类型
数字型vs字符型索引
更高效,查询更快,占用空间更小 如用无符号的int存储ip,而非char
7,优先使用enum或set
enum占用1字节,转为数值运算
set视节点定,最多占用8字节
8,避免使用null字段
很难进行查询优化
null列加索引需要额外空间
含null复合索引无效
举例:‘a’ char(32) default null 错
‘b’ int(10) not null 错
'c' int(10) not null default 0 对
9,少用并拆分text/blob
强制生成硬盘临时表
浪费更多空间
若要使用则拆分到单独的表
10,不在数据库里存图片
11,谨慎合理添加索引
改善查询
减慢更新
索引不是越多越好
能不加索引尽量不加
结合核心SQL优先考虑覆盖索引
举例:不要给***别加索引
12,字符字段必须建前缀索引
举例:'pinyin' varchar(100) default null comment '小区拼音',
key 'idx_pinyin' ('pinyin'(8))'
)engine=innoDB
13,不在索引列做运算
无法使用索引
导致全表扫描
14,自增列或全局ID做INNODB主键
对主键建立聚簇索引
聚簇索引:索引和数据在一块,无指针,只能有一个,因为索引排序。辅助索引指向了聚簇索引并不指向数据
二级索引存储主键值
主键不应更新修改
按自增顺序插入值
忌用字符串做主键
聚簇索引分裂
推荐用独立于业务的AUTO_INCREMENT列或全局ID生成器所代理主键
若不指定主键,InnoDB会用唯一的非空值索引代替
15,尽量不用外键
外键可节省开发量
有额外的开销
逐行操作
可到达其他表,意味着锁
高并发时容易死锁
由程序保证约束
16,SQL语句尽可能简单
传统设计思想,BUT MySQL NOT
一条SQL只能在一个CPU运算
5000+QPS的高并发中,1秒大SQL意味着?
可能一条大SQL就把整个数据库堵死
拒绝大SQL,拆分多条简单SQL
简单SQL缓存命中率更高
减少所表时间,特别是MyISAM
用上多CPU
17,保持事务短小
事务/连接使用原则:即开即关,用完即关
与事务无关的操作放到事务外面,减少锁资源的占用
不破坏一致***的前提下,使用多个短小的事务代替长事务
18,尽可能避免使用SP/TRIG/PUNC
尽可能少用存储过程
经可能少用触发器
减少使用MYSQL函数对结果进行处理
用客户端程序负责
19,尽量不用SELECT *
更多消耗CPU,内存,IO,网络带宽
先向数据库请求所有列,然后丢掉不需要的列?
20,改写OR为IN()
OR效率:O(n)
IN效率:O(Log n)
当n很大时,OR会慢很多,注意控制IN的个数建议小于200
举例:select * from opp where phone = '123456' or phone = '42242233'
-->
select * from opp where phone in ('123456','42242233')
21,改写OR为UNION
22,避免负向查询和%前缀模糊查询
B+Tree
使用不了索引
导致全表扫描
举例:mysql> select * from post where title like '北京%';
293 rows in set (0.01 sec)
mysql> select * from post where title like '%北京%';
572 rows in set (3.27 sec)
23,减少count(*)
count(*)开销大
24,limit高效分页
25,用UNION ALL 而非UNION
UNION有去重开销
26,分解联结保证高并发
高并发的DB不建议进行两个表以上的JOIN
27,GROUP BY去除排序
GROUP BY实现
分组,自动排序
28,同数据类型的列值比较
原则:数字对数字,字符对字符
数值列于字符类型比较
同时转换为双精度
进行比对
字符列与数值类型比较
字符列整列转为数值
不会使用索引
29,Load data
Load data 比insert快20倍
尽量不用INSERT ...SELECT
延迟,同步出错
30,Konw Every SQL
EXPLAIN
show profile
mysqllsla
show slow log
show query_response_time(percona)
show processlist
msyqldumpslow
show profile
31,隔离线上线下
32,禁止未经DBA确认的子查询
例子:mysql> insert into table1 (select * from table2);可能会导致复制异常
5,索引的构建
mysql优化的重点,使用什么索引在哪使用索引太关键了
一条好的索引可以让***能提升n倍,看情况,索引的不好***能是会下降的
因为更新字段还得更新索引,不要数据1G,索引9G。这样就不好了
explain
EXPLAIN列的解释:
mysql> explain select user,host,password from user;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | user | ALL | NULL | NULL | NULL | NULL | 5 | |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
下面的在网上贴的
table:显示这一行的数据是关于哪张表的
type:这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL
possible_keys:显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句
key: 实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句中使用USE INDEX(indexname)来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MYSQL忽略索引
key_len:使用的索引的长度。在不损失精确***的情况下,长度越短越好
ref:显示索引的哪一列被使用了,如果可能的话,是一个常数
rows:MYSQL认为必须检查的用来返回请求数据的行数
Extra:关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢
extra列返回的描述的意义
Distinct:一旦MYSQL找到了与行相联合匹配的行,就不再搜索了
Not exists: MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了
Range checked for each Record(index map:#):没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一
Using filesort: 看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行
Using index: 列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候
Using temporary 看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上
Where used 使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题不同连接类型的解释(按照效率高低的顺序排序)
system 表只有一行:system表。这是const连接类型的特殊情况
const:表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为MYSQL先读这个值然后把它当做常数来对待
eq_ref:在连接中,MYSQL在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用
ref:这个连接类型只***诓檠褂昧瞬皇俏┮换蛑骷募蛘呤钦庑├嘈偷牟糠郑ū热纾米钭蟊咔白海┦狈⑸6杂谥暗谋淼拿恳桓鲂辛希考锹级冀颖碇卸脸觥U飧隼嘈脱现匾览涤诟菟饕ヅ涞募锹级嗌佟缴僭胶
range:这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况
index: 这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更好,因为索引一般小于表数据)
ALL:这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免
1, TPS: 每秒事务量,通过以下方式来得到客户端应用程序所请求的 TPS 值,
TPS = (Com_commit + Com_rollback) / Seconds
通过如下命令可以获得相应的值
Com_commit = /bin/mysqladmin extended-status --relative --sleep=1|grep -w Com_commit
Com_rollback = /bin/mysqladmin extended-status --relative --sleep=1|grep -w Com_rollback
TPS = $Com_commit + $Com_rollback
2, QPS: 每秒Query 量,这里的QPS 是指MySQL Server 每秒执行的Query总量,通过Questions 状态值每秒内的变化量来近似表示,所有,insert delete update都算
QPS = /bin/mysqladmin extended-status --relative --sleep=1|grep -w Questions
PS: 仿照上面的方法还可以得到,mysql server 的每秒 查询,更新量等等,如:
每秒SLECECT量 = /bin/mysqladmin extended-status --relative --sleep=1|grep -w Com_select