本文从存储引擎出发,简要介绍索引目的,原理及索引底层实现的数据结构,索引原则,索引优化等,内容虽然不够深入,但是也足以应对日常的面试
存储引擎
事务、读锁(共享锁)、写锁(排它锁)
行锁、表锁
行锁 只对指定的记录加锁,其他进程可对同一表中的其他记录进行操作
表锁 对整张表进行加锁,写的话 阻塞,读 不影响
表锁 速度快冲突多,行锁速度慢 冲突少
存储引擎:官方存储引擎和第三方存储引擎
InnoDB 支持事务、支持行锁、支持非锁定读、支持外键,支持最大64TB的数据量(第三方存储引擎)
MyISAM 不支持事务,不支持行锁,支持表锁,支持全文检索,支持最大256TB的数据量。最大的缺陷是崩溃后无法安全恢复
Memory 数据放在内存中,速度快,但因其支持表锁,所以并发性能差。重启或崩溃后数据全部丢失,只适合存临时表
Archive 只支持INSERT和SELECT操作,支持行锁,但本身并不是事务安全的存储引擎,优点:压缩比1:10,适合存储历史数据、日志信息数据等
索引
目的:提高查询效率
原理:通过不断地缩小想要获得数据的范围来筛选出最终想要的数据
索引结构:b+树
磁盘块两个数据项,三个指针(分别指向两个数据项分割的三个区间)
真实的数据在叶子节点
b+树的查找过程
二分查找
3层的b+树,可以表示上百万的数据
b+树的性质
1.索引字段要尽量小
2.索引最左匹配特性:b+树的数据项是复合的数据结构
mysql为什么要使用b+树作为索引的数据结构
因为B+树所有数据都在叶子结点,不用跨层,同时由于有链表结构,只需要找到首尾,通过链表就能把所有数据取出来。
hash比B+树快,为啥mysql还用B+树来存索引呢?
这和业务场景有关,如果只选一个数据,那确实是hash更快。但是数据库中经常会选择多条,这时候B+树索引更快。
参考
mysql存储引擎的实现
MyISAM存储引擎
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址
MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。
MyISAM的索引方式称为 非聚集索引
InnoDB索引
InnoDB的数据文件本身就是索引文件
第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址
如何建立合适的索引
建立索引的原则
最左匹配原则
索引匹配的最左原则具体是说,假如索引列分别为A,B,C,顺序也是A,B,C:
那么查询的时候,如果查询【A】【A,B】 【A,B,C】,那么可以通过索引查询
- 如果查询的时候,采用【A,C】,那么C这个虽然是索引,但是由于中间缺失了B,因此C这个索引是用不到的,只能用到A索引
- 如果查询的时候,采用【B】 【B,C】 【C】,由于没有用到第一列索引,不是最左前缀,那么后面的索引也是用不到了
- 如果查询的时候,采用范围查询,并且是最左前缀,也就是第一列索引,那么可以用到索引,但是范围后面的列无法用到索引
建立索引的常用技巧
最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可,当然要考虑原有数据和线上使用情况
MySQL优化
配置优化
基本配置
- innodb_buffer_pool_size:缓冲池是数据和索引缓存的地方,典型的值是5-6GB(8GB内存),20-25GB(32GB内存),100-120GB(128GB内存)
- innodb_log_file_size:redo日志的大小,redo日志的总尺寸被限定在4GB(默认可以有2个log文件)
- max_connections:默认151个,可以设置更大,如300;不能设置的过大,如1000,当服务器运行1000个或更高的活动事务时会变的没有响应。在应用程序中使用数据库连接池
InnoDB配置
- innodb_file_per_table:InnoDB是否需要将所有表的数据和索引存放在共享表空间里
- innodb_flush_log_at_trx_commit:默认值为1,表示InnoDB完全支持ACID特性
- innodb_flush_method:数据和日志写入硬盘的方式,O_DIRECT、fdatasync(默认值)
- innodb_log_buffer_size:尚未执行的事务分配的缓存;看看Innodb_log_waits状态变量,如果它不是0,增加innodb_log_buffer_size。
其他配置
- query_cache_size:设置query_cache_size = 0(现在MySQL 5.6的默认值)并利用其他方法加速查询
- log_bin:开启二进制日志
- skip_name_resolve:建议在启动服务器时关闭skip_name_resolve选项而不进行DNS查找。唯一的局限是之后GRANT语句中只能使用IP地址了,因此在添加这项设置到一个已有系统中必须格外小心。
SQL调优
有慢查询的SQL,系统或者server可以开启慢查询日志,尤其是线上系统,一般都会开启慢查询日志,如果有慢查询,可以通过日志来过滤
慢查询优化基本步骤
- 先运行看看是否真的很慢,注意设置SQL_NO_CACHE
- where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高
- explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
- order by limit 形式的sql语句让排序的表优先查
- 了解业务方使用场景
6 . 加索引时参照建索引的几大原则 - 观察结果,不符合预期继续从0分析
常用调优手段
执行计划explain
通过profiling命令得到更准确的SQL执行消耗系统资源的信息
优化思路:避免全表扫描 & 注重SQL语句写法 & 索引
mysql支持的最大连接数:默认是100,最大是16384