首页 > 代码库 > Oracle ASM学习之(1)--ASM Instance管理
Oracle ASM学习之(1)--ASM Instance管理
Oracle ASM学习之(1)--ASM Instance管理
About Oracle ASM Instances
An Oracle ASM instance is built on the same technology as an Oracle Database instance. An Oracle ASM instance has a System Global Area (SGA) and background processes that are similar to those of Oracle Database. However, because Oracle ASM performs fewer tasks than a database, an Oracle ASM SGA is much smaller than a database SGA. In addition, Oracle ASM has a minimal performance effect on a server. Oracle ASM instances mount disk groups to make Oracle ASM files available to database instances; Oracle ASM instances do not mount databases. For information about managing an Oracle ASM instance, see .
Oracle ASM is installed in the Oracle Grid Infrastructure home before Oracle Database is installed in a separate Oracle home. Oracle ASM and database instances require shared access to the disks in a disk group. Oracle ASM instances manage the metadata of the disk group and provide file layout information to the database instances.
查看ASM Instance和DBMS Instance之间的关系:
[grid@node1 ~]$ sqlplus ‘/as sysdba‘ SQL*Plus: Release 11.2.0.1.0 Production on Mon Jul 7 15:48:14 2014 Copyright (c) 1982, 2009, Oracle. All rights reserved. Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production With the Real Application Clusters and Automatic Storage Management options SQL> set linesize 120 SQL> col instance_name for a20 SQL> select group_number,INSTANCE_NAME,DB_NAME,status from v$asm_client; GROUP_NUMBER INSTANCE_NAME DB_NAME STATUS ------------ -------------------- -------- ------------ 1 +ASM1 +ASM CONNECTED 2 +ASM1 +ASM CONNECTED 2 prod1 prod CONNECTED 3 prod1 prod CONNECTED SQL> select group_number,INSTANCE_NAME,DB_NAME,status from v$asm_client GROUP_NUMBER INSTANCE_NAME DB_NAME STATUS ------------ -------------------- -------- ------------ 1 +ASM2 +ASM CONNECTED 2 +ASM2 +ASM CONNECTED 2 prod2 prod CONNECTED 3 prod2 prod CONNECTED
ASM的物理限制:
Oracle从第7版开始,每个数据库中的数据文件数一直都在增加,在Oracle 7,每个数据库只能使用1022个数据文件。现在的Oracle版本在每个表空间中支持65 533个数据文件,在一个多数据库环境中管理数千个文件是很有挑战性的。ASM允许将所有可用存储划分到磁盘组中,从而简化了存储管理。我们可以针对不同性能要求创建不同的磁盘组。例如,可以用低性能硬盘创建ASM磁盘组,以存储存档数据,而由高性能磁盘组成的ASM磁盘组可用于活动数据。
我们管理为数不多的磁盘组,ASM自动在这些磁盘组中放置数据库文件。利用ASM,可以拥有63个磁盘组,其中放置10 000个ASM磁盘,每个ASM磁盘可以存储高达2TB数据。一个磁盘组可处理100万个ASM文件。在Oracle Database 11g中,一个数据文件所支持的最大文件大小为128TB,而ASM在采用外部冗余时支持高达140PB数据,正常冗余时支持42PB,高冗余时支持15PB。
AMS INSTANCE管理:
在Oracle 11g R2中,ASM实例可以通过ASMCA、ASMCMD和SRVCTL实用工具来启动和停止。SRVCTL使用OCR中注册的启动和关闭选项来启动或停止一个ASM实例。下面的示例说明使用SRVCTL实用工具来启动和停止集群节点racnode01上的ASM实例+ASM1。
使用下面的命令启动racnodel集群节点上的ASM实例:
$srvctl start asm -n node1
使用以下命令停止racnode01集群节点上的ASM实例:
$srvctl stop asm -n node1
[grid@node1 ~]$ srvctl status asm -n node2
ASM is running on node2
[grid@node1 ~]$ srvctl status asm -n node1
ASM is running on node1
与SQL*Plus类似,也可以使用ASMCMD命令行实用工具来启动和停止ASM实例,只是其启动/关闭选项的语法有所不同。下面给出使用ASMCMD启动和停止ASM实例的示例。
使用以下命令以挂载状态启动ASM实例:
$asmcmd
ASMCMD> startup –mount
使用以下命令立即关闭ASM实例:
ASMCMD> shutdown –immediate
注意:
如果在ASM磁盘组中存储OCR和表决磁盘,那么不能单独启动或关闭Oracle RAC数据库系统中的ASM实例。必须使用crsctl命令来启动或停止CRS,它也会启动/停止ASM实例。
案例:
[grid@node2 ~]$ asmcmd ASMCMD> help asmcmd [-vV] [-a <sysasm|sysdba>] [-p] [command] The environment variables ORACLE_HOME and ORACLE_SID determine the instance to which the program connects, and ASMCMD establishes a bequeath connection to it, in the same manner as a SQLPLUS / AS SYSASM. The user must be a member of the OSASM group. Specifying the -V option prints the asmcmd version number and exits immediately. Specifying the -v option prints extra information that can help advanced users diagnose problems. Specify the -a option to choose the type of connection. There are only two possibilities: connecting as SYSASM or as SYSDBA. The default value if this option is unspecified is SYSASM. Specifying the -p option allows the current directory to be displayed in the command prompt, like so: ASMCMD [+DATA/ORCL/CONTROLFILE] > [command] specifies one of the following commands, along with its parameters. Type "help [command]" to get help on a specific ASMCMD command. commands: -------- md_backup, md_restore lsattr, setattr cd, cp, du, find, help, ls, lsct, lsdg, lsof, mkalias mkdir, pwd, rm, rmalias chdg, chkdg, dropdg, iostat, lsdsk, lsod, mkdg, mount offline, online, rebal, remap, umount dsget, dsset, lsop, shutdown, spbackup, spcopy, spget spmove, spset, startup chtmpl, lstmpl, mktmpl, rmtmpl chgrp, chmod, chown, groups, grpmod, lsgrp, lspwusr, lsusr mkgrp, mkusr, orapwusr, passwd, rmgrp, rmusr volcreate, voldelete, voldisable, volenable, volinfo volresize, volset, volstat ASMCMD> help startup startup [--nomount] [--restrict] [--pfile <pfile.ora>] Start the ASM instance. [--nomount] specifies the nomount option. [--restrict] start the instance in restricted mode. [--pfile <pfile.ora>] specifies the location of the pfile. ASMCMD> help shutdown shutdown [--immediate] [--abort] Shut down an ASM instance. [--immediate] Performs shutdown immediate. [--abort] Abort all existing operations.
如果在ASM磁盘组中存储OCR和表决磁盘,那么不能单独启动或关闭Oracle RAC数据库系统中的ASM实例。必须使用crsctl命令来启动或停止CRS,它也会启动/停止ASM实例。
尽管ASM没有数据字典,但它提供了一个存储在内存中的动态性能视图,可用于从ASM实例中提取元数据信息。下面对这些重要的动态性能视图进行简短描述。当然,它并不是一个完整的性能视图清单。因此,要了解ASM动态性能视图的完整列表,应参阅Oracle技术网络网站上的Oracle文档。
V$ASM: 这个视图显示了所连接ASM实例的实例信息。
V$ASM_DISKGROUP: 这个视图列出了在ASM中创建的磁盘组,还有元数据信息,如磁盘组的空闲空间、分配单元大小和状态。
V$ASM_DISK: 这个视图列出了在ASM中创建的磁盘,如磁盘的空闲空间、分配单元大小和状态。
V$ASM_FILE: 这个视图列出了在V$ASM_DISKGROUP视图所列磁盘组中创建的文件。
V$ASM_ALIAS: 这个视图列出了在V$ASM_FILE视图中所列ASM文件的用户友好名称。这个视图对于识别ASM文件的确切名称非常有用,因为V$ASM_FILE视图仅列出了文件号。
V$ASM_DISK_IOSTAT: 这个视图列出了V$ASM_DISKGROUP视图中所列每个磁盘的磁盘I/O性能统计信息。
V$ASM_ACFSVOLUMES: 这个视图列出了ASM动态卷的元数据信息。
V$ASM_OPERATION: 这个视图显示了当前操作,例如在V$ASM_DISKGROUP视图中所列磁盘组上发生的任何再均衡操作。这个视图对于监控ASM中的再均衡操作非常有用。
案例:
SQL> desc v$asm_diskgroup; Name Null? Type ----------------------------------------- -------- ---------------------------- GROUP_NUMBER NUMBER NAME VARCHAR2(30) SECTOR_SIZE NUMBER BLOCK_SIZE NUMBER ALLOCATION_UNIT_SIZE NUMBER STATE VARCHAR2(11) TYPE VARCHAR2(6) TOTAL_MB NUMBER FREE_MB NUMBER HOT_USED_MB NUMBER COLD_USED_MB NUMBER REQUIRED_MIRROR_FREE_MB NUMBER USABLE_FILE_MB NUMBER OFFLINE_DISKS NUMBER COMPATIBILITY VARCHAR2(60) DATABASE_COMPATIBILITY VARCHAR2(60) VOTING_FILES VARCHAR2(1) SQL> col name for a10 SQL> select GROUP_NUMBER,NAME,ALLOCATION_UNIT_SIZE,STATE,TOTAL_MB,FREE_MB from v$asm_diskgroup GROUP_NUMBER NAME ALLOCATION_UNIT_SIZE STATE TOTAL_MB FREE_MB ------------ ---------- -------------------- ----------- ---------- ---------- 1 OCR_VOTE 1048576 MOUNTED 2892 1966 2 DG1 1048576 MOUNTED 9554 5854 3 RCY1 1048576 MOUNTED 5740 5070 SQL> desc v$asm_disk; Name Null? Type ----------------------------------------- -------- ---------------------------- GROUP_NUMBER NUMBER DISK_NUMBER NUMBER COMPOUND_INDEX NUMBER INCARNATION NUMBER MOUNT_STATUS VARCHAR2(7) HEADER_STATUS VARCHAR2(12) MODE_STATUS VARCHAR2(7) STATE VARCHAR2(8) REDUNDANCY VARCHAR2(7) LIBRARY VARCHAR2(64) OS_MB NUMBER TOTAL_MB NUMBER FREE_MB NUMBER HOT_USED_MB NUMBER COLD_USED_MB NUMBER NAME VARCHAR2(30) FAILGROUP VARCHAR2(30) LABEL VARCHAR2(31) PATH VARCHAR2(256) UDID VARCHAR2(64) PRODUCT VARCHAR2(32) CREATE_DATE DATE MOUNT_DATE DATE REPAIR_TIMER NUMBER READS NUMBER WRITES NUMBER READ_ERRS NUMBER WRITE_ERRS NUMBER READ_TIME NUMBER WRITE_TIME NUMBER BYTES_READ NUMBER BYTES_WRITTEN NUMBER PREFERRED_READ VARCHAR2(1) HASH_VALUE NUMBER HOT_READS NUMBER HOT_WRITES NUMBER HOT_BYTES_READ NUMBER HOT_BYTES_WRITTEN NUMBER COLD_READS NUMBER COLD_WRITES NUMBER COLD_BYTES_READ NUMBER COLD_BYTES_WRITTEN NUMBER VOTING_FILE VARCHAR2(1) SECTOR_SIZE NUMBER FAILGROUP_TYPE VARCHAR2(7) SQL> select GROUP_NUMBER,DISK_NUMBER,NAME ,PATH,FAILGROUP,FREE_MB from v$asm_disk GROUP_NUMBER DISK_NUMBER NAME PATH FAILGROUP FREE_MB ------------ ----------- ---------- ------------------------------ ------------------------------ ---------- 2 0 ASM_DATA1 ORCL:ASM_DATA1 ASM_DATA1 2927 2 1 ASM_DATA2 ORCL:ASM_DATA2 ASM_DATA2 2927 3 0 ASM_RCY1 ORCL:ASM_RCY1 ASM_RCY1 2535 3 1 ASM_RCY2 ORCL:ASM_RCY2 ASM_RCY2 2535 1 0 OCR_VOTE1 ORCL:OCR_VOTE1 OCR_VOTE1 655 1 1 OCR_VOTE2 ORCL:OCR_VOTE2 OCR_VOTE2 656 1 2 OCR_VOTE3 ORCL:OCR_VOTE3 OCR_VOTE3 655 7 rows selected.
ASM后台进程:
由于ASM是使用RDBMS框架构建的,因此这些软件的体系结构类似于Oracle RDBMS进程的结构。ASM实例是使用各种后台进程构建的,这些进程中专属于ASM实例的一部分用来管理ASM中的磁盘组、ASM动态卷管理器和ASM集群文件系统。下面的列表显示ASM实例的后台进程,它们拥有ASM的SID:
DBMS Instance: [grid@node2 ~]$ ps -ef |grep ora_|grep -v grep oracle 3983 1 0 14:38 ? 00:00:00 ora_pmon_prod2 oracle 3985 1 0 14:38 ? 00:00:01 ora_vktm_prod2 oracle 3989 1 0 14:38 ? 00:00:00 ora_gen0_prod2 oracle 3991 1 0 14:38 ? 00:00:00 ora_diag_prod2 oracle 3993 1 0 14:38 ? 00:00:00 ora_dbrm_prod2 oracle 3995 1 0 14:38 ? 00:00:00 ora_ping_prod2 oracle 3997 1 0 14:38 ? 00:00:00 ora_psp0_prod2 oracle 3999 1 0 14:38 ? 00:00:00 ora_acms_prod2 oracle 4001 1 0 14:38 ? 00:00:04 ora_dia0_prod2 oracle 4003 1 0 14:38 ? 00:00:02 ora_lmon_prod2 oracle 4005 1 0 14:38 ? 00:00:14 ora_lmd0_prod2 oracle 4007 1 0 14:38 ? 00:00:02 ora_lms0_prod2 oracle 4011 1 0 14:38 ? 00:00:00 ora_rms0_prod2 oracle 4013 1 0 14:38 ? 00:00:00 ora_lmhb_prod2 oracle 4015 1 0 14:38 ? 00:00:00 ora_mman_prod2 oracle 4017 1 0 14:38 ? 00:00:00 ora_dbw0_prod2 oracle 4019 1 0 14:38 ? 00:00:00 ora_lgwr_prod2 oracle 4021 1 0 14:38 ? 00:00:00 ora_ckpt_prod2 oracle 4023 1 0 14:38 ? 00:00:00 ora_smon_prod2 oracle 4025 1 0 14:38 ? 00:00:00 ora_reco_prod2 oracle 4027 1 0 14:38 ? 00:00:00 ora_rbal_prod2 oracle 4029 1 0 14:38 ? 00:00:00 ora_asmb_prod2 oracle 4031 1 0 14:38 ? 00:00:01 ora_mmon_prod2 oracle 4033 1 0 14:38 ? 00:00:00 ora_mmnl_prod2 oracle 4035 1 0 14:38 ? 00:00:00 ora_d000_prod2 oracle 4037 1 0 14:38 ? 00:00:00 ora_s000_prod2 oracle 4052 1 0 14:38 ? 00:00:00 ora_mark_prod2 oracle 4077 1 0 14:39 ? 00:00:03 ora_lck0_prod2 oracle 4079 1 0 14:39 ? 00:00:00 ora_rsmn_prod2 oracle 4131 1 0 14:39 ? 00:00:03 ora_pz99_prod2 oracle 4149 1 0 14:39 ? 00:00:00 ora_gtx0_prod2 oracle 4151 1 0 14:39 ? 00:00:00 ora_rcbg_prod2 oracle 4159 1 0 14:40 ? 00:00:00 ora_qmnc_prod2 oracle 4162 1 0 14:40 ? 00:00:00 ora_pz98_prod2 oracle 4164 1 0 14:40 ? 00:00:00 ora_q000_prod2 oracle 4166 1 0 14:40 ? 00:00:00 ora_q001_prod2 oracle 4437 1 0 14:40 ? 00:00:00 ora_cjq0_prod2 oracle 4576 1 0 14:44 ? 00:00:00 ora_pz97_prod2 oracle 4715 1 0 14:45 ? 00:00:00 ora_smco_prod2 oracle 6508 1 0 15:05 ? 00:00:00 ora_w000_prod2 ASM Instance: [grid@node2 ~]$ ps -ef |grep asm_|grep -v grep grid 3421 1 0 14:37 ? 00:00:00 asm_pmon_+ASM2 grid 3423 1 0 14:37 ? 00:00:01 asm_vktm_+ASM2 grid 3427 1 0 14:37 ? 00:00:00 asm_gen0_+ASM2 grid 3429 1 0 14:37 ? 00:00:00 asm_diag_+ASM2 grid 3431 1 0 14:37 ? 00:00:00 asm_ping_+ASM2 grid 3433 1 0 14:37 ? 00:00:00 asm_psp0_+ASM2 grid 3435 1 0 14:37 ? 00:00:04 asm_dia0_+ASM2 grid 3437 1 0 14:37 ? 00:00:01 asm_lmon_+ASM2 grid 3439 1 0 14:37 ? 00:00:02 asm_lmd0_+ASM2 grid 3444 1 0 14:37 ? 00:00:00 asm_lms0_+ASM2 grid 3448 1 0 14:37 ? 00:00:00 asm_lmhb_+ASM2 grid 3450 1 0 14:37 ? 00:00:00 asm_mman_+ASM2 grid 3452 1 0 14:37 ? 00:00:00 asm_dbw0_+ASM2 grid 3454 1 0 14:37 ? 00:00:00 asm_lgwr_+ASM2 grid 3456 1 0 14:37 ? 00:00:00 asm_ckpt_+ASM2 grid 3458 1 0 14:37 ? 00:00:00 asm_smon_+ASM2 grid 3460 1 0 14:37 ? 00:00:00 asm_rbal_+ASM2 grid 3462 1 0 14:37 ? 00:00:00 asm_gmon_+ASM2 grid 3464 1 0 14:37 ? 00:00:00 asm_mmon_+ASM2 grid 3466 1 0 14:37 ? 00:00:00 asm_mmnl_+ASM2 grid 3472 1 0 14:37 ? 00:00:00 asm_lck0_+ASM2 grid 3500 1 0 14:37 ? 00:00:00 asm_asmb_+ASM2
ASM特有的这些进程。在ASM实例中创建ASM动态卷时,将会看到更多的后台进程,如VDBG、VBGn和VMB。这里将解释一些重要的ASM后台进程。
RBAL: 这是一个再均衡后台进程。它负责再均衡操作,还协调ASM磁盘恢复进程。
GMON: 这是“组监控器”后台进程。它在管理磁盘组时将磁盘组标记为“脱机”,甚至会删除这个磁盘组。
ARBn: 尽管RBAL负责协调磁盘组的再均衡,但实际上是由ARBn来执行再均衡协调的。
VMB: 这是一个“卷成员资格”后台进程,它与ASM实例一同负责集群成员资格。在创建ASM动态卷时,ASM实例会启动这个后台进程。
VDBG: 这是“卷驱动程序”后台进程。它与动态卷驱动程序一同提供卷盘区的锁定和解锁。这是一个非常重要的进程,如果被意外终止,那么它会关闭ASM实例。
VBGn: 这是“卷后台”进程。ASM实例中的VBG与操作系统卷驱动程序通信。它负责ASM与操作系统之间的消息发送。
XDMG: 这是一个Exadata自动管理器。XDMG监控所有配置的Exadate单元,以了解状态变化,例如更换了一个坏磁盘等。它的主要任务是监控不可访问的磁盘和单元,当它们再次可供访问时,启动ASM ONLINE操作。
初始化参数:
就像数据库实例一样,ASM实例需要有强制参数和可选参数。可以在数据库实例和ASM实例中设置初始化参数,但有些参数仅对ASM实例有效。以下初始化参数可以在ASM实例中设置。以“ASM_”开头的参数不能用于数据库实例。强烈建议将这些参数存储在ASM参数文件中(也称为SPFILE或注册表文件)。
INSTANCE_TYPE: 这个参数向Oracle可执行文件指明实例类型。默认情况下,Oracle可执行文件假定实例类型是一个数据库实例。这是ASM实例中的唯一强制参数。所有其他参数在没有指明时都有适当的默认参数。
ASM_POWER_LIMIT: 设置磁盘再均衡的功能限制。这个参数值默认为1,有效值为0~11。这个参数是动态的。有关再均衡的详细信息将在本章后面提供。
ASM_DISKSTRING: 一个用逗号分隔的字符串列表,它限制了ASM发现的磁盘集。这个参数接受通配符。只有那些与字符串之一匹配的磁盘才能被发现。字符串格式取决于所使用的ASM库和操作系统。ASM的标准系统库支持glob模式匹配。如果正在使用ASMLib来创建ASM磁盘,那么默认路径为ORCL:*。
CLUSTER_DATABASE: 如果集群节点上的ASM实例希望访问同一ASM磁盘,那么这个参数必须被设置为TRUE。这个参数实际地启用集群存储。必须确保集群中所有ASM实例上的这个参数都设置为相同值。
ASM_DISKGROUPS: 使用ALTER DISKGROUP ALLMOUNT语句时或者由ASM在启动时挂载的磁盘组名称列表。如果没有指定这个参数,那么除存储SPFILE、OCR和表决磁盘的ASM磁盘组之外,不会挂载其他磁盘组。这个参数是动态的,在使用服务器参数文件(SPFILE)时,不需要改变这个值。
ASM_PREFERRED_READ_FAILURE_GROUPS: 这个参数是在Oracle 11g中引入的,允许扩展集群配置中的ASM实例从本地磁盘中读取数据,而不需要总是从主副本中读取(在扩展集群配置中,每个站点都有自己的专用存储)。在Oracle 11g之前,无论本地磁盘上是否有可供使用的相同盘区,ASM总是从主副本中读取数据。这是Oracle 11g中非常受欢迎的改进,它对于提高Oracle 扩展集群的性能非常有用。在为扩展集群配置ASM时,要非常仔细地选择故障组的数量,因为这一设置会对ASM读取性能产生直接影响。
LARGE_POOL_SIZE: ASM实例使用的内部包是从大型池中执行的,因此应当将初始化参数LARGE_POOL_SIZE设置为一个大于8MB的值。至于其他缓冲区参数,可以使用其默认值。
案例:
[grid@node2 ~]$ sqlplus ‘/as sysasm‘ SQL*Plus: Release 11.2.0.1.0 Production on Mon Jul 7 15:07:27 2014 Copyright (c) 1982, 2009, Oracle. All rights reserved. Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production With the Real Application Clusters and Automatic Storage Management options SQL> show parameter spfile NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ spfile string +OCR_VOTE/node-cluster/asmpara meterfile/registry.253.8522882 53 SQL> create pfile=‘/home/grid/init+ASM2.ora‘ from spfile=‘+OCR_VOTE/node-cluster/asmparameterfile/registry.253.852288253‘; File created. [grid@node2 ~]$ cat init+ASM2.ora +ASM1.__oracle_base=‘/u01/app/oracle‘#ORACLE_BASE set from in memory value +ASM2.__oracle_base=‘/u01/app/oracle‘#ORACLE_BASE set from in memory value +ASM1.asm_diskgroups=‘DG1‘,‘RCY1‘#Manual Mount +ASM2.asm_diskgroups=‘RCY1‘,‘DG1‘#Manual Mount *.asm_power_limit=1 *.diagnostic_dest=‘/u01/app/oracle‘ *.instance_type=‘asm‘ *.large_pool_size=12M *.remote_login_passwordfile=‘EXCLUSIVE‘
ASM实例关闭类似于数据库实例关闭。在关闭一个ASM实例之前,必须先关闭使用这个ASM实例的数据库实例。当使用NORMAL、IMMEDIATE或TRANSACTIONAL关闭时,ASM会等待任何正在进行的SQL操作完成。一旦完成所有ASM SQL操作,就卸载所有磁盘组,并按照有序方式关闭ASM实例。如果任何数据库实例连接到ASM实例,SHUTDOWN命令就将返回一条错误,仍然使ASM实例保持运行状态。
在使用SHUTDOWN ABORT时,ASM实例被立即终止。它不会以有序方式卸载磁盘组。下一次启动时需要利用ASM恢复(类似于RDBMS恢复)使磁盘组的状态一致。ASM实例还有一些类似于撤销和重做的组件(具体细节将在本章后面讨论),它们支持崩溃恢复和实例恢复。
如果有任何数据库实例连接到这个ASM实例,那么数据库实例会终止,因为它不能访问由这个ASM实例管理的存储系统。
案例:
关闭ASM Instance: SQL> conn /as sysasm Connected. SQL> shutdown immediate; ORA-15097: cannot SHUTDOWN ASM instance with connected client
本文出自 “天涯客的blog” 博客,请务必保留此出处http://tiany.blog.51cto.com/513694/1435323