首页 » SEO优化 » php中isdefin技巧_MySQL 80New data dictionary

php中isdefin技巧_MySQL 80New data dictionary

访客 2024-11-26 0

扫一扫用手机浏览

文章目录 [+]

为理解决这些问题(尤其是DDL无法做到atomic),从MySQL8.0开始取消了FRM文件及其他server层的元数据文件(frm, par, trn, trg, isl,db.opt),所有的元数据都用InnoDB引擎进行存储, 其余一些诸如权限表之类的系统表也改用InnoDB引擎。

本文是笔者初次理解这块内容,因此不会过多深入,由于涉及的改动太多,后面有空再逐个展开。

php中isdefin技巧_MySQL 80New data dictionary

本文所有测试和代码干系部分都是基于MySQL8.0.0版本,由于这是8.0大版本的第一个开拓版本,不用除未来行为会发生变革。

php中isdefin技巧_MySQL 80New data dictionary
(图片来自网络侵删)

测试

首先我们创建一个新库,并在库下创建两个表来开启我们的测试

mysql> CREATE DATABASE sbtest;

Query OK, 1 row affected (0.00 sec)

mysql> USE sbtest

Database changed

mysql> CREATE TABLE t1 (a int primary key);

Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE t2 (a int primary key, b int);

Query OK, 0 rows affected (0.00 sec)$ls -lh /u01/my80/data/sbtest

total 256K

-rw-r----- 1 yinfeng.zwx users 128K Oct 5 19:44 t1.ibd

-rw-r----- 1 yinfeng.zwx users 128K Oct 5 19:44 t2.ibd$ls /u01/my80/data/sbtest_9.SDI

/u01/my80/data/sbtest_9.SDI$cat /u01/my80/data/sbtest_9.SDI

{ \"大众sdi_version\"大众: 1, \"大众dd_version\"大众: 1, \"大众dd_object_type\公众: \"大众Schema\"大众, \"大众dd_object\"大众: { \"大众name\公众: \"大众sbtest\"大众, \"大众default_collation_id\"大众: 33, \"大众created\"大众: 0, \"大众last_altered\"大众: 0

}

}

可以看到在库目录下只有ibd文件,并没有frm文件,而在数据目录下,相应的天生了一个SDI文件,来描述这个sbtest库的信息。

我们再来看看创建一个MYISAM引擎的表:

mysql> create database my;Query OK, 1 row affected (0.00 sec)

mysql> use myDatabase changed

mysql> create table t1 (a int, b varchar(320)) engine=myisam;Query OK, 0 rows affected (0.00 sec)

$ls my/

t1_435.SDI t1.MYD t1.MYI{ \公众sdi_version\"大众: 1, \公众dd_version\公众: 1, \"大众dd_object_type\公众: \"大众Table\"大众, \公众dd_object\"大众: { \"大众name\公众: \"大众t1\"大众, \"大众mysql_version_id\"大众: 80000, \公众created\"大众: 20161005201935, \"大众last_altered\"大众: 20161005201935, \公众options\公众: \"大众avg_row_length=0;key_block_size=0;keys_disabled=0;pack_record=1;stats_auto_recalc=0;stats_sample_pages=0;\"大众, \"大众columns\"大众: [

{ \"大众name\公众: \"大众a\"大众, \"大众type\"大众: 4, \"大众is_nullable\公众: true, \公众is_zerofill\公众: false, \公众is_unsigned\"大众: false, \"大众is_auto_increment\"大众: false, \"大众is_virtual\"大众: false, \"大众hidden\"大众: false, \"大众ordinal_position\公众: 1, \"大众char_length\"大众: 11, \"大众numeric_precision\"大众: 10, \"大众numeric_scale\"大众: 0, \"大众datetime_precision\"大众: 0, \"大众has_no_default\"大众: false, \公众default_value_null\"大众: true, \"大众default_value\公众: \"大众\"大众, \"大众default_option\"大众: \"大众\"大众, \"大众update_option\"大众: \公众\"大众, \"大众comment\"大众: \"大众\"大众, \"大众generation_expression\公众: \公众\公众, \"大众generation_expression_utf8\"大众: \公众\公众, \公众options\"大众: \"大众interval_count=0;\"大众, \"大众se_private_data\"大众: \"大众\公众, \"大众column_key\公众: 1, \"大众column_type_utf8\"大众: \"大众int(11)\"大众, \公众elements\"大众: [], \"大众collation_id\"大众: 33

},

{ \"大众name\"大众: \"大众b\"大众, \公众type\公众: 16, \"大众is_nullable\公众: true, \"大众is_zerofill\"大众: false, \"大众is_unsigned\"大众: false, \公众is_auto_increment\公众: false, \"大众is_virtual\"大众: false, \"大众hidden\"大众: false, \"大众ordinal_position\公众: 2, \公众char_length\"大众: 960, \公众numeric_precision\"大众: 0, \公众numeric_scale\"大众: 0, \公众datetime_precision\公众: 0, \"大众has_no_default\"大众: false, \"大众default_value_null\公众: true, \"大众default_value\"大众: \公众\"大众, \公众default_option\公众: \"大众\公众, \"大众update_option\公众: \"大众\"大众, \"大众comment\公众: \"大众\"大众, \"大众generation_expression\"大众: \"大众\"大众, \"大众generation_expression_utf8\公众: \"大众\"大众, \公众options\"大众: \"大众interval_count=0;\公众, \"大众se_private_data\公众: \"大众\公众, \公众column_key\公众: 1, \公众column_type_utf8\"大众: \公众varchar(320)\"大众, \"大众elements\公众: [], \"大众collation_id\公众: 33

}

], \"大众schema_ref\"大众: \公众my\公众, \公众hidden\"大众: false, \公众se_private_id\"大众: 18446744073709551615, \"大众engine\"大众: \"大众MyISAM\"大众, \"大众comment\公众: \"大众\"大众, \"大众se_private_data\"大众: \"大众\"大众, \"大众row_format\公众: 2, \"大众partition_type\"大众: 0, \"大众partition_expression\"大众: \"大众\公众, \公众default_partitioning\"大众: 0, \公众subpartition_type\"大众: 0, \公众subpartition_expression\"大众: \公众\"大众, \"大众default_subpartitioning\公众: 0, \公众indexes\公众: [], \"大众foreign_keys\"大众: [], \"大众partitions\"大众: [], \"大众collation_id\"大众: 33

}

}

这里我们创建了一个MyISAM表t1,相应的一个SDI文件被创建,文件中以JSON的格式记录了该表的详细信息。
根据官方文件的描述,这个文件的存在是为了一个还未完备实现的功能。

新的Information Schema定义

一些新IS表利用View进行了重新设计,紧张包括这些表:

CHARACTER_SETS

COLLATIONS

COLLATION_CHARACTER_SET_APPLICABILITY

COLUMNS

KEY_COLUMN_USAGE

SCHEMATA

STATISTICS

TABLES

TABLE_CONSTRAINTS

VIEWS

#例如SCHEMATA

mysql> show create table information_schema.schemata\G

1. row View: SCHEMATA Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `information_schema`.`SCHEMATA` AS select `cat`.`name` AS `CATALOG_NAME`,`sch`.`name` AS `SCHEMA_NAME`,`cs`.`name` AS `DEFAULT_CHARACTER_SET_NAME`,`col`.`name` AS `DEFAULT_COLLATION_NAME`,NULL AS `SQL_PATH` from (((`mysql`.`schemata` `sch` join `mysql`.`catalogs` `cat` on((`cat`.`id` = `sch`.`catalog_id`))) join `mysql`.`collations` `col` on((`sch`.`default_collation_id` = `col`.`id`))) join `mysql`.`character_sets` `cs` on((`col`.`character_set_id` = `cs`.`id`))) where can_access_database(`sch`.`name`)character_set_client: utf8collation_connection: utf8_general_ci1 row in set (0.01 sec)

也便是说,虽然DD系统表被隐蔽不可见了,但你依然可以通过视图得到大部分信息。
这种办法实际上大大加快了IS表的查询速率,转换成物理表的查询后,将无需为每个IS表的查询创建临时表(临时表的操作包含了server层创建frm, 引擎层获取数据or须要锁保护的全局数据)。
其余优化器也能为IS表的查询选择更好的实行操持(例如利用系统表上的索引进行查询)。

官方对此做了测试,结果显示对IS表的查询性能大幅度提升,官方博客传送门:

MySQL 8.0: Improvements to Information_schema

MySQL 8.0: Scaling and Performance of INFORMATION_SCHEMA

新选项: information_schema_stats: CACHED | LATEST

目前表的元数据信息缓存在statistics及tables表中以加速对IS表的查询性能。
你可以通过参数information_schema_stats来直接读取已经缓存到内存的数据(cached),还是从存储引擎中获取最新的数据(latest). 很显然后者要慢一点。

而从is库下,可以看到对应两种表:TABLES及TABLES_DYNAMIC, 以及STATISTICS及STATISTICS_DYNAMIC。
当被设置为LATEST时,就会去从_DYNAMIC表中去读取数据。

该选项也会影响到SHOW TABLES等语句的行为。

Data Dictionary Cache

数据词典的构造发生巨大的变革后,相应的对付内存数据词典Cache也做改动,

mysql> show variables like '%defin%';

+---------------------------------+-------+| Variable_name | Value |

+---------------------------------+-------+| schema_definition_cache | 256 |

| stored_program_definition_cache | 256 |

| table_definition_cache | 1400 |

| tablespace_definition_cache | 256 |

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

4 rows in set (0.00 sec)

tablespace_definition_cache: tablespace cache的大小,存储了tablespace的定义. 一个tablespace中可能包含多个table。

table_definition_cache:

stored_program_definition_cache: 存储过程&&function的定义cache.

schema_definition_cache: 存储schema定义的cache

hardcode的字符集cache:

character set definition cache partition: Stores character set definition objects and has a hardcoded object limit of 256.collation definition cache partition: Stores collation definition objects and has a hardcoded object limit of 256.

系统表变革

和权限干系的表转换成InnoDB引擎

// 包括:user, db, tables_priv, columns_priv, procs_priv, proxies_priv

// 官方博客先容

func表转换成InnoDB事务表

// 基于此变革,对function的操作(例如CREATE FUNCTION或者DROP FUNCTION, 或者用户定义的UDF)可能会导致一次隐式提交

mysql库下的routine表及event表不再利用,这些信息被存储到新的DD table中,并且在mysql库下是不可见的。

外键系统表

// 利用两个不可见的系统表foreign_keys和foreign_key_column_usage来存储外键信息

// 由于这两个别系表不可见,你须要通过IS库下的REFERENTIAL_CONSTRAINTS和KEY_COLUMN_USAGE表来得到外键信息

// 引入的不兼容:foreign key的名字不可以超过64个字符(之前版本是许可的)

源码概览

我们回到源代码目录下,大量的新代码文件被引入,以从server层管理New DD,紧张定义了一系列统一的API,代码存于sql/dd目录下,函数和类定义在namespace dd下

针对不同的元数据分别定义了不同的类及其继续关系:

namespace dd {

Weak_object

Entity_object

Dictionary_object

Tablespace

Schema

Event

Routine

Function Procedure

Charset

Collation

Abstract_table

Table

Spatial_reference_system

Index_stat

View

Table_stat

Partition

Trigger

Index

Foreign_key

Parameter

Column

Partition_index

Partition_value

View_routine

View_table

Tablespace_file

Foreign_key_element

Index_element

Column_type_element

Parameter_type_element

Object_table

Dictionary_object_table

Object_type

Object_table_definition }

数据词典Cache管理类:

dd::cache { dd::cache::Dictionary_client

Object_registry

Element_map

Multi_map_base

Local_multi_map

Shared_multi_map

Cache_element

Free_list

Shared_dictionary_cache

Storage_adapter}

mysql库存储的是系统表,但通过show tables命令,我们只能看到37个表,而从磁盘来看mysql目录下ibd文件远远超过37个,这意味着有些系统表对用户是不可见的,这些表也是用于管理核心数据词典信息,不可见的缘故原由是避免用户不恰当的操作。
(当然也不用除未来这一行为发生变革),关于这些表的访问,在目录sql/dd/impl/tables/中进行了接口定义,这些隐蔽的表包括:

$grep 'std::string s_table_name' sql/dd/impl/tables/ | awk '{ print $4}'s_table_name(\公众catalogs\公众);

s_table_name(\公众character_sets\"大众);

s_table_name(\"大众collations\"大众);

s_table_name(\公众columns\"大众);

s_table_name(\"大众column_type_elements\公众);

s_table_name(\"大众events\公众);

s_table_name(\"大众foreign_key_column_usage\"大众);

s_table_name(\公众foreign_keys\公众);

s_table_name(\"大众index_column_usage\公众);

s_table_name(\公众indexes\公众);

s_table_name(\"大众index_partitions\公众);

s_table_name(\"大众index_stats\"大众);

s_table_name(\"大众parameters\"大众);

s_table_name(\"大众parameter_type_elements\"大众);

s_table_name(\"大众routines\"大众);

s_table_name(\公众schemata\公众);

s_table_name(\"大众st_spatial_reference_systems\公众);

s_table_name(\"大众table_partitions\"大众);

s_table_name(\"大众table_partition_values\公众);

s_table_name(\公众tables\"大众);

s_table_name(\"大众tablespace_files\公众);

s_table_name(\"大众tablespaces\"大众);

s_table_name(\公众table_stats\"大众);

s_table_name(\"大众triggers\"大众);

s_table_name(\"大众version\"大众);

s_table_name(\公众view_routine_usage\"大众);

s_table_name(\公众view_table_usage\"大众);

我们以对一个表的常见操作为例,看看个中一些代码是如何被调用的。

(由于New DD的代码改动很大,干系的worklog有几十个,笔者通过测试+代码debug的办法第一步先熟习代码,记录的比较缭乱)

库级操作

创建database

mysql> create database db1;Query OK, 1 row affected (2.87 sec)

mysql> create database db2;Query OK, 1 row affected (3.05 sec)

入口函数:mysql_create_db

-- 创建database目录

-- 构建binlog并写入文件

-- 调用DD API接口: dd::create_schema

构建工具dd::Schema 存储到数据词典中mysql.schemata表中,干系堆栈:

``` dd::create_schema

|--> dd::cache::Dictionary_client::store<dd::Schema>

|--> dd::cache::Storage_adapter::store<dd::Schema>

|--> dd::Weak_object_impl::store

|--> dd::Raw_new_record::insert

Note: schemata表对用户是不可见的

mysql> desc schemata; ERROR 3554 (HY000): Access to system table 'mysql.schemata' is rejected.

```

创建并存储当前库的信息到SDI文件中,sdi文件命名以库名为前缀,堆栈如下

``` dd::create_schema

|--> dd::store_sdi

|--> dd::sdi_file::store

|--> write_sdi_file

```

成功则commit,失落败则rollback

修正database

mysql> alter database db1 default charset gbk;

Query OK, 1 row affected (2 min 17.54 sec)

入口函数: mysql_alter_db

-- 调用DD API接口: dd::alter_schema

更新数据词典信息,干系堆栈:```dd::alter_schema|--> dd::cache::Dictionary_client::update<dd::Schema>

|--> dd::cache::Dictionary_client::store<dd::Schema>

|--> dd::cache::Storage_adapter::store<dd::Schema>

|--> dd::Weak_object_impl::store

|--> dd::Raw_record::update```

更新sdi文件, 干系堆栈

```dd::alter_schema|--> dd::Sdi_updater::operator()

|--> dd::update_sdi

|--> dd::sdi_file::store

|--> write_sdi_file

```

但奇怪的是,更新后很快就删除了 ?? (8.0.0版本,why ??)

看起来sdi文件的序列号没有递增,导致文件被快速删除了,实际上的目的是创建一个新的文件,写入新的数据,然后将老的SDI删掉ref: http://bugs.mysql.com/bug.php?id=83281

-- 写Binlog

show databases

mysql> show databases;

+--------------------+| Database |

+--------------------+| db1 |

| db2 |

| information_schema |

| mysql |

| performance_schema || sys |

+--------------------+6 rows in set (1.40 sec)

实行该命令时,实际上会对其进行一个SQL转换,将其转换成一个标准的查询语句,堆栈如下:

dispatch_command

|-->mysql_parse

|-->parse_sql

|-->MYSQLparse

|--> dd::info_schema::build_show_databases_query

转换后的SQL类似:

SELECT SCHEMA_NAME as `Database`, FROM information_schema.schemata;

由于直接从系统表中读取, 这意味着在数据目录下创建一个文件夹将不会当作新的数据库目录。

删除database

mysql> drop database db2;

Query OK, 0 rows affected (1 min 1.86 sec)

-- 删除干系文件

-- 删除系统表mysql/schemata中记录

mysql_rm_db

|--> dd::drop_schema

|--> dd::cache::Dictionary_client::drop<dd::Schema>

|-->dd::cache::Storage_adapter::drop<dd::Schema>

|--> dd::Weak_object_impl::drop

|--> dd::Raw_record::drop

|--> handler::ha_delete_row

表级操作

创建表

mysql> create table t1 (a int primary key, b int, c int, key(b));

Query OK, 0 rows affected (7 min 12.29 sec)

入口函数:

mysql_create_table_no_lock|--> create_table_impl

|--> rea_create_table

-- 先在dd中插入新的记录(dd::create_table --> dd::create_dd_user_table)

// 根据建表语句初始化dd::Table 工具,包括表的列定义,各个属性和选项,索引定义

// 存到系统表中

```

dd::create_dd_user_table

|--> dd::cache::Dictionary_client::storedd::Table

|-->dd::cache::Storage_adapter::storedd::Table

|-->dd::Weak_object_impl::store

// 先插入到mysql/tables系统表中

// 再插入到其他系统表中,如\公众mysql/columns\公众,

|-->dd::Table_impl::store_children

|--> dd::Abstract_table_impl::store_children // mysql/columns

|--> dd::Collection<dd::Column>::store_items

|--> Weak_object_impl::store

|-->dd::Collection<dd::Index>::store_items // mysql/indexes

|--> dd::Weak_object_impl::store

|-->dd::Index_impl::store_children

|--> dd::Collection<dd::Index_element>::store_items // mysql/index_column_usage```

-- 然后再创建引擎文件

Open table

-- 将实例重启后,然后再打开表,表定义第一次载入内存,须要先去访问系统表拿到表定义:

open_and_process_table

|-->open_table

|-->get_table_share_with_discover

|-->get_table_share

|-->open_table_def // 先看schema是否存在,并从系统表`mysql/schemata`载入内存cache中

|-->dd::schema_exists

|--> dd::cache::Dictionary_client::acquire<dd::Schema>

|-->dd::cache::Dictionary_client::acquire<dd::Item_name_key, dd::Schema>

|-->dd::cache::Shared_dictionary_cache::get<dd::Item_name_key, dd::Schema>

|-->dd::cache::Shared_dictionary_cache::get_uncached<dd::Item_name_key, dd::Schema>

|-->dd::cache::Storage_adapter::get<dd::Item_name_key, dd::Schema>

|-->dd::Raw_table::find_record

// 再获取表的定义并从系统表mysql/tables载入

|-->dd::abstract_table_type

|-->dd::cache::Dictionary_client::acquire<dd::Abstract_table>

|-->dd::cache::Dictionary_client::acquire<dd::Item_name_key, dd::Abstract_table>

|-->dd::cache::Shared_dictionary_cache::get<dd::Item_name_key, dd::Abstract_table>

|-->dd::cache::Shared_dictionary_cache::get_uncached<dd::Item_name_key, dd::Abstract_table>

|-->dd::cache::Storage_adapter::get<dd::Item_name_key, dd::Abstract_table>

|--> dd::Raw_table::find_record

// 获取表上的属性信息

|-->Dictionary_object_table_impl::restore_object_from_record

|-->dd::Table_impl::restore_children

|-->dd::Abstract_table_impl::restore_children

// 从mysql/columns系统表得到列信息

|-->dd::Collection<dd::Column>::restore_items<dd::Abstract_table_impl> // 从mysql/indexs系统表得到索引信息

|-->dd::Collection<dd::Index>::restore_items<dd::Table_impl> //从mysql/index_column_usage获取索引信息

|-->dd::Collection<dd::Index_element>::restore_items<dd::Index_impl> // 从mysql/foreign_keys得到外键信息

|-->dd::Collection<dd::Foreign_key>::restore_items<dd::Table_impl> // 从mysql/table_partitions得到分区信息

|-->dd::Collection<dd::Partition>::restore_items<dd::Table_impl> //从\"大众mysql/triggers得到触发器信息

|-->dd::Collection<dd::Trigger>::restore_items<dd::Table_impl>

干系WorkLog

WL#6379: Schema definitions for new DD

WL#6380: Formulate framework for API for DD

WL#6381: Handler API changes for new dictionary

WL#6382: Define and Implement API for Table objects

WL#6383: Define and Implement API for Triggers

WL#6384: Define and Implement API for Stored Routines

WL#6385: Define and Implement API for Schema

WL#6387: Define and Implement API for Tablespaces

WL#6388: Define and Implement API for Events

WL#6389: Define and Implement API for Views

WL#6390: Use new DD API for handling non-partitioned tables

WL#6391: Protect Data Dictionary tables

WL#6392: Upgrade to Transactional Data Dictionary

WL#6394: Bootstrap code for new DD

WL#6416: InnoDB: Remove the use of .isl files

WL#6599: New Data Dictionary and I_S integration

WL#6929: Move FOREIGN KEY constraints to the global data dictionary

WL#7053: InnoDB: Provide storage for tablespace dictionary

WL#7066: External tool to extract InnoDB tablespace dictionary information

WL#7069: Provide data dictionary information in serialized form

WL#7167: Change DDL to update rows for view columns in DD.COLUMNS and other dependent values.

WL#7284: Implement common code for different DD APIs

WL#7464: InnoDB: provide a way to do non-locking reads

WL#7488: InnoDB startup refactoring

WL#7630: Define and Implement API for Table Partition Info

WL#7771: Make sure errors are properly handled in DD API

WL#7784: Store temporary table metadata in memory

WL#7836: Use new DD API for handling partitioned tables

WL#7896: Use DD API to work with triggers

WL#7897: Use DD API to work with stored routines

WL#7898: Use DD API to work with events

WL#7907: Runtime: Use non-locking reads for DD tables under I_S view.

WL#8150: Dictionary object cache

WL#8433: Separate DD commands from regular SQL queries in the parser grammar

WL#8980: Move UDF table from MyISAM to Transactional Storage

WL#9045: Make user management DDLs atomic

标签:

相关文章

letv登录php技巧_乐视网独播刀剑神域人气旺

《刀剑神域》讲述了一个发生在网络角色扮演游戏“刀剑神域”中的奇妙故事。参加了游戏的主人公桐人得知了“在游戏中去世去的人在现实中也会...

SEO优化 2024-12-14 阅读0 评论0

php反射hasmethod技巧_php反射机制用法详解

面向工具编程中工具被授予了自省的能力,而这个自省的过程便是反射。反射,直不雅观理解便是根据到达地找到出发地和来源。比如,一个光秃秃...

SEO优化 2024-12-14 阅读0 评论0