《Mysql技术内幕》札记(中)

 第四章   表

一、innodb逻辑结构

Innodb存储引擎表,每张表都有个主键,如果没有显示的主键,则innodb存储引擎会按如下的方式选择或创建主键。

  1. 是否有非空的唯一索引,如果有即为主键
  2. 不符合上述条件,innodb自动创建一个6个字节大小的指针

表空间由段,区,页组成。

Innodb_file_per_table选项,每张表的表空间内存放的只是数据,索引和插入缓冲。其他类型undo信息,系统事务信息,二次写缓冲等还是存放在原来的共享表空间内。即使在Innodb_file_per_table为On的情况下,共享文件依然会增大。在执行事务时会产生大量的undo操作,当rollback后,共享表空间也不会缩减至原来大小。当发现不需要时将此空间标记为可用空间,供下次undo使用,这时你会发现下次再执行update语句时,共享表空间没有再增大

表空间由分散的页和段组成。段常见的有数据段,索引段和回滚段。区有连续的64个页构成,每个区大小为1MB,大于大的数据段,最多可以申请4个区。然而在每个段开始,先申请32页大小的碎片页来存储,当这些页使用完之后才是申请连续的64个页。

行:每个页最多7992行记录。

行格式:默认为compact格式

mysql> show table status like‘user_ship‘\G

*************************** 1. row***************************

Name: user_ship

Engine: InnoDB

Version: 10

Row_format: Compact

Rows: 277974

Avg_row_length: 115

Data_length: 32047104

Max_data_length: 0

Index_length: 13156352

Data_free: 5242880

Auto_increment: NULL

Create_time: 2014-12-22 01:30:10

Update_time: NULL

Check_time: NULL

Collation: utf8_unicode_ci

Checksum: NULL

Create_options:

Comment: 我的舰船

1 row in set (0.14 sec)

Compact行记录格式

变长字段长度列表 NULL标识位 记录头信息 列1数据 列2数据 ………

变长字段:不能超过2个字节,2个字节一共16位,即=65535个字节,因此varchar最大长度不超过65535个字节。

NULL表示位:标识该数据中是否有NULL值,有则显示1。

记录头信息,占5个字节。

NULL占用标识位不占用任何空间。

还有两列为事务id列和回滚指针列,若Innodb没有定义primarykey 每行会增加一个6字节的rowid列。

记录头信息:

该行是否被删除 该行是否为最小记录 该记录拥有记录数 索引堆中该条记录额排序记录
记录类型

(指针/普通节点)

页中下一条记录的相对位置

二、分区表

水平分区:同一个表中不同行的记录分配到不同的物理文件中
垂直分区:同一表中不同列分配到不同的物理文件
分区中即存了数据又存了索引
分区类型:
range分区:给定连续区域区间存入不同分区(year(),todays(), tosecond(),unixtimestamp()只能包含这些函数的一个,不然将全表扫描) 
List分区 离散的值存入不同分区

Column分区  不需要整形数据 是升级版(时间列用了比较多)
Hash分区 自定义表达式返回值进行分区 
Key分区 根据哈希函数分区
分区的列如果表中有唯一索引或主键索引,那必须他是其中的一部分,不然会报A PRIMARY KEY must include all columns in the table‘s partitioningfunction如果没有这种唯一索引,他可以任何个列。
Hash分区根据给定的列除以分区个数决定分区在哪

Colunmn分区

create table div_colum(id int ,d datetime )partition by rangecolumns (d) (partition p0 values less than (‘2015-10-01‘),partition p1 valuesless than (‘2015-11-01‘),partition p2 values less than maxvalue);

根据d分区 d小于2015-10-01或者为Null的存在p0分区里,同理。。。

insert into div_colum values (1,‘2015-09-01‘);

insert into div_colum values (2,‘2015-12-01‘);

insert into div_colum values (3,‘null‘);

插入时如果不在定义的分区范围将会报错

mysql> select * from information_schema.PARTITIONS whereTABLE_SCHEMA=‘freedom‘ and TABLE_NAME=‘div_colum‘\G

*************************** 1. row ***************************

TABLE_CATALOG:def

TABLE_SCHEMA:freedom

TABLE_NAME:div_colum

PARTITION_NAME:p0

SUBPARTITION_NAME:NULL

PARTITION_ORDINAL_POSITION:1

SUBPARTITION_ORDINAL_POSITION: NULL

PARTITION_METHOD:RANGE COLUMNS

SUBPARTITION_METHOD:NULL

PARTITION_EXPRESSION:`d`

SUBPARTITION_EXPRESSION:NULL

PARTITION_DESCRIPTION:‘2015-10-01‘

TABLE_ROWS:2

AVG_ROW_LENGTH:8192

*************************** 2. row ***************************

TABLE_CATALOG:def

TABLE_SCHEMA:freedom

TABLE_NAME:div_colum

PARTITION_NAME:p1

SUBPARTITION_NAME:NULL

PARTITION_ORDINAL_POSITION:2

SUBPARTITION_ORDINAL_POSITION: NULL

PARTITION_METHOD:RANGE COLUMNS

SUBPARTITION_METHOD:NULL

PARTITION_EXPRESSION:`d`

SUBPARTITION_EXPRESSION:NULL

PARTITION_DESCRIPTION:‘2015-11-01‘

TABLE_ROWS:0

AVG_ROW_LENGTH:0

*************************** 3. row ***************************

TABLE_CATALOG:def

TABLE_SCHEMA:freedom

TABLE_NAME:div_colum

PARTITION_NAME: p2

SUBPARTITION_NAME:NULL

PARTITION_ORDINAL_POSITION:3

SUBPARTITION_ORDINAL_POSITION: NULL

PARTITION_METHOD:RANGE COLUMNS

SUBPARTITION_METHOD:NULL

PARTITION_EXPRESSION:`d`

SUBPARTITION_EXPRESSION:NULL

PARTITION_DESCRIPTION:MAXVALUE

TABLE_ROWS:1

AVG_ROW_LENGTH:16384

可以看到表PARTITIONS,知道分区情况 ,p0有两个数据,p2有1个

-rw-rw----. 1 mysql mysql 8.4K Nov 24 10:05 div_colum.frm

-rw-rw----. 1 mysql mysql   32 Nov 24 10:05 div_colum.par

-rw-rw----. 1 mysql mysql 96K Nov 24 10:13 div_colum#P#p0.ibd

-rw-rw----. 1 mysql mysql 96K Nov 24 10:05 div_colum#P#p1.ibd

-rw-rw----. 1 mysql mysql 96K Nov 24 10:06 div_colum#P#p2.ibd

可以看到分区表的存储是均等分配的,每个分区有独立的文件。

删除数据直接drop这个分区

mysql> alter table div_colum drop partition p0;

Query OK, 0 rows affected (0.04 sec)

Records: 0 Duplicates: 0  Warnings: 0

mysql> select * from div_colum;

+------+---------------------+

| id   | d                   |

+------+---------------------+

|    2 | 2015-12-0100:00:00 |

+------+---------------------+

子分区
在分区上再分区
每个子分区数量必须相同
子分区要么全显示定义要么都隐式定义
分区可以规定分区放在哪 索引放在哪

create table div_colum_key(id int ,d datetime )ENGINE=innodb partition by range(month(d)) SUBPARTITION BY HASH(month(d))(partition p0 values less than (10)(SUBPARTITION s0 data directory=‘/opt/data0‘ index directory =‘/opt/index0‘, SUBPARTITION s1 data directory=‘/opt/data1‘  index directory=‘/opt/index1‘));

只能运用range list分区法,并且字分区只能用hash 和key,存储地址直接加 data directory,index directory进行存储

-rw-rw----. 1 mysql mysql 8.4K Nov 24 11:09div_colum_key.frm

-rw-rw----. 1 mysql mysql   40 Nov 24 11:09 div_colum_key.par

-rw-rw----. 1 mysql mysql   47 Nov 24 11:09 div_colum_key#P#p0#SP#s0.isl

-rw-rw----. 1 mysql mysql   47 Nov 24 11:09 div_colum_key#P#p0#SP#s1.isl

文件中包含isl,真正的数据在指定目录里

/opt/data1/freedom/ div_colum_key#P#p0#SP#s1.ibd 子分区文件在指定文件夹中再加入了库名

分区表在获取大量数据而且一定要走这个列速度提快
分区列不作为搜索条件时会进行n个分区倍次数的io执行


第五章  索引与算法

一、索引简介

索引的多少需要找到合适的平衡点。如果索引太多,磁盘的使用率会增加(索引字段的更新需要随机读)。索引太少,查询性能会被影响(查询速度和行锁的个数)。

Innodb支持两种索引:B+树索引和哈希索引。Innodb的哈希索引时自适应式哈希索引,系统自动为表建,不需要认为干涉。B+树索引就是数据库常见的索引方式,b+树是平衡树,b+树索引不能直接找到记录,而是找到页,将页读入内存,在内存中innodb根据page diretory中的槽二分查找定位页中的记录。

平衡二叉树查询速度快,最快的是最优二叉树,但维护成本高,一般性平衡二叉树运用在内存里。

B+树维护时避免更多拆分采用旋转将数据放到兄弟节点。B+树索引有其高扇出性,b+树一般都在2-3层,意味着查某个值记录最多需要2,3次io,一般的磁盘每秒至少可以做100次Io。

B+树索引可以分为聚集索引和辅助索引。聚集索引和辅助索引是否叶子节点存储一整行记录。

Innodb存储引擎是索引组织表,即逻辑上表中数据都根据主键存放,物理存储堆方式顺序存储,一个表只能拥有一个聚集索引。定义了逻辑顺序,通过集聚索引很容易找到记录。

辅助索引一般包括键值和书签,innodb是索引组织表,所以他的书签就是他的主键值。搜索数据时遍历索引并通过页节点指针指向主建 然后通过主件索引找到完整记录行。Sqlsever存储堆方式,书签就是用文件号,页号,槽号。这样的方式在查询时不需要通过主键虽然快了,预读避免了这其中离散读的情况,但是在插入,更新,删除等更新时,需要不断的更改书签。这样会更慢。然而在排序和范围查询时,索引组织表可以通过访问中间节点就可以查找读取数据是一种顺序读。

mysql> alter table a add indexa(name(5));

可以对列的前几个字符建索引。

事务一

mysql> start transaction ;

mysql> delete from a;

事务二

mysql> alter table a add index ab(name);

^CCtrl-C -- sending "KILL QUERY2069" to server ...

Ctrl-C -- query aborted.

可以看到在加索引时会有排斥锁的存在。

在建主键索引时,他会新建一张临时表,然后将数据导入该表,删除原表,再把临时表重命名

索引详细的查看

mysql> show index from a\G

*************************** 1. row***************************

Table: a

Non_unique: 1 是否为非唯一索引

Key_name: a 索引名

Seq_in_index: 1 联合索引时,次序列号将一致

Column_name: name 列名

Collation: A 列的存放方式

Cardinality: 1   唯一值的数量

Sub_part: 5   如果是部分索引,表示索引前几个字符

Packed: NULL   如何被压缩 没有压缩则为Null

Null: YES     是否索引含有Null值

Index_type: BTREE

Comment:

Index_comment:

Collation/行数是否接近1,优化器以Collation和explain rows是否小于一个阀值来确定是否走索引。但是有时会不准,需要analyze table t来优化。

强制走索引select * from c force index(PRIMARY);

预读:就是通过一次Io,将多个页预读到缓冲池,并且多个页马上会被访问,

预读分为随机预读和线性预读。随机预读(效果不好),当一个区(64个页)中13个页在缓冲区中,并且在lru列表前端(页的频繁访问)。则将这个区的剩余所有页预读到缓冲区。线性预读:基于页的访问模式,如果一个区的24个页都被顺序的访问,则innodb会读取下一个区的所有页。

mysql> show variables like‘%read_ahead%‘;

+-----------------------------+-------+

| Variable_name               | Value |

+-----------------------------+-------+

| innodb_random_read_ahead    | OFF  |

| innodb_read_ahead_threshold |56    |

+-----------------------------+-------+

innodb_random_read_ahead显示随机预读已关

innodb_read_ahead_threshold 表明一个区多少页在缓冲池里就将下一个区的所有页读入缓冲池

二、辅助索引的利用

mysql> select * from id_var;

+----+------+

| id | name |

+----+------+

| 2 | a    |

| 1 | b    |

+----+------+

可以看到利用辅助索引来得到结果,那为什么不利用主键呢?因为查出来数据包括主键数据和索引数据,都可以在辅助索引页中找到,并且辅助索引页能存放数据多,所以优化器就选择直接利用附注索引来查找得到数据

select * from id_var order by iddesc;

但涉及到主键排序,将走聚集索引。 因为聚集索引已经排好序

联合索引

CREATE TABLE `c` (

`id` int(11) DEFAULT NULL,

`dd` datetime DEFAULT NULL,

KEY `id` (`id`,`dd`)

)

mysql> insert into c values (1,‘2016-08-01‘);

mysql> insert into c values(2,‘2015-08-01‘);

联合索引第一:加快了联合键值查询的速度

mysql> explain select * from cwhere id=2 and dd=‘2015-08-01‘;

+----+-------------+-------+------+---------------+------+---------+-------------+------+-------------+

| id | select_type | table | type| possible_keys | key  | key_len |ref         | rows | Extra       |

+----+-------------+-------+------+---------------+------+---------+-------------+------+-------------+

| 1 | SIMPLE      | c     | ref (根据索引查找值) | id            | id  | 11      | const,const |    1 | Using index |

+----+-------------+-------+------+---------------+------+---------+-------------+------+-------------+

1 row in set (0.03 sec)

mysql> explain select * from cwhere dd=‘2015-08-01‘;

+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+

| id | select_type | table |type  | possible_keys | key  | key_len | ref  | rows | Extra                    |

+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+

| 1 | SIMPLE      | c     | index |NULL          | id   | 11     | NULL |    2 | Using where; Usingindex |

+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+

可以看到是索引遍历。对第二个索引列进行查找时,虽然他在索引页中出现,但是这课联合索引的B+树不是根据第二列所排序组成的(即无序),所以需要全索引遍历

联合索引第二:可以对后一个键值进行排序

mysql> explain select * from cwhere id=1 order by dd desc;

+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+

| id | select_type | table | type| possible_keys | key  | key_len |ref   | rows | Extra                    |

+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+

| 1 | SIMPLE      | c     | ref | id            | id   | 5      | const |    1 | Using where;Using index |

+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+

mysql> explain select *  from c  order by dd desc;

+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------------+

| id | select_type | table |type  | possible_keys | key  | key_len | ref  | rows | Extra                       |

+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------------+

| 1 | SIMPLE      | c     | index | NULL          | id  | 11      | NULL |   3 | Using index; Using filesort |

+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------------+

mysql> show status like‘sort_rows‘;#共排序了几个数据

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| Sort_rows     | 3    |

+---------------+-------+

对比发现第二个语句就是没有用文件中完成好的排序。sort_rows统计也上升了。

第六章 锁

一、一致性非锁定读操作

Innodb读默认非锁定一致性读,即读取正在执行的dml行的操作时,不需要等待锁的释放
Innodb用行多版本来读取当前行,读取快照是指该行之前版本的数据,历史数据,因此历史数据不需要加锁
READ-COMMITTED和REPEATABLE-READ,都是采用快照,但快照的定义不一样,READ-COMMITTED是被锁定行最新版本,REPEATABLE-READ是事务一开始的版本。

二、容易忽视的锁
外建约束与自增也会锁表,(详细介绍:你容易忽视的mysql外键锁和自增锁

)#外建约束必须加索引才能建立。
脏页在缓存中的页与磁盘不同 在刷新到磁盘前日志被写到重做日志里?
脏数据读到未提交的数据

三、阻塞

innodb_lock_wait_timeout用来控制等待时间默认50秒,此参数可以动态修改,但是是在新的链接里才能生效。
innodb_rollback_on_timeout超时是否回滚,默认不回滚
死锁:a锁住一行,b锁住一行,b需要访问a的那一行被锁住,a需要访问b的那一行,也锁住,死锁后报1213错误,系统自动回滚a的事务

mysql> select * from q;

+----+------+

| id | b    |

+----+------+

|  1 |    1 |

|  2 |    2 |

+----+------+

        事务a         事务b

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

mysql> delete from q where id=1;

Query OK, 1 row affected (0.00 sec)

mysql> delete from q where id=2;

ERROR 1213 (40001): Deadlock found when trying to get lock;try restarting transaction

mysql> delete from q where id=2;

ERROR 1213 (40001): Deadlock found when trying to getlock; try restarting transaction

mysql> select * from q;

+----+------+

| id | b    |

+----+------+

|  1 |    1 |

|  2 |    2 |

+----+------+

2 rows in set (0.00 sec)


delete from qwhere id=2;

Query OK, 0rows affected(0.00 sec)

mysql> deletefrom q where id=1

Query OK, 1 row affected (4.98 sec)

mysql> select * from q

Empty set (0.00 sec)

死锁发生,可以看到事务1发生了回滚。

时间: 2024-10-12 08:19:08

《Mysql技术内幕》札记(中)的相关文章

《mysql技术内幕 InnoDB存储引擎(第二版)》阅读笔记

一.mysql架构 mysql是一个单进程多线程架构的数据库. 二.存储引擎 InnoDB: 支持事务 行锁 读操作无锁 4种隔离级别,默认为repeatable 自适应hash索引 每张表的存储都是按主键的顺序记性存放 支持全文索引(InnoDB1.2.x - mysql5.6) 支持MVCC(多版本并发控制)实现高并发 MyISAM: 不支持事务 表锁 支持全文索引 三.InnoDB体系架构 1.后台线程 Master Thread 负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性 IO

MySQL技术内幕-InnoDB存储引擎-读书笔记(一)

MySQL技术内幕-InnoDB存储引擎-读书笔记(一) 作为php开发,使用mysql总是少不了的 博客链接 http://itsong.net/articles/466.html 第一章 MySQL体系结构和存储引擎 MySQL被设计为一个单进程多线程架构的数据库 ./mysql --help | grep my.cnf 可以查看mysql数据库实例启动时,它会在哪些位置查找配置文件. 配置文件中有一个datadir参数,指定了数据库所在的路径.默认为/usr/local/mysql/dat

Mysql技术内幕——表&索引算法和锁

表 4.1.innodb存储引擎表类型 innodb表类似oracle的IOT表(索引聚集表-indexorganized table),在innodb表中每张表都会有一个主键,如果在创建表时没有显示的定义主键则innodb如按照如下方式选择或者创建主键.首先表中是否有唯一非空索引(unique not null),如果有则该列即为主键.不符合上述条件,innodb存储引擎会自动创建一个6字节大小的指针,rowid(). 4.2.innodb逻辑存储结构 innodb的逻辑存储单元由大到小分别是

MySQL技术内幕-InnoDB存储引擎-读书笔记(二)

MySQL技术内幕-InnoDB存储引擎-读书笔记(二) 作为php开发,使用mysql总是少不了的 系列文章博客链接 http://itsong.net/articles/466.html 第三章 文件 mysql与innodb几个类型的文件 参数文件,配置路径.初始化参数.内存大小等 日志文件,包括错误日志,二进制日志,慢查询日志,查询日志 socket文件,用unix域套接字,unix domain socket来进行连接时需要的文件,这一般是本机连接,比通常tcp快 pid文件,进程id

mysql技术内幕InnoDB存储引擎-阅读笔记

mysql技术内幕InnoDB存储引擎这本书断断续续看了近10天左右,应该说作者有比较丰富的开发水平,在源码级别上分析的比较透彻.如果结合高可用mysql和高性能mysql来看或许效果会更好,可惜书太厚,还在啃当中,希望能早点读完……. 应该说与oracle相比,mysql数据库还是相对比简单,以后还是深入学习下oracle去. 搞数据库也比搞应用运维相对单纯,不用知道各种应用架构,不用写各种脚本工具,只要掌握这个软件就足够了.当然希望自己的知识还是全面一些好.

Mysql技术内幕——InnoDB存储引擎

一.mysql体系结构和存储引擎 1.1.数据库和实例的区别 数据库:物理操作系统或其他形式文件类型的集合.在mysql下数据库文件可以是frm,myd,myi,ibd结尾的文件. 数据库实例:由数据库后台进程/线程以及一个共享内存区组成.数据库实例才是真正用来操作数据库文件的. mysql数据库是单进程多线程的程序,与sql server比较类似.也就是说,Mysql数据库实例在系统上的表现就是一个进程. 1.2.mysql的体系结构 mysql由连接池组件.管理服务和工具组件.sql接口组建

《Mysql技术内幕》札记(上)

第一章   MYSQL体系结构和存储引擎 一.数据库的概念 数据库:数据库文件类型的集合,以frm与ibd结尾等. 数据库实例:数据库后台的进程/线程 以及共享内存组成,实例操作数据库文件数据库与实例一一对应,一个实例对应一个数据库Mysql是单进程多线程,这就意味着一个实例一个进程 MYSQL架构    SQL接口组件   查询分析器组件    优化器组件    缓存                       插件式存储引擎(基于表)                           物理

《Mysql技术内幕》札记(下)

第七章 事务 一.redo和undo 当一个事务开始时,会记录这个事务的日志序列号,当此事务执行时,会往日志缓冲池插入数据,当事务提交时,日志缓冲区的数据存入磁盘.(innodb_flush_log_at_trx_commit=1时) show engine innodb status --- LOG --- Log sequence number 1759155   (当前的lsn ) Log flushed up to  1759155(已经被刷新到重做日志lsn ) Last checkp

MySQL技术内幕(SQL编程)-数据类型

一:MySQL存储引擎 MySQL插件式存储引擎可以让存储引擎层的开发人员设计他们希望的存储层(满足事务需求.满足数据放到内存中等),常见的存储引擎如下: InnoDB存储引擎:支持事务,面向联机事务处理的应用.特点是行锁设计.支持外键.默认读取操作不会产生锁.最为常用的引擎,5.5.8版本后的默认存储引擎. MyISAM:表锁设计.支持全文索引.不支持事务,面向OLAP数据库应用,5.5.8版本前曾是默认存储引擎.其缓冲池只缓存索引文件,不缓存数据文件. NDB:集群存储引擎,结构是share