皮皮网

【rtsp源码 下载】【golang 源码开源】【雷达分屏源码】innodb源码

2024-11-23 12:59:16 来源:看源码经验

1.MySql轻松入门系列——第二站 使用visual studio 对mysql进行源码级调试
2.MySQL 核心模块揭秘 | 12 期 | 创建 savepoint
3.MySQL的源码三种模式简介mysql三种模式
4.Mysql InnoDB和MyISAM的区别
5.MySQL技术内幕:InnoDB存储引擎目录
6.源码详解系列(四) ------ DBCP2的使用和分析(包括JNDI和JTA支持)已停更

innodb源码

MySql轻松入门系列——第二站 使用visual studio 对mysql进行源码级调试

       在探索MySQL世界的过程中,有些同学希望更深入地了解如何在Visual Studio中进行源码级调试。源码不用担心,源码让我们一步步来。源码

       必备工具

       MySQL是源码用C++编写的,要在Windows上编译,源码rtsp源码 下载需要几个关键工具:CMake用于生成可打开的源码解决方案,如MySQL.sln;Boost是源码强大的C++库,Bison是源码用于解析MySQL语法规则的工具;当然,选择适合自己版本的源码MySQL源码(如5.7.)也是必不可少的。

       详细安装步骤

       安装过程需要细心,源码特别是源码Bison,务必避免默认路径中的源码空格问题,以免后续VS编译受阻。源码安装CMake和Bison时选择自定义路径,源码例如C:\2\GnuWin,确保它们的bin文件路径被添加到环境变量中。接下来解压mysql-5.7..zip,构建项目。

       编译与调试

       使用CMake编译MySQL源码,当看到Build files written to: C:/2/mysql-5.7./brelease,说明成功生成.sln文件。用Visual Studio 打开MySql.Sln,耐心等待十几分钟,编译成功后即可进行下一步。

       启动MySQL并调试

       首先,开启MySQL的调试模式,修改mysqld.cc中的test_lc_time_sz方法。然后,在Visual Studio的命令行参数中加入--console --initialize,开始调试。可能会遇到编码问题,解决后,golang 源码开源输入默认密码zJDE>IC5o+ya,连接到MySQL并修改密码。

       追踪write_row

       在上一篇中提到的write_row是一个虚方法,通过实际调试,我们可以看到它在ha_innodb.cc的实现。设置断点,执行insert操作,可以看到代码进入ha_innodb::write_row方法,深入查看局部变量和调用堆栈,验证之前的理论。

       总结

       通过一整天的努力,我们掌握了在Visual Studio中对MySQL源码进行调试的技巧。记住,每一步都可能是个挑战,但只有亲自动手,才能真正理解MySQL的运作机制。希望这些经验能帮助你避免一些常见的坑,祝你在源码的世界里探索得更深入!

MySQL 核心模块揭秘 | 期 | 创建 savepoint

       回滚操作,除了回滚整个事务,还可以部分回滚。部分回滚,需要保存点(savepoint)的协助。本文我们先看看保存点里面都有什么。

       作者:操盛春,爱可生技术专家,公众号『一树一溪』作者,专注于研究 MySQL 和 OceanBase 源码。 爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源

       本文基于 MySQL 8.0. 源码,存储引擎为 InnoDB。雷达分屏源码

       InnoDB 的事务对象有一个名为undo_no 的属性。事务每次改变(插入、更新、删除)某个表的一条记录,都会产生一条 undo 日志。这条 undo 日志中会存储它自己的序号。这个序号就来源于事务对象的 undo_no 属性。

       也就是说,事务对象的 undo_no 属性中保存着事务改变(插入、更新、删除)某个表中下一条记录产生的 undo 日志的序号。

       每个事务都维护着各自独立的 undo 日志序号,和其它事务无关。

       每个事务的 undo 日志序号都从 0 开始。事务产生的第 1 条 undo 日志的序号为 0,第 2 条 undo 日志的序号为 1,依此类推。

       InnoDB 的 savepoint 结构中会保存创建 savepoint 时事务对象的 undo_no 属性值。

       我们通过 SQL 语句创建一个 savepoint 时,server 层、binlog、InnoDB 会各自创建用于保存 savepoint 信息的结构。

       server 层的 savepoint 结构是一个SAVEPOINT 类型的对象,主要属性如下:

       binlog 的 savepoint 结构很简单,是一个 8 字节的整数。这个整数的值,是创建 savepoint 时事务已经产生的 binlog 日志的字节数,也是接下来新产生的 binlog 日志写入 trx_cache 的 offset。

       为了方便介绍,我们把这个整数值称为binlog offset。

       InnoDB 的 savepoint 结构是一个trx_named_savept_t 类型的对象,主要属性如下:

       创建 savepoint 时,clickhouse 源码导读server 层会分配一块 字节的内存,除了存放它自己的 SAVEPOINT 对象,还会存放 binlog offset 和 InnoDB 的 trx_named_savept_t 对象。

       server 层的 SAVEPOINT 对象占用这块内存的前 字节,InnoDB 的 trx_named_savept_t 对象占用中间的 字节,binlog offset 占用最后的 8 字节。

       客户端连接到 MySQL 之后,MySQL 会分配一个专门用于该连接的用户线程。

       用户线程中有一个m_savepoints 链表,用户创建的多个 savepoint 通过 prev 属性形成链表,m_savepoints 就指向最新创建的 savepoint。

       server 层创建 savepoint 之前,会按照创建时间从新到老,逐个查看链表中是否存在和本次创建的 savepoint 同名的 savepoint。

       如果在用户线程的 m_savepoints 链表中找到了和本次创建的 savepoint 同名的 savepoint,需要先删除 m_savepoints 链表中的同名 savepoint。

       找到的同名 savepoint,是 server 层的SAVEPOINT 对象,它后面的内存区域分别保存着 InnoDB 的 trx_named_savept_t 对象、binlog offset。

       binlog 是个老实孩子,乖乖的把 binlog offset 写入了 server 层为它分配的内存里。删除同名 savepoint 时,不需要单独处理 binlog offset。

       InnoDB 就不老实了,虽然 server 层也为 InnoDB 的 trx_named_savept_t 对象分配了内存,但是 InnoDB 并没有往里面写入内容。

       事务执行过程中,用户每次创建一个 savepoint,InnoDB 都会创建一个对应的 trx_named_savept_t 对象,并加入 InnoDB 事务对象的 trx_savepoints 链表的末尾。

       因为 InnoDB 自己维护了一个存放 savepoint 结构的visualbasic源码大全链表,server 层删除同名 savepoint 时,InnoDB 需要找到这个链表中对应的 savepoint 结构并删除,流程如下:

       InnoDB 从事务对象的 trx_savepoints 链表中删除 trx_named_savept_t 对象之后,server 层接着从用户线程的 m_savepoints 链表中删除 server 层的SAVEPOINT 对象,也就连带着清理了 binlog offset。

       处理完查找、删除同名 savepoint 之后,server 层就正式开始创建 savepoint 了,这个过程分为 3 步。

       第 1 步,binlog 会生成一个 Query_log_event。

       以创建名为test_savept 的 savepoint 为例,这个 event 的内容如下:

       binlog event 写入 trx_cache 之后,binlog offset 会写入 server 层为它分配的 8 字节的内存中。

       第 2 步,InnoDB 创建 trx_named_savept_t 对象,并放入事务对象的 trx_savepoints 链表的末尾。

       trx_named_savept_t 对象的 name 属性值是 InnoDB 的 savepoint 名字。这个名字是根据 server 层为 InnoDB 的 trx_named_savept_t 对象分配的内存的地址计算得到的。

       trx_named_savept_t 对象的savept 属性,是一个 trx_savept_t 类型的对象。这个对象里保存着创建 savepoint 时,事务对象中 undo_no 属性的值,也就是下一条 undo 日志的序号。

       第 3 步,把 server 层的 SAVEPOINT 对象加入用户线程的 m_savepoints 链表的尾部。

       server 层会创建一个SAVEPOINT 对象,用于存放 savepoint 信息。

       binlog 会把binlog offset 写入 server 层为它分配的一块 8 字节的内存里。

       InnoDB 会维护自己的 savepoint 链表,里面保存着trx_named_savept_t 对象。

       如果 m_savepoints 链表中存在和本次创建的 savepoint 同名的 savepoint, 创建新的 savepoint 之前,server 层会从链表中删除这个同名的 savepoint。

       server 层创建的 SAVEPOINT 对象会放入m_savepoints 链表的末尾。

       InnoDB 创建的 trx_named_savept_t 对象会放入事务对象的trx_savepoints 链表的末尾。

MySQL的三种模式简介mysql三种模式

       MySQL的三种模式简介

       MySQL 是一种开放源代码的关系型数据库管理系统,可用于处理大量数据。MySQL的三种模式是:MyISAM、InnoDB 和 MEMORY。这些模式具有不同的特性和用途,因此在选择模式时应了解其优缺点。

       1. MyISAM模式

       MyISAM 是 MySQL 最常用的模式之一,它最适用于读操作较多的系统。MyISAM 对于大量的读操作具有良好的表现,但不够适合写入频率很高的应用程序。

       下面是使用 MyISAM 模式创建一张表的示例:

       CREATE TABLE `mytable` (

        `id` int() NOT NULL AUTO_INCREMENT,

        `name` varchar() NOT NULL,

        PRIMARY KEY (`id`)

       ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

       2. InnoDB 模式

       InnoDB 是 MySQL 模式中的另一个流行选项。它适用于需要频繁写入的应用程序场景。InnoDB 是一个支持事务处理、外键约束和异常处理的存储引擎。它还支持行级锁定,这意味着多个用户可以同时访问同一数据表,而不会产生冲突。

       下面是使用 InnoDB 模式创建一张表的示例:

       CREATE TABLE `mytable` (

        `id` int() NOT NULL AUTO_INCREMENT,

        `name` varchar() NOT NULL,

        PRIMARY KEY (`id`)

       ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

       3. MEMORY 模式

       MEMORY 模式是 MySQL 中的一种高速缓存存储引擎。与 MyISAM 和 InnoDB 不同,MEMORY 模式将数据存储在 RAM 中,而不是硬盘。这使得存储和检索数据的速度非常快,但是,当系统发生崩溃或服务器被关闭时,数据将会丢失。

       下面是使用 MEMORY 模式创建一张表的示例:

       CREATE TABLE `mytable` (

        `id` int() NOT NULL AUTO_INCREMENT,

        `name` varchar() NOT NULL,

        PRIMARY KEY (`id`)

       ) ENGINE=MEMORY DEFAULT CHARSET=utf8;

       结论

       在选择MySQL模式时,要根据应用的性质和需求来选择。如果很少进行写操作,可以使用 MyISAM,如果需要处理大量事务,可以选择 InnoDB。如果需要处理临时数据,可以使用 MEMORY 存储引擎。

       MySQL模式的选择改变了 MySQL 服务器的性能和特性。在实施 MySQL 数据库时,应始终选择最适合应用程序的存储引擎。

Mysql InnoDB和MyISAM的区别

       ã€€ã€€InnoDB和MyISAM是在使用MySQL最常用的两个表类型,各有优缺点,视具体应用而定。基本的差别为:MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持。MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持已经外部键等高级数据库功能。

       ã€€ã€€MyIASM是IASM表的新版本,有如下扩展:

       ã€€ã€€äºŒè¿›åˆ¶å±‚次的可移植性。

       ã€€ã€€NULL列索引。

       ã€€ã€€å¯¹å˜é•¿è¡Œæ¯”ISAM表有更少的碎片。

       ã€€ã€€æ”¯æŒå¤§æ–‡ä»¶ã€‚

       ã€€ã€€æ›´å¥½çš„索引压缩。

       ã€€ã€€æ›´å¥½çš„键吗统计分布。

       ã€€ã€€æ›´å¥½å’Œæ›´å¿«çš„auto_increment处理。

       ã€€ã€€ä»¥ä¸‹æ˜¯ä¸€äº›ç»†èŠ‚和具体实现的差别:

       ã€€ã€€1.InnoDB不支持FULLTEXT类型的索引。

       ã€€ã€€2.InnoDB中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含where条件时,两种表的操作是一样的。

       ã€€ã€€3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。

       ã€€ã€€4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。

       ã€€ã€€5.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。

       ã€€ã€€å¦å¤–,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”

       ã€€ã€€ä»»ä½•ä¸€ç§è¡¨éƒ½ä¸æ˜¯ä¸‡èƒ½çš„,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势.

       ã€€ã€€

       ã€€ã€€MySQL中MyISAM引擎与InnoDB引擎性能简单测试

       ã€€ã€€[硬件配置]

       ã€€ã€€CPU : AMD+ (1.8G)

       ã€€ã€€å†…å­˜: 1G/现代

       ã€€ã€€ç¡¬ç›˜: G/IDE

       ã€€ã€€[软件配置]

       ã€€ã€€OS : Windows XP SP2

       ã€€ã€€SE : PHP5.2.1

       ã€€ã€€DB : MySQL5.0.

       ã€€ã€€Web: IIS6

       ã€€ã€€[MySQL表结构]

       ã€€ã€€CREATE TABLE `myisam` (

       ã€€ã€€`id` int() NOT NULL auto_increment,

       ã€€ã€€`name` varchar() default NULL,

       ã€€ã€€`content` text,

       ã€€ã€€PRIMARY KEY (`id`)

       ã€€ã€€) ENGINE=MyISAM DEFAULT CHARSET=gbk;

       ã€€ã€€CREATE TABLE `innodb` (

       ã€€ã€€`id` int() NOT NULL auto_increment,

       ã€€ã€€`name` varchar() default NULL,

       ã€€ã€€`content` text,

       ã€€ã€€PRIMARY KEY (`id`)

       ã€€ã€€) ENGINE=InnoDB DEFAULT CHARSET=gbk;

       ã€€ã€€[数据内容]

       ã€€ã€€$name = "heiyeluren";

       ã€€ã€€$content = "MySQL支持数个存储引擎作为对不同表的类型的处理器。MySQL存储引擎包括处理事务安全表的引擎和处理非事务安全表的引擎:· MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。MyISAM在所有MySQL配置里被支持,它是默认的存储引擎,除非你配置MySQL默认使用另外一个引擎。 ·MEMORY存储引擎提供“内存中”表。MERGE存储引擎允许集合将被处理同样的MyISAM表作为一个单独的表。就像MyISAM一样,MEMORY和MERGE存储引擎处理非事务表,这两个引擎也都被默认包含在MySQL中。 释:MEMORY存储引擎正式地被确定为HEAP引擎。· InnoDB和BDB存储引擎提供事务安全表。BDB被包含在为支持它的操作系统发布的MySQL-Max二进制分发版里。InnoDB也默认被包括在所有MySQL 5.1二进制分发版里,你可以按照喜好通过配置MySQL来允许或禁止任一引擎。·EXAMPLE存储引擎是一个“存根”引擎,它不做什么。你可以用这个引擎创建表,但没有数据被存储于其中或从其中检索。这个引擎的目的是服务,在MySQL源代码中的一个例子,它演示说明如何开始编写新存储引擎。同样,它的主要兴趣是对开发者。";

       ã€€ã€€[插入数据-1] (innodb_flush_log_at_trx_commit=1)

       ã€€ã€€MyISAM 1W:3/s

       ã€€ã€€InnoDB 1W:/s

       ã€€ã€€MyISAM W:/s

       ã€€ã€€InnoDB W:/s

       ã€€ã€€MyISAM W:/s

       ã€€ã€€InnoDB W:没敢测试

       ã€€ã€€[插入数据-2] (innodb_flush_log_at_trx_commit=0)

       ã€€ã€€MyISAM 1W:3/s

       ã€€ã€€InnoDB 1W:3/s

       ã€€ã€€MyISAM W:/s

       ã€€ã€€InnoDB W:/s

       ã€€ã€€MyISAM W:/s

       ã€€ã€€InnoDB W:/s

       ã€€ã€€[插入数据3] (innodb_buffer_pool_size=M)

       ã€€ã€€InnoDB 1W:3/s

       ã€€ã€€InnoDB W:/s

       ã€€ã€€InnoDB W:/s

       ã€€ã€€[插入数据4] (innodb_buffer_pool_size=M, innodb_flush_log_at_trx_commit=1, set autocommit=0)

       ã€€ã€€InnoDB 1W:3/s

       ã€€ã€€InnoDB W:/s

       ã€€ã€€InnoDB W:/s

       ã€€ã€€[MySQL 配置文件] (缺省配置)

       ã€€ã€€# MySQL Server Instance Configuration File

       ã€€ã€€[client]

       ã€€ã€€port=

       ã€€ã€€[mysql]

       ã€€ã€€default-character-set=gbk

       ã€€ã€€[mysqld]

       ã€€ã€€port=

       ã€€ã€€basedir="C:/mysql/"

       ã€€ã€€datadir="C:/mysql/Data/"

       ã€€ã€€default-character-set=gbk

       ã€€ã€€default-storage-engine=INNODB

       ã€€ã€€sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

       ã€€ã€€max_connections=

       ã€€ã€€query_cache_size=0

       ã€€ã€€table_cache=

       ã€€ã€€tmp_table_size=M

       ã€€ã€€thread_cache_size=8

       ã€€ã€€myisam_max_sort_file_size=G

       ã€€ã€€myisam_max_extra_sort_file_size=G

       ã€€ã€€myisam_sort_buffer_size=M

       ã€€ã€€key_buffer_size=M

       ã€€ã€€read_buffer_size=K

       ã€€ã€€read_rnd_buffer_size=K

       ã€€ã€€sort_buffer_size=K

       ã€€ã€€innodb_additional_mem_pool_size=4M

       ã€€ã€€innodb_flush_log_at_trx_commit=1

       ã€€ã€€innodb_log_buffer_size=2M

       ã€€ã€€innodb_buffer_pool_size=M

       ã€€ã€€innodb_log_file_size=M

       ã€€ã€€innodb_thread_concurrency=8

       ã€€ã€€ã€æ€»ç»“】

       ã€€ã€€å¯ä»¥çœ‹å‡ºåœ¨MySQL 5.0里面,MyISAM和InnoDB存储引擎性能差别并不是很大,针对InnoDB来说,影响性能的主要是 innodb_flush_log_at_trx_commit 这个选项,如果设置为1的话,那么每次插入数据的时候都会自动提交,导致性能急剧下降,应该是跟刷新日志有关系,设置为0效率能够看到明显提升,当然,同样你可以SQL中提交“SET AUTOCOMMIT = 0”来设置达到好的性能。另外,还听说通过设置innodb_buffer_pool_size能够提升InnoDB的性能,但是我测试发现没有特别明显的提升。

       ã€€ã€€åŸºæœ¬ä¸Šæˆ‘们可以考虑使用InnoDB来替代我们的MyISAM引擎了,因为InnoDB自身很多良好的特点,比如事务支持、存储过程、视图、行级锁定等等,在并发很多的情况下,相信InnoDB的表现肯定要比MyISAM强很多,当然,相应的在my.cnf中的配置也是比较关键的,良好的配置,能够有效的加速你的应用。

       ã€€ã€€å¦‚果不是很复杂的Web应用,非关键应用,还是可以继续考虑MyISAM的,这个具体情况可以自己斟酌。

MySQL技术内幕:InnoDB存储引擎目录

       MySQL技术深度解析:InnoDB存储引擎详解

       1. MySQL体系结构与存储引擎

        MySQL的核心是其体系结构,包括数据库和实例。存储引擎是关键组件,如InnoDB,提供了关键的功能。InnoDB以其高效和可靠性著名,其他引擎如MyISAM、NDB、Memory、Archive和Federated各有其特点。连接MySQL的方式有TCP/IP、命名管道、共享内存和Unix域套接字。

       2. InnoDB存储引擎详解

        InnoDB是MySQL的默认存储引擎,拥有后台线程和内存管理机制。MasterThread的源码分析和潜在问题也值得关注。InnoDB的关键特性包括插入缓冲、两次写操作和自适应哈希索引。启动、关闭与恢复机制是理解其运作的重要部分。新版本的InnoDBPlugin也值得学习。

       3. 深入理解InnoDB文件系统

        参数文件、日志文件(如错误、慢查询、查询和二进制日志)以及套接字、PID和表结构定义文件都对InnoDB性能至关重要。表空间、区、页和行的物理与逻辑存储结构是理解InnoDB数据存储的基础。

       4. 表设计与索引优化

        InnoDB表类型、行记录格式和分区表的详细讨论,揭示了索引策略,如B+树、聚集索引、辅助索引及哈希索引。锁机制,包括各种锁类型、锁算法和事务管理,对并发控制至关重要。

       5. 备份与恢复策略

        数据备份与恢复是数据库运维的重要环节,理解InnoDB的备份机制以及性能调优技巧,能确保数据的安全性和系统的高效运行。

       6. 源代码编译与深入学习

        对InnoDB存储引擎源代码的编译理解,能够帮助开发者更深入地掌握MySQL技术,实现定制化开发和性能优化。

源码详解系列(四) ------ DBCP2的使用和分析(包括JNDI和JTA支持)已停更

       深入剖析DBCP2的精髓,掌握连接池管理与事务支持(DBCP2),它在项目开发中的作用不容小觑。让我们一起探索它的配置、源码细节以及JNDI和JTA的支持。

       1. 环境配置

       以JDK 1.8、Maven 3.6.1、Eclipse 4.和MySQL 5.7.为平台,DBCP 2.6.0提供高效连接管理。以下是关键步骤:

       创建dbcp.properties,配置基础数据库连接信息,如driverClassName、url、字符编码和时区。

       通过BasicDataSourceFactory获取BasicDataSource实例,这是连接池的核心。

       执行SQL操作时,通过dataSource.getConnection()获取Connection对象。

       项目结构上,包括Maven项目、war打包、JUnit测试框架和必要的库依赖。

       2. 配置详解

       基础配置包括连接池大小(maxTotal、maxIdle、minIdle)和初始化数量(initialSize)。务必关注验证SQL(validationQuery)、超时时间(maxWaitMillis)和资源回收策略。

       例如,连接池配置示例:

       url=jdbc:mysql://localhost:/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true

       连接池参数如PSCache、lifo、connectionInitSqls等,务必启用testWhileIdle检测连接状态。

       3. JNDI与JTA支持

       DBCP支持JNDI获取数据源,如PerUserPoolDataSource和SharedPoolDataSource,分别针对不同的用户连接管理策略。在Tomcat 9.0.中,可通过Spring-like配置实现,如在web.xml中定义DataSource引用。

       对于JTA事务,DBCP提供BasicManagedDataSource和ManagedDataSource类,用于支持XA事务,例如在MySQL中启用innodb_support_xa。

       4. 实践与测试

       使用Atomikos的transactions-jdbc,为JTA事务提供支持,例如设置DefaultCatalog以避免资源冲突。在测试时,确保两阶段提交的正确性,如START、END、PREPARE、COMMIT和ROLLBACK。

       5. 源码洞察

       源码中,从BasicDataSource.getConnection()开始,初始化连接池,包括创建Connection对象、DataSource实例和设置相关参数。核心组件如GenericObjectPool的makeObject()方法展示了连接对象的创建逻辑。

       理解了这些,你将能更有效地利用DBCP2来优化数据库资源管理,确保应用程序的稳定性和性能。

       欲了解更多源码链接和详细教程,请参考:[源码链接] 和 [原创文章链接]

       本文由[作者]撰写,版权所有,转载请注明出处。