MySQL
-
影响mysql性能的因素
商业需求影响性能,一些需要实时处理数据的需求(但是这个需求只有极少数一部分的用户需要),时刻数据同步肯定影响mysql的性能
-
系统架构及实现:因为mysql是和硬盘交互的,这个层面不是很好优化,在软件架构系统优化的性价比要高很多,比如尽量少让请求到mysql,mysql是硬盘级别的,而一些缓存如redis是内存级别的,速度肯定是快很多的
不适合在数据库中存放的数据:二级制多媒体数据,流水队列数据(因为流水量太大,源源不断的过来),超大文本数据
是否合理的利用了应用层Cashe机制
过度依赖数据库sql语句的功能造成数据库操作效率低下(比如有的比较长的sql语句可能还没有拆分成多条sql语句执行的快)
表设计对系统性能的影响:比如金融系统(冷热数据分离,不变的数据可以放到缓存里面),登录信息和账户信息就不要设计在一张表里面,因为登录信息是很少变动的,也是不需要事务保证安全的,但是账户信息是经常变动的,而且要事务保证安全,因为钱不能出差错,所以,如果在一张表里面的话,那么每次用户登录都需要走下事务,导致性能变差
硬件对系统性能的影响
query语句对系统性能的影响
-
优化
建立索引,生活中的字典就有拼音索引,通过拼音找到我们需要的字,后面跟的页码就是我们的索引,比如我现在要从一堆数字(1,3,5,7,9,10)里面去找到7这个数字,如果没有索引没有主键,那么就只能整个遍历,但是我加了索引,把它变成了二叉树,小的在左边,大的在右边,那么查询的时候就可以先判断一下范围再查询,查询的次数最多就是树的高度,时间复杂度是log(n),当数据量很大的时候,速度会比不做索引的快很多,但是它的代价就是把数据变复杂了占用了更多的存储空间
索引类型:B+TREE;Hash;FullText;R-Tree,innoDB中只有B+TREE;Hash这两个,hash适用于冲突不大的情况,如果冲突很大的话,那些冲突的数据就会形成链表,性能就会变差
mysqlInnoDB存储引擎是支持hash索引的,但是hash索引的创建由innoDB存储引擎自动优化创建,我们干预不了,我们自己创建只有选择B+TREE
B tree 一个节点可以放多个数据,限制是自己设置的,比如我的上限不超过3个,那么我一个节点放两个数据是没有问题的,但是当该节点加入第三个数据时,就会分裂,而B+Tree是B Tree的变种,数据结构是一样的,区别是B tree的非叶子节点也和叶子节点一样除了存放元素之外,还存储数据,而B+tree,只有叶子结点才会存储数据,非叶子结点只存放元素和导航指针(指针占用的空间是非常小的),并且所有的叶子节点都有一个链指针
系统从磁盘读取数据到内存时,是以磁盘块(block)为基本单位,位于同一个磁盘块的数据会被一次性读取出来,并不是需要什么就读取什么,innodb引擎中有页的概念,页是innodb和磁盘交互的最小单位,而我们的系统磁盘块(block)的空间往往是达不到16kb的,所以每次innodb从磁盘拿数据时就需要一次性拿好几个磁盘块(block)的数据来凑齐一页的大小(16kb),树的每一个节点就是一次io操作,每次操作只能拿到16kb的数据,所以在16kb固定的情况下,单个数据的大小越小,那么节点存储的数据就越多,如果节点能存放的数据越多,那么树的高度就越矮,树越矮,那么查询的速度就越快(因为树越矮,io操作就越少,性能消耗就越少)
磁盘IO操作应该是树高减1,因为不论走树的左边还是右边,始终都是要过根节点的,所以mysql会将根节点缓存起来,因为不确定下一次是走左边还是右边,所以只会缓存根节点,不会缓存其他的节点,所以IO操作的次数是树高减1
-
索引
主键就是主索引
如果没有主键,那么mysql会选择非空且唯一的一列来作为主索引
如果既没有主键,也没有非空且唯一的列,那么mysql会建立一个隐藏列(有个自增的id)来作为主索引
除了主键以外,自己添加的索引就是辅助索引
创建索引的命名规范(名字全部小写,可以简写,不要超过太长):1.普通索引(idx_表名索引字段或直接使用idx索引字段名),示例:(alter table 表名 add index 索引名(索引列名),如:alert table order add index id_order_sno(sno)),2.唯一索引(uniq_表名字段名,或者直接uniq字段名),示例:alter table product add unique uniq_sid(sid),3.主键索引alter table product add primary key 索引名字(sid),4.组合索引创建,alter table student add index idx_age_name,height(age,name,height)
-
MyISAM
主索引都在myi文件中,索引中存储的都是数据的地址,拿到地址去myd中去查找数据
辅助索引也是在myi文件中,同上也是存储的数据地址,去myd中拿数据,区别就是,主索引是唯一且不为空的,而辅助索引是没有这个要求的(辅助索引是可以重复的)
-
innodb
主索引与数据是在一起的,找到了主索引也就找到了数据,数据存放在节点的data域中
辅助索引的叶子节点存储的是主索引的值,所以要查看完整的数据,还得通过这个值找找到主索引
-
区别:
如果是通过主索引来找数据那么innodb是比myISAM快的,因为innodb的的数据是放在叶子节点的,和主索引在一起,找到了索引也就找到了数据,但是MyISAM的叶子节点存放的是数据的地址,还要通过地址去找数据,索引对于通过主索引来查找数据而言innodb要快点
但是如果是通过辅助索引来找数据的话,那么就是MyISAM要快点,因为innodb中辅助索引的叶子节点存放的是主键索引值,还要再走一遍主键索引才能找到数据,而MyISAM辅助索引和主索引是一样的逻辑都是叶子节点存放数据地址,再根据地址查找数据(不同点是辅助MyISAM的索引可以重复)
什么是聚簇索引:索引与数据放在一起的(比如innodb的主键索引)
什么是非聚簇索引:索引与数据不在一起的(innodb的辅助索引和MySIAM的索引)
-
索引的优缺点
索引可以提高数据的检索效率
如果排序的列是索引列,可以大大降低排序的成本
在分组操作中如果分组的条件是索引,那么也会提高效率
建立索引需要占用一定的空间,维护索引也需要成本
-
如何创建索引
索引的目的是帮助我们提高检索速度,不出现在where子句的字段不要加索引,因为没用上,我们的新增,删除,修改,对应的索引树是会变化的,是需要额外维护的,而我们加索引的目的是为了提高检索速度的,你都没出现在where子句中,就证明都没有查你,那这时建立索引不仅没用,还会增加额外的开销
频繁作为查询条件的字段应该加上索引
更新非常频繁的字段不适合创建索引,因为字段更新势必会影响到索引,维护索引需要额外的开销
唯一性太差的字段也不适合创建索引,即使它是频繁作为查询条件的字段,比如男和女的字段,就两种情况,如果我有1千万数据,最多也就只能区分一半,唯一性太差,所以不适合建立索引
单值索引即一列作为索引,组合索引即多列作为索引,组合索引从左到右是有要求的,比如我们有sql语句select * from student where age=18 and height = 180 and weight = 70 创建了组合索引(age,height,weight),情况一:如果三个值都有,那么索引顺序和where子句后面索引列的顺序没有影响,情况二:如果where子句中没有age条件,那么索引就不生效了,因为索引生效是从age开始的,情况三:如果where子句中没有height条件,那么就只有age索引能生效,在height的时候就被阻断了,所以weight也不生效了,情况四:如果where子句中只有heigth和age,且顺序是height在前age在后,那么age和height索引是可以生效的,因为height和age是相邻的,mysql会自动帮我们优化调整顺序(即相邻的顺序不一样没有影响),但是如果没有age条件那么后面的是不生效的,因为age是在第一个位置,没有他索引就被阻断了,所以建立组合索引的时候要把出现频次最高的索引列放在最左边(最左前缀原则)
为什么索引最好不为空呢?因为不为空的索引就只有一个索引结构,而可以为空的列设置索引,那么索引就要开辟两块空间,一块存储非空的,一块存储空的
-
索引选择性与索引前缀
索引选择性:是指索引列中不同列值的数目与记录数的比,比如我现在有2000条数据,索引列有1500个不同的值,那么索引的选择性就为1500/2000=0.75
索引的选择性越接近1,那么这个索引的效率就越高,当索引选择性越接近于0,就越没必要建立索引
当数据量小的时候是没必要建立索引的,因为全表扫描花的时间就不多,建立索引提升的速度意义不大,一般以2000条数据为分界线,多余2000的才考虑
建立索引的时候也要考虑索引字段不要太长,10个长度的索引肯定是比30个长度的索引要匹配的快的(索引字段的长度就是我们建立表字段给的长度,比如name给了25,age给了16等)
-
索引失效情况:
在复合索引中,最左索引条件没有满足
在复合索引中,使用or的情况,其中一个条件没有匹配到索引,会导致索引失效走全表扫描
当使用like进行模糊查询时,后缀查询会导致索引失效,前缀查询不会
当使用复合索引时有范围查询,那么范围条件后的索引就失效了(特列:between也是查范围,但是它实际上是相当于in,是精确查询,所以不会导致索引失效)
在查询表达式中使用了函数表达式(比如:left(name,4)会导致失效
还有在查询表达式中使用了算术运算,如select * from emp where no - 1 = 21会使索引失效),但是select * from emp where no = 22这样是不会导致索引失效的,所以不要给有索引的列(no)做任何运算
-
innodb主键选择与插入优化
- 尽量选择有自增的字段,因为数据最终是存入磁盘的,如果是自增的,那么我们在磁盘上申请和操作的空间就是连续的,如果一会大一会儿小,那么我的数据一会要插左边一会要插在右边,会导致需要申请很多的空间,空间没用完又会产生许多的碎片,影响性能
-
join优化原则
因为join是将两张表连在,如果A表有100条数据,B表也有100条数据,那么数据比较的次数就是100100=1W次(产生1w的笛卡尔积),如果AJonB时on后面的条件本身就有索引,那么比较的次数等于A的数据量100树高,比如树高为3,那么比较的次数就是300次,找到索引就可以快速找到数据
小结果集驱动大结果集==》目的是减少内层访表次数(mysql内部帮我们做了优化),即把结果集小的表作为外表,就是左边的
可以使用STRAIGHT_JON,指定驱动表和被驱动表
为匹配的条件增加索引,A为驱动表,B为被驱动表,关联条件(on)为A.age=B.age,那么在B.age字段添加索引,可以减少匹配次数(就是上述第一条的例子)
增大join_buffer_size的大小(join_buffer_size的作用是将外表的数据缓存起来,然后一次性去访问内表),这个缓存一次性缓存外表的数据越多,那么外表访问内表的次数就越少,默认的大小是256k,可以使用show variables like 'join_%'查看,可以在mysql的配置文件my.ini中配置缓存区大小:在[mysqld] 这个节点下配置:join_buffer_size = 8M
减少不必要的字段查询,比如写select *(单条记录大小为1M),select 需要字段(单条记录大小为0.1M),那么在join buffer size固定的情况下,单条记录越小,能容纳的数据就越多,减少对内层表的访问
-
order by优化
单路排序的性能比双路排序的性能高,因为双路排序要经历两次读,第一次是顺序的,第二次是乱序的,
单路排序直接把需要的数据放到内存,然后从内存里面读数据,所以比双路排序要快
若果sql语句需要排序,根据你select返回字段的总长度是否大于max_length_for_sort_data来判断,如果大于这个值走双路,小于走单路,排序时<固定长度的排序列,需要返回的列>一起组成元组,放入sort buffer中
单路排序的元组比双路排序的要长,导致它需要多次向临时文件写入内容,增加io操作,当需要返回的列的总长度很长时尤其明显
-
MySQL根据max_length_for_sort_data变量来确定使用哪种算法,默认值1024字节,如果需要返回的列的总长度大max_length_for_sort_data,使用双路,否则使用单路。
-
优化
加大max_length_for_sort_data参数
去掉不必要的返回字段(比如不要用select*)
增大sort——buffer——size参数
-
如果是多个表联合排序时,排序的字段是被驱动表,那么肯定就会出现临时表和之后排序,临时表(临时表包含两个表的字段,非常容易走双路排序,产生的临时文件也比较多),可以使用STRAIGHT_JOIN调整驱动表和被驱动表的关系
-
优化sql
开启慢查询日志,超过2秒的sql会记录下来,通过explain命令和profile查看sql执行计划和资源消耗,主要查看sql是否用上索引,索引是否由于某些原因失效,是否有出现临时表
永远用小结果集驱动大结果集
在索引中完成排序
-
使用最小的columns
特别是需要使用column排序的时候;
减少网络传输数据量;
MYSQL排序原理,是把所有的column(列)数据全部取出,在排序缓存区排序,再返回结果;如果column数据量大,排序区容量不够的时候,就会使用先column排序,再取数据,再返回的多次请求方式;
-
mysql主从复制
在主节点的配置文件中配置bin-log文件,目的是存储mysql主节点的DML(增删改)操作
重启mysql服务,启动线程把DML,DDL操作记录到bin-log文件中
在从节点的配置文件中配置relay-log文件,存储主节点的DML,DDL操作
主从复制采取的是被动注册的方式,由从节点给主节点发送要求同步的命令,启动从节点模式,发送命令时需要知道哪些信息呢?1.主节点的地址端口,2.主节点的账号密码,3.需要同步主节点中的哪个文件,4.以及3中所说额文件的哪个位置开始同步
主节点收到从节点的同步请求后,会启动线程把bin-log的文件内容以二进制的方式通过网络传输到从节点
从节点会启动一个线程来接收主节点发来的数据存入到relay-log文件中
从节点再启动一个线程把relay-log中内容在数据库中重做一遍(重做的效率是很快的,比我们自己写的普通sql要快很多,因为普通sql的执行要权限验证,解析优化等,而我们是从主节点bin-log中复制来的,是被主节点执行过的,所以在从节点重做时是无须那些额外操作的)
因为主从复制需要走网络传输,所以可能存在网络波动的情况,是无法保证一定准确的,此时我们可以把重要数据的读写都在主节点执行,而一些不重要的信息(比如日志等)到从节点进行
Mycat
-
原理:
Mycat实现了和MySql一样的sql标准,应用程序连接MyCat和直接连接数据是一样的,是感觉不到差别的,在mycat中要配置逻辑表和真实表的关系,因为mycat是用java写的,所以在安装mycat之前必须具备jdk的环境
mycat里面有一个逻辑库和逻辑表的概念,我们连接其实是连接的mycat的逻辑库,再由逻辑库去找到我们真实的库,逻辑库的名字可以和真实库的名字不一样
应用程序发送一个去t_order表中查询的语句,其实操作的是逻辑表,再由逻辑表进行sql语句进行分发,如果是写操作就给主节点执行,如果是读操作就给从节点执行,当主节点有操作执行时会同步到从节点,在mycat中配置了主从节点,读写分离后,正常情况下读是不会去主节点去读,但是可以通过mycat的注解来强制要求去主节点读
-
配置
server.xml:mycat相关的配置:端口,线程池,账号密码,黑名单、白名单,表权限操作等,mycat默认的端口是8066,用户名密码root/123456
rule.xml:分片规则
schema.xml:配置逻辑库逻辑表和真实库真实表的映射关系
-
分库分表
当数据超过500w时,数据库的性能会明显下降,所以才有了分库分表的需求
垂直拆分,比如以前有商品,订单,支付操作的都是同一个库,现在它们各自有自己对应的数据库,这就是垂直拆分
水平拆分,比如以前订单系统只有一个数据库,现在水平横向扩展加了两个库,那么订单系统就有了三个数据库
-
如何实现分片
在mycat中要配置分片字段和规则,比如orderNo作为分片字段,分片规则为:求模取余(比如我们现在分了三片,即分了3个数据库,那么就对orderNo%3求余,得到的余数是多少,交把sql语句交给哪个数据库节点执行)
在执行sql语句的时候是insert的时候就是按照1中所说的规则分配,只有分配到的节点执行sql,其余的节点mycat是不会给它们发送sql语句的
如果执行的是select语句,如果语句中有orderNo条件,那么也会按照1中的规则去分配
如果select语句中没有orderNo条件,那么mycat会给每个节点都发送sql去执行,然后在mycat中进行结果的聚合处理再返回出去
同理如果有order by时,mycat也是给各节点发送sql执行,最后在mycat中进行结果的聚合和处理