mysql集群主从库读写分离

高负载高并发环境下,数据业务层、数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器负载,如此多的数据库连接操作,数据库必然会崩溃,数据库如果宕机的话,后果更是不堪设想。这时候,我们会考虑如何减少数据库的连接,一方面采用优秀的代码框架,进行代码的优化,采用优秀的数据缓存技术如:redis,如果资金丰厚的话,必然会想到架设mysql服务集群,来分担主数据库的压力。今天总结一下利用MySQL主从配置,实现读写分离,减轻数据库压力。

具体搭建:https://v3u.cn/a_id_85

mysql主从同步的原理很简单,从库生成两个线程,一个I/O线程,一个SQL线程;i/o线程去请求主库 的binlog(二进制日志),并将得到的binlog日志写到relay log(中继日志) 文件中;主库会生成一个 log dump 线程,用来给从库 i/o线程传binlog;

SQL 线程,会读取relay log文件中的日志,并解析成具体操作,来实现主从的操作一致,而最终数据一致。

关于binlog日志

binlog是二进制日志文件,用于记录mysql的数据更新或者潜在更新(比如DELETE语句执行删除而实际并没有符合条件的数据)

binlog索引文件mysql-bin.index。如官方文档中所写,binlog格式如下:

binlog文件以一个值为0Xfe62696e的魔数开头,这个魔数对应0xfe ‘b’’i’’n’。

binlog由一系列的binlog event构成。每个binlog event包含header和data两部分。

header部分提供的是event的公共的类型信息,包括event的创建时间,服务器等等。

data部分提供的是针对该event的具体信息,如具体数据的修改。

Mysql - 图1

主从同步延迟问题:

架构方面

1.业务的持久化层的实现采用分库架构,mysql服务可平行扩展,分散压力。

2.单个库读写分离,一主多从,主写从读,分散压力。这样从库压力比主库高,保护主库。

3.服务的基础架构在业务和mysql之间加入memcache或者Redis的cache层。降低mysql的读压力。

4.不同业务的mysql物理上放在不同机器,分散压力。

5.使用比主库更好的硬件设备作为slave

总结,mysql压力小,延迟自然会变小。

硬件方面

1.采用好服务器,比如4u比2u性能明显好,2u比1u性能明显好。

2.存储用ssd或者盘阵或者san,提升随机写的性能。

3.主从间保证处在同一个交换机下面,并且是万兆环境。

总结,硬件强劲,延迟自然会变小。一句话,缩小延迟的解决方案就是花钱和花时间。

mysql主从同步加速

1、sync_binlog在slave端设置为0

2、-logs-slave-updates 从服务器从主服务器接收到的更新不记入它的二进制日志。

3、直接禁用slave端的binlog

4、slave端,如果使用的存储引擎是innodb,innodb_flush_log_at_trx_commit = 2

悲观锁和乐观锁

乐观锁

乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。

悲观锁

每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁

更新丢失:最后的更新覆盖了其他事务之前的更新,而事务之间并不知道,发生更新丢失。更新丢失,可以完全避免,应用对访问的数据加锁即可。

脏读:(针对未提交的数据)一个事务在更新一条记录,未提交前,第二个事务读到了第一个事务更新后的记录,那么第二个事务就读到了脏数据,会产生对第一个未提交,解决方案加锁,或者调整mysql事务隔离级别,数据库的事务隔离越严格,并发负作用越小,代价越高

触发器

触发程序是与表有关的命名数据库对象,当该表出现特定事件时,将激活该对象

监听:记录的增加、修改、删除。

-- 创建触发器

CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW trigger_stmt 参数: trigger_time是触发程序的动作时间。它可以是 before 或 after,以指明触发程序是在激活它的语句之前或之后触发。 trigger_event指明了激活触发程序的语句的类型 INSERT:将新行插入表时激活触发程序 UPDATE:更改某一行时激活触发程序 DELETE:从表中删除某一行时激活触发程序 tbl_name:监听的表,必须是永久性的表,不能将触发程序与TEMPORARY表或视图关联起来。 trigger_stmt:当触发程序激活时执行的语句。执行多个语句,可使用BEGIN…END复合语句结构

-- 删除

DROP TRIGGER [schema_name.]trigger_name

可以使用old和new代替旧的和新的数据 更新操作,更新前是old,更新后是new. 删除操作,只有old. 增加操作,只有new.

-- 注意

1. 对于具有相同触发程序动作时间和事件的给定表,不能有两个触发程序。

事务

事务是指逻辑上的一组操作,组成这组操作的各个单元,要不全成功要不全失败。

- 支持连续SQL的集体成功或集体撤销。
- 事务是数据库在数据晚自习方面的一个功能。
- 需要利用 InnoDB 或 BDB 存储引擎,对自动提交的特性支持完成。
- InnoDB被称为事务安全型引擎。

-- 事务开启 START TRANSACTION; 或者 BEGIN; 开启事务后,所有被执行的SQL语句均被认作当前事务内的SQL语句。 -- 事务提交 COMMIT; -- 事务回滚 ROLLBACK; 如果部分操作发生问题,映射到事务开启前。

-- 事务的特性

1. 原子性(Atomicity)
    事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
2. 一致性(Consistency)
    事务前后数据的完整性必须保持一致。
    - 事务开始和结束时,外部数据一致
    - 在整个事务过程中,操作是连续的
3. 隔离性(Isolation)
    多个用户并发访问数据库时,一个用户的事务不能被其它用户的事物所干扰,多个并发事务之间的数据要相互隔离。
4. 持久性(Durability)
    一个事务一旦被提交,它对数据库中的数据改变就是永久性的。

-- 事务的实现

1. 要求是事务支持的表类型
2. 执行一组相关的操作前开启事务
3. 整组操作完成后,都成功,则提交;如果存在失败,选择回滚,则会回到事务开始的备份点。

-- 事务的原理 利用InnoDB的自动提交(autocommit)特性完成。 普通的MySQL执行语句后,当前的数据提交操作均可被其他客户端可见。 而事务是暂时关闭”自动提交”机制,需要commit提交持久化数据操作。

-- 注意

1. 数据定义语言(DDL)语句不能被回滚,比如创建或取消数据库的语句,和创建、取消或更改表或存储的子程序的语句。
2. 事务不能被嵌套

-- 保存点 SAVEPOINT 保存点名称 -- 设置一个事务保存点 ROLLBACK TO SAVEPOINT 保存点名称 -- 回滚到保存点 RELEASE SAVEPOINT 保存点名称 -- 删除保存点

-- InnoDB自动提交特性设置 SET autocommit = 0|1; 0表示关闭自动提交,1表示开启自动提交。

- 如果关闭了,那普通操作的结果对其他客户端也不可见,需要commit提交后才能持久化数据操作。
- 也可以关闭自动提交来开启事务。但与START TRANSACTION不同的是,
    SET autocommit是永久改变服务器的设置,直到下次再次修改该设置。(针对当前连接)
    而START TRANSACTION记录开启前的状态,而一旦事务提交或回滚后就需要再次开启事务。(针对当前事务)

并发事务带来的几个问题:更新丢失,脏读,不可重复读,幻读。

事务隔离级别:未提交读(Read uncommitted),已提交读(Read committed),可重复读(Repeatable read),可序列化(Serializable)

Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

Repeatable Read(可重读)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的”幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

Serializable(可串行化) 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。

3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

可重复读(Repeatable read)是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的”幻影” 行。

锁表

表锁定只用于防止其它客户端进行不正当地读取和写入

MyISAM 支持表锁,InnoDB 支持行锁

-- 锁定 LOCK TABLES tbl_name [AS alias]

-- 解锁 UNLOCK TABLES

联合索引&最左原则

具体实践和测试:https://v3u.cn/a_id_91

联合索引的好处:

覆盖索引,这一点是最重要的,重所周知非主键索引会先查到主键索引的值再从主键索引上拿到想要的值,这样多一次查询索引下推。但是覆盖索引可以直接在非主键索引上拿到相应的值,减少一次查询。

在一张大表中如果有 (a,b,c)联合索引就等于同时加上了 (a) (ab) (abc) 三个索引减少了存储上的一部分的开销和操作开销

梯度漏斗,比如 select *from t where a = 1 and b = 2 and c = 3; 就等于在满足 a = 1 的一部分数据中过滤掉b = 2 的 再从 a = 1 and b = 2 过滤掉 c = 3 的,越多查询越高效。

最左原则:最左优先,在检索数据时从联合索引的最左边开始匹配,类似于给(a,b,c)这三个字段加上联合索引就等于同时加上了 (a) (ab) (abc) 这三种组合的查询优化

底层数据结构:b+tree

BTREE 每个节点都是一个二元数组: [key, data],所有节点都可以存储数据。key为索引key,data为除key之外的数据。

查找算法:首先从根节点进行折半查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或未找到节点返回空指针

B+Tree有以下不同点:非叶子节点不存储data,只存储索引key;只有叶子节点才存储data,而Mysql中B+Tree:在经典B+Tree的基础上进行了优化,增加了顺序访问指针。在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree。这样就提高了区间访问性能:请见下图,如果要查询key为从18到49的所有数据记录,当找到18后,只需顺着节点和指针顺序遍历就可

总结:B+Tree 在 B-Tree 的基础上有两点变化:

数据是存在叶子节点中的;

数据节点之间是有指针指向的。

Mysql - 图2

连接池问题

连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

有点类似单例模式的概念

django最新版本已经包含了连接池,通过修改配置控制,官方文档:https://docs.djangoproject.com/en/1.9/ref/databases/

关于ip存储问题

1、为什么要转换?

  IP转换成整型存储可以减少索引IP字符串类型消耗的资源量。

2、转换的原理是什么?

  IP转换为整形:形如123.125.0.236 可以看成用点分割的四个小段的整数,每个小段的范围为0~255,那么就可以将每个数字转换为二进制的数字。

 第一段:123转换为二进制   01111011

 第二段:125转换为二进制   01111101

 第三段: 0   转换为二进制   00000000

 第四段: 236转换为二进制  11101100

 然后将四段拼接起来成了长的二进制数:01111011011111010000000011101100

 并将这个厂二进制数转换为整形的数字:2071789804

 整形转换为IP的思路刚好与上述相反,将十进制数转换为二进制之后,从后向前每8个数取一个段落,转换为十进制数字



def convert_int(a):
    #  转换为4个段的列表
    a_list = a.split('.',4)
    # a_list.reverse()
    a_str = ''
    for i in a_list:
        a_tem = bin(int(i))[2:] # 字符串
        if len( a_tem) != 8:
        # 在前面加 0
            a_str += '0'*(8-len(a_tem))+a_tem
        else:
            a_str += a_tem
    # print a_str
    return int(a_str,2)

def convert_ip(b):
    #先转换为二进制
    b_tem = bin(int(b))[2:]
    b_str = ''
    # 将所有的 0 补齐
    if len(b_tem) != 32:
       b_str += '0' * (32 - len(b_tem)) + b_tem
    #切片处理
    b1 = b_str[0:8]
    b2 = b_str[8:16]
    b3 = b_str[16:24]
    b4 = b_str[24:]
    b_out= str(int(b1,2))+'.'+str(int(b2 ,2))+'.'+str(int(b3,2))+'.'+str(int(b4,2))
    return b_out

mysql 优化

为什么要优化

系统的吞吐量瓶颈往往出现在数据库的访问速度上 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢 数据是存放在磁盘上的,读写速度无法和内存相比

如何优化

设计数据库时:数据库表、字段的设计,存储引擎 利用好MySQL自身提供的功能,如索引等 横向扩展:MySQL集群、负载均衡、读写分离 SQL语句的优化(收效甚微)

字段设计

字段类型的选择,设计规范,范式,常见设计案例 原则:尽量使用整型表示字符串 存储IP INET_ATON(str),address to number

INET_NTOA(number),number to address

MySQL内部的枚举类型(单选)和集合(多选)类型 但是因为维护成本较高因此不常使用,使用关联表的方式来替代enum

原则:定长和非定长数据类型的选择 decimal不会损失精度,存储空间会随数据的增大而增大。double占用固定空间,较大数的存储会损失精度。非定长的还有varchar、text 金额 对数据的精度要求较高,小数的运算和存储存在精度问题(不能将所有小数转换成二进制) 定点数decimal price decimal(8,2)有2位小数的定点数,定点数支持很大的数(甚至是超过int,bigint存储范围的数)

小单位大数额避免出现小数 元->分

字符串存储 定长char,非定长varchar、text(上限65535,其中varchar还会消耗1-3字节记录长度,而text使用额外空间记录长度)

原则:尽可能选择小的数据类型和指定短的长度 原则:尽可能使用 not null 非null字段的处理要比null字段的处理高效些!且不需要判断是否为null。

null在MySQL中,不好处理,存储需要额外空间,运算也需要特殊的运算符。如select null = null和select null <> null(<>为不等号)有着同样的结果,只能通过is null和is not null来判断字段是否为null。

如何存储?MySQL中每条记录都需要额外的存储空间,表示每个字段是否为null。因此通常使用特殊的数据进行占位,比如int not null default 0、string not null default ‘’

原则:字段注释要完整,见名知意 原则:单表字段不宜过多 二三十个就极限了

原则:可以预留字段 在使用以上原则之前首先要满足业务需求 关联表的设计 外键foreign key只能实现一对一或一对多的映射 一对多 使用外键

多对多 单独新建一张表将多对多拆分成两个一对多

一对一 如商品的基本信息(item)和商品的详细信息(item_intro),通常使用相同的主键或者增加一个外键字段(item_id)

范式 Normal Format

数据表的设计规范,一套越来越严格的规范体系(如果需要满足N范式,首先要满足N-1范式)。N 第一范式1NF:字段原子性 字段原子性,字段不可再分割。

关系型数据库,默认满足第一范式 注意比较容易出错的一点,在一对多的设计中使用逗号分隔多个外键,这种方法虽然存储方便,但不利于维护和索引(比如查找带标签java的文章)

第二范式:消除对主键的部分依赖 即在表中加上一个与业务逻辑无关的字段作为主键 主键:可以唯一标识记录的字段或者字段集合。

course_namecourse_classweekday(周几)course_teacherMySQL教育大楼1525周一张三Java教育大楼1521周三李四MySQL教育大楼1521周五张三

依赖:A字段可以确定B字段,则B字段依赖A字段。比如知道了下一节课是数学课,就能确定任课老师是谁。于是周几和下一节课和就能构成复合主键,能够确定去哪个教室上课,任课老师是谁等。但我们常常增加一个id作为主键,而消除对主键的部分依赖。

对主键的部分依赖:某个字段依赖复合主键中的一部分。

解决方案:新增一个独立字段作为主键。

第三范式:消除对主键的传递依赖 传递依赖:B字段依赖于A,C字段又依赖于B。比如上例中,任课老师是谁取决于是什么课,是什么课又取决于主键id。因此需要将此表拆分为两张表日程表和课程表(独立数据独立建表):

idweekdaycourse_classcourse_id1001周一教育大楼15213546course_idcourse_namecourse_teacher3546Java张三

这样就减少了数据的冗余(即使周一至周日每天都有Java课,也只是course_id:3546出现了7次)

垂直分割(分表)

是一种把数据库中的表按列变成几张表的方法,这样可以降低表的复杂度和字段的数目,从而达到优化的目的。(以前,在银行做过项目,见过一张表有100多个字段,很恐怖)

示例一:在Users表中有一个字段是家庭地址,这个字段是可选字段,相比起,而且你在数据库操作的时候除了个人信息外,你并不需要经常读取或是改写这个字段。那么,为什么不把他放到另外一张表中呢? 这样会让你的表有更好的性能,大家想想是不是,大量的时候,我对于用户表来说,只有用户ID,用户名,口令,用户角色等会被经常使用。小一点的表总是会有好的性能。

示例二: 你有一个叫 “last_login” 的字段,它会在每次用户登录时被更新。但是,每次更新时会导致该表的查询缓存被清空。所以,你可以把这个字段放到另一个表中,这样就不会影响你对用户ID,用户名,用户角色的不停地读取了,因为查询缓存会帮你增加很多性能。

另外,你需要注意的是,这些被分出去的字段所形成的表,你不会经常性地去Join他们,不然的话,这样的性能会比不分割时还要差,而且,会是极数级的下降。

存储引擎选择

早期问题:如何选择MyISAM和Innodb? 现在不存在这个问题了,Innodb不断完善,从各个方面赶超MyISAM,也是MySQL默认使用的。 存储引擎Storage engine:MySQL中的数据、索引以及其他对象是如何存储的,是一套文件系统的实现。

功能差异 show engines

EngineSupportCommentInnoDBDEFAULTSupports transactions, row-level locking, and foreign keysMyISAMYESMyISAM storage engine

存储差异 MyISAMInnodb文件格式数据和索引是分别存储的,数据.MYD,索引.MYI数据和索引是集中存储的,.ibd文件能否移动能,一张表就对应.frm、MYD、MYI3个文件否,因为关联的还有data下的其它文件记录存储顺序按记录插入顺序保存按主键大小有序插入空间碎片(删除记录并flush table 表名之后,表文件大小不变)产生。定时整理:使用命令optimize table 表名实现不产生事务不支持支持外键不支持支持锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的)表级锁定行级锁定、表级锁定,锁定力度小并发能力高

锁扩展 表级锁(table-level lock):lock tables ,… read/write,unlock tables ,…。其中read是共享锁,一旦锁定任何客户端都不可读;write是独占/写锁,只有加锁的客户端可读可写,其他客户端既不可读也不可写。锁定的是一张表或几张表。 行级锁(row-level lock):锁定的是一行或几行记录。共享锁:select _from where <条件> LOCK IN SHARE MODE;,对查询的记录增加共享锁;select _ from where <条件> FOR UPDATE;,对查询的记录增加排他锁。这里值得注意的是:innodb的行锁,其实是一个子范围锁,依据条件锁定部分范围,而不是就映射到具体的行上,因此还有一个学名:间隙锁。比如select * from stu where id < 20 LOCK IN SHARE MODE会锁定id在20左右以下的范围,你可能无法插入id为18或22的一条新纪录。 选择依据 如果没有特别的需求,使用默认的Innodb即可。

MyISAM:以读写插入为主的应用程序,比如博客系统、新闻门户网站。

Innodb:更新(删除)操作频率也高,或者要保证数据的完整性;并发量高,支持事务和外键保证数据完整性。比如OA自动化办公系统。

索引

关键字与数据的映射关系称为索引(==包含关键字和对应的记录在磁盘中的地址==)。关键字是从数据当中提取的用于标识、检索数据的特定内容。 索引检索为什么快? 关键字相对于数据本身,==数据量小== 关键字是==有序==的,二分查找可快速确定位置 图书馆为每本书都加了索引号(类别-楼层-书架)、字典为词语解释按字母顺序编写目录等都用到了索引。

MySQL中索引类型 普通索引(key),唯一索引(unique key),主键索引(primary key),全文索引(fulltext key) 三种索引的索引方式是一样的,只不过对索引的关键字有不同的限制:

普通索引:对关键字没有限制 唯一索引:要求记录提供的关键字不能重复 主键索引:要求关键字唯一且不为null 索引管理语法 查看索引 show create table 表名:

索引的缺点

1.创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加

2.需占用额外的物理空间

3.当对表中数据进行增加、删除和修改的时候,

索引也要动态的维护,降低了数据的维护速度

执行计划explain

我们可以通过explain selelct来分析SQL语句执行前的执行计划: 执行计划是:当执行SQL语句时,首先会分析、优化,形成执行计划,在按照执行计划执行。

索引使用场景(重点)

where

order by 当我们使用order by将查询结果按照某个字段排序时,如果该字段没有建立索引,那么执行计划会将查询出的所有数据使用外部排序(将数据从硬盘分批读取到内存使用内部排序,最后合并排序结果),这个操作是很影响性能的,因为需要将查询涉及到的所有数据从磁盘中读到内存(如果单条数据过大或者数据量过多都会降低效率),更无论读到内存之后的排序了。

但是如果我们对该字段建立索引alter table 表名 add index(字段名),那么由于索引本身是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。而且如果分页的,那么只用取出索引表某个范围内的索引对应的数据,而不用像上述那取出所有数据进行排序再返回某个范围内的数据。(从磁盘取数据是最影响性能的)

join

对join语句匹配关系(on)涉及的字段建立索引能够提高效率

like查询,不能以通配符开头 比如搜索标题包含mysql的文章:

select * from article where title like ‘%mysql%’; 这种SQL的执行计划用不了索引(like语句匹配表达式以通配符开头),因此只能做全表扫描,效率极低,在实际工程中几乎不被采用。而一般会使用第三方提供的支持中文的全文索引来做。

但是 关键字查询 热搜提醒功能还是可以做的,比如键入mysql之后提醒mysql 教程、mysql 下载、mysql 安装步骤等。用到的语句是:

select * from article where title like ‘mysql%’; 这种like是可以利用索引的(当然前提是title字段建立过索引)。

索引的存储结构

BTree btree(多路平衡查找树)是一种广泛应用于==磁盘上实现索引功能==的一种数据结构,也是大多数数据库索引表的实现。

Mysql - 图3

BTree的一个node可以存储多个关键字,node的大小取决于计算机的文件系统,因此我们可以通过减小索引字段的长度使结点存储更多的关键字。如果node中的关键字已满,那么可以通过每个关键字之间的子节点指针来拓展索引表,但是不能破坏结构的有序性,比如按照first_name第一有序、last_name第二有序的规则,新添加的韩香就可以插到韩康之后。白起 < 韩飞 < 韩康 < 李世民 < 赵奢 < 李寻欢 < 王语嫣 < 杨不悔。这与二叉搜索树的思想是一样的,只不过二叉搜索树的查找效率是log(2,N)(以2为底N的对数),而BTree的查找效率是log(x,N)(其中x为node的关键字数量,可以达到1000以上)。

倒排索引

在关系数据库系统里,索引是检索数据最有效率的方式,。但对于搜索引擎,它并不能满足其特殊要求: 1)海量数据:搜索引擎面对的是海量数据,像Google,百度这样大型的商业搜索引擎索引都是亿级甚至百亿级的网页数量 ,面对如此海量数据 ,使得数据库系统很难有效的管理。 2)数据操作简单:搜索引擎使用的数据操作简单 ,一般而言 ,只需要增、 删、 改、 查几个功能 ,而且数据都有特定的格式 ,可以针对这些应用设计出简单高效的应用程序。而一般的数据库系统则支持大而全的功能 ,同时损失了速度和空间。最后 ,搜索引擎面临大量的用户检索需求 ,这要求搜索引擎在检索程序的设计上要分秒必争 ,尽可能的将大运算量的工作在索引建立时完成 ,使检索运算尽量的少。一般的数据库系统很难承受如此大量的用户请求 ,而且在检索响应时间和检索并发度上都不及我们专门设计的索引系统。

正向索引(正排索引):正排表是以文档的ID为关键字,表中记录文档中每个字的位置信息,查找时扫描表中每个文档中字的信息直到找出所有包含查询关键字的文档。 正排表结构如图1所示,这种组织方法在建立索引的时候结构比较简单,建立比较方便且易于维护;因为索引是基于文档建立的,若是有新的文档加入,直接为该文档建立一个新的索引块,挂接在原来索引文件的后面。若是有文档删除,则直接找到该文档号文档对应的索引信息,将其直接删除。但是在查询的时候需对所有的文档进行扫描以确保没有遗漏,这样就使得检索时间大大延长,检索效率低下。 尽管正排表的工作原理非常的简单,但是由于其检索效率太低,除非在特定情况下,否则实用性价值不大。

倒排索引(英语:Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最常用的数据结构。通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:”单词词典”和”倒排文件”。

倒排索引有两种不同的反向索引形式: 一条记录的水平反向索引(或者反向档案索引)包含每个引用单词的文档的列表。 一个单词的水平反向索引(或者完全反向索引)又包含每个单词在一个文档中的位置。 后者的形式提供了更多的兼容性(比如短语搜索),但是需要更多的时间和空间来创建。 现代搜索引擎的索引都是基于倒排索引。相比”签名文件”、”后缀树”等索引结构,”倒排索引”是实现单词到文档映射关系的最佳实现方式和最有效的索引结构。

select * 要少用

即尽量选择自己需要的字段select,但这个影响不是很大,因为网络传输多了几十上百字节也没多少延时,并且现在流行的ORM框架都是用的select *,只是我们在设计表的时候注意将大数据量的字段分离,比如商品详情可以单独抽离出一张商品详情表,这样在查看商品简略页面时的加载速度就不会有影响了。

order by rand()不要用

它的逻辑就是随机排序(为每条数据生成一个随机数,然后根据随机数大小进行排序)。如select * from student order by rand() limit 5的执行效率就很低,因为它为表中的每条数据都生成随机数并进行排序,而我们只要前5条。

解决思路:在应用程序中,将随机的主键生成好,去数据库中利用主键检索。

单表和多表查询

多表查询:join、子查询都是涉及到多表的查询。如果你使用explain分析执行计划你会发现多表查询也是一个表一个表的处理,最后合并结果。因此可以说单表查询将计算压力放在了应用程序上,而多表查询将计算压力放在了数据库上。

现在有ORM框架帮我们解决了单表查询带来的对象映射问题(查询单表时,如果发现有外键自动再去查询关联表,是一个表一个表查的)。

count(*)

在MyISAM存储引擎中,会自动记录表的行数,因此使用count(*)能够快速返回。而Innodb内部没有这样一个计数器,需要我们手动统计记录数量,解决思路就是单独使用一张表:

idtablecount1student100

limit 1

如果可以确定仅仅检索一条,建议加上limit 1,其实ORM框架帮我们做到了这一点(查询单条的操作都会自动加上limit 1)。

慢查询日志

用于记录执行时间超过某个临界值的SQL日志,用于快速定位慢查询,为我们的优化做参考。 开启慢查询日志 配置项:slow_query_log

可以使用show variables like ‘slov_query_log’查看是否开启,如果状态值为OFF,可以使用set GLOBAL slow_query_log = on来开启,它会在datadir下产生一个xxx-slow.log的文件。

__