MYSQL

77 阅读11分钟

MySQL服务器已经获得了文本形式的请求,接着 还要经过九九八十一难的处理,其中的几个比较重要的部分分别是查询缓存、语法解析和查询优化 不过既然是缓存,那就有它缓存失效的时候。MySQL的缓存系统会监测涉及到的每张表,只要该表的结构或者数据被修改,如对该表使用了INSERT、 UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE或 DROP DATABASE语句,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除!

系统变量比较牛逼的一点就是,对于大部分系统变量来说,它们的值可以在服务器程序运行过程中进行动态修改而无需停止并重启服务器。不过系统变量有作用范围之分,下边详细唠叨下。 系统变量的作用范围的概念,具体来说作用范围分为这两种: GLOBAL:全局变量,影响服务器的整体操作。 SESSION:会话变量,影响某个客户端连接的操作。(注:SESSION有个别名叫LOCAL)

所以当我们想从表中获取某些记录时,InnoDB存储引擎需要一条一条的把记录从磁盘上读出来么?不,那样会慢死,InnoDB采取的方式是:将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB。也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。 我们平时是以记录为单位来向表中插入数据的,这些记录在磁盘上的存放方式也被称为行格式或者记录格式。设计InnoDB存储引擎的大叔们到现在为止设计了4种不同类型的行格式,分别是Compact、Redundant、Dynamic和Compressed行格式 对于record_format_demo表来说,记录的真实数据除了c1、c2、c3、c4这几个我们自己定义的列的数据以外,MySQL会为每个记录默认的添加一些列(也称为隐藏列),具体的列如下: row_id,transaction_id,roll_pointer InnoDB表对主键的生成策略:优先使用用户自定义主键作为主键,如果用户没有定义主键,则选取一个Unique键作为主键,如果表中连Unique键都没有定义的话,则InnoDB会为表默认添加一个名为row_id的隐藏列作为主键。所以我们从上表中可以看出:InnoDB存储引擎会为每条记录都添加 transaction_id 和 roll_pointer 这两个列,但是 row_id 是可选的(在没有自定义主键以及Unique键的情况下才会添加该列)。 但是这只是因为我们的record_format_demo表采用的是ascii字符集,这个字符集是一个定长字符集,也就是说表示一个字符采用固定的一个字节,如果采用变长的字符集(也就是表示一个字符需要的字节数不确定,比如gbk表示一个字符要1~2个字节、utf8表示一个字符要1~3个字节等)的话,c3列的长度也会被存储到变长字段长度列表中,比如我们修改一下record_format_demo表的字符集:

需要回表的记录越多,使用二级索引的性能就越低,甚至让某些查询宁愿使用全表扫描也不使用二级索引。比方说name值在Asa~Barlow之间的用户记录数量占全部记录数量90%以上,那么如果使用idx_name_birthday_phone_number索引的话,有90%多的id值需要回表,这不是吃力不讨好么,还不如直接去扫描聚簇索引(也就是全表扫描)。 SELECT * FROM person_info WHERE name > 'Asa' AND name < 'Barlow' LIMIT 10; 添加了LIMIT 10的查询更容易让优化器采用二级索引 + 回表的方式进行查询。 SELECT * FROM person_info ORDER BY name, birthday, phone_number; 由于查询列表是*,所以如果使用二级索引进行排序的话,需要把排序完的二级索引记录全部进行回表操作,这样操作的成本还不如直接遍历聚簇索引然后再进行文件排序(filesort)低,所以优化器会倾向于使用全表扫描的方式执行查询。 如何建立索引: a.只为用于搜索、排序或分组的列创建索引:也就是说,只为出现在WHERE子句中的列、连接子句中的连接列,或者出现在ORDER BY或GROUP BY子句中的列创建索引。而出现在查询列表中的列就没必要建立索引了; b.考虑列的基数:最好为那些列的基数大的列建立索引,为基数太小列的建立索引效果可能不好; c.索引列的类型尽量小:数据类型越小,在查询时进行的比较操作越快;数据类型越小,索引占用的存储空间就越少,在一个数据页内就可以放下更多的记录,从而减少磁盘I/O带来的性能损耗,; d.索引字符串值的前缀:字符串越长,在索引中占用的存储空间越大;索引列存储的字符串很长,那在做字符串比较时会占用更多的时间。 e.让索引列在比较表达式中单独出现:如果索引列在比较表达式中不是以单独列的形式出现,而是以某个表达式,或者函数调用形式出现的话,是用不到索引的。 f.主键插入顺序:为了避免产生页面分裂,最好让插入的记录的主键值依次递增,这样就不会发生这样的性能损耗了; g.避免冗余和重复索引;

引入了区(extent)的概念,一个区就是在物理位置上连续的64个页。在表中数据量大的时候,为某个索引分配空间的时候就不再按照页为单位分配了,而是按照区为单位分配,甚至在表中的数据十分非常特别多的时候,可以一次性分配多个连续的区。虽然可能造成一点点空间的浪费(数据不足填充满整个区),但是从性能角度看,可以消除很多的随机I/O; 对B+树的叶子节点和非叶子节点进行了区别对待,也就是说叶子节点有自己独有的区,非叶子节点也有自己独有的区。存放叶子节点的区的集合就算是一个段(segment),存放非叶子节点的区的集合也算是一个段。也就是说一个索引会生成2个段,一个叶子节点段,一个非叶子节点段。 为某个段分配存储空间的策略是这样的: 在刚开始向表中插入数据的时候,段是从某个碎片区以单个页面为单位来分配存储空间的。 当某个段已经占用了32个碎片区页面之后,就会以完整的区为单位来分配存储空间。

const:通过主键或者唯一二级索引列与常数的等值比较来定位一条记录是像坐火箭一样快的,所以他们把这种通过主键或者唯一二级索引列来定位一条记录的访问方法定义为:const,意思是常数级别的,代价是可以忽略不计的。 ref:对某个普通的二级索引列与常数进行等值比较; 二级索引列值为NULL的情况:不论是普通的二级索引,还是唯一二级索引,它们的索引列对包含NULL值的数量并不限制,所以我们采用key IS NULL这种形式的搜索条件最多只能使用ref的访问方法,而不是const的访问方法。 ref_or_null: range:可以使用全表扫描的方式来执行这个查询,不过也可以使用二级索引 + 回表的方式执行; index:遍历二级索引记录; all:全表扫描; 索引合并: MySQL在一般情况下执行一个查询时最多只会用到单个二级索引,但不是还有特殊情况么,在这些特殊情况下也可能在一个查询中使用到多个二级索引。虽然读取多个二级索引比读取一个二级索引消耗性能,但是读取二级索引的操作是顺序I/O,而回表操作是随机I/O,所以哪个消耗大没有绝对。可能用到索引合并的情况如下: Intersection求交(AND): 情况1:二级索引列是等值匹配的情况,对于联合索引来说,在联合索引中的每个列都必须等值匹配,不能出现只匹配部分列的情况。 情况2:主键列可以是范围匹配; Union合并(OR): 情况1:二级索引列是等值匹配的情况,对于联合索引来说,在联合索引中的每个列都必须等值匹配,不能出现只出现匹配部分列的情况。 情况2:主键列可以是范围匹配; 情况3:使用Intersection索引合并的搜索条件; Sort-Union合并

对于内连接的两个表,驱动表中的记录在被驱动表中找不到匹配的记录,该记录不会加入到最后的结果集,我们上边提到的连接都是所谓的内连接。 对于外连接的两个表,驱动表中的记录即使在被驱动表中没有匹配的记录,也仍然需要加入到结果集。 左外连接:选取左侧的表为驱动表。 右外连接:选取右侧的表为驱动表。 WHERE:WHERE子句中的过滤条件,不论是内连接还是外连接,凡是不符合WHERE子句中的过滤条件的记录都不会被加入最后的结果集。 ON:对于外连接的驱动表的记录来说,如果无法在被驱动表中找到匹配ON子句中的过滤条件的记录,那么该记录仍然会被加入到结果集中,对应的被驱动表记录的各个字段使用NULL值填充。 对于左(外)连接和右(外)连接来说,必须使用ON子句来指出连接条件。 对于两表连接来说,驱动表只会被访问一遍,但被驱动表却要被访问到好多遍,具体访问几遍取决于对驱动表执行单表查询后的结果集中的记录条数。对于内连接来说,选取哪个表为驱动表都没关系,而外连接的驱动表是固定的; join buffer

13-19.未看

在对某个页面进行读写访问时,都会先把这个页面加载到Buffer Pool中,之后如果修改了某个页面,也不会立即把修改同步到磁盘,而只是把这个修改了的页面加到Buffer Pool的flush链表。 原子性(Atomicity)、隔离性(Isolation)、一致性(Consistency)和持久性(Durability) START TRANSACTION语句和BEGIN语句有着相同的功效,都标志着开启一个事务; 显示提交:COMMIT语句就代表提交一个事务 隐式提交 自动提交 保存点

21-24未看

当于读到了一个不存在的数据,这种现象就称之为脏读; 如果一个事务多次读取同一个数据值不同,称为不可重复读; 幻读强调的是一个事务按照某个相同条件多次读取记录时,后读取时读到了之前没有读到的记录。 脏读的产生是因为当前事务读取了另一个未提交事务写的一条记录; 不可重复读的产生是因为当前事务先读取一条记录,另外一个事务对该记录做了改动之后并提交之后,当前事务再次读取时会获得不同的值; 我们说幻读问题的产生是因为当前事务读取了一个范围的记录,然后另外的事务向该范围内插入了新记录。