精华内容
下载资源
问答
  • LaTeX基础四:字体字号设置
    千次阅读
    2020-12-22 00:26:40

    1 %导言区2 \documentclass[10pt]{article} %确定normalsize大小,为可选参数,在中括号内,此为10磅,只有10,11,12磅三个选项。3

    4 \usepackage{ctex}5

    6 %自定义字体7 \newcommand{\myfont}{\textit{\textbf{\textsf{Fancy Text}}}}8

    9 %文稿区10 \begin{document}11 %字体族设置(罗马字体、无衬线字体、打印机字体)12 \textrm{Roman Family} \textsf{Sans Serif Family} \texttt{Typewriter Family}13

    14 {\rmfamily Roman Family} {\sffamily Sans Serif Family} {\ttfamily Typewriter Family}15

    16 {\sffamily who you are? you find self on everyone around. take you as the same as others!}17

    18 {\ttfamily Are you wiser than others? definitely on. in some days, my it is true. What can you achieve? a luxurious house? a brillilant car? an admirable career? who knows?}19

    20 %字体系列设置(粗细、宽度)21 \textmd{Medium Series} \textbf{Boldface Series}22

    23 {\mdseries Medium Series} {\bfseries Boldface Series}24

    25 %字体形状(直立、斜体、伪斜体、小型大写)26 \textup{Upright Shape} \textit{Italic Shape} \textsl{Slanted Shape} \textsc{Small Caps Shape}27

    28 {\upshape Upright Shape} {\itshape Italic Shape} {\slshape Slanted Shape} {\scshape Small Caps Shape}29

    30 %中文字体31 {\songti 宋体} \quad {\heiti 黑体} \quad {\fangsong 仿宋} \quad {\kaishu 楷书}32

    33 中文字体的 \textbf{粗体} 和 \textit{斜体} 。34

    35 %字体大小,根据normalsize的大小确定,normalsize 在文档类的参数决定36 {\tiny Hello}\\37 {\scriptsize Hello}\\38 {\footnotesize Hello}\\39 {\small Hello}\\40 {\normalsize Hello}\\41 {\large Hello}\\42 {\Large Hello}\\43 {\LARGE Hello}\\44 {\huge Hello}\\45 {\Huge Hello}\\46

    47 %中文字号设置命令、48 \zihao{-0} 你好!49 \zihao{5} 你好!50

    51 \myfont52

    53 \end{document}

    更多相关内容
  • CSS设置行间距和字间距

    万次阅读 2019-08-18 22:12:19
    CSS设置行间距 在CSS中通过line-height属性来实现行间距的设置,line-height的值表示的是两行文字之间基线的距离。 文字的基线,指的是如果给文字加上下划线,那么上下划线就是文字的基线。 Line-height的值设置为...

    CSS设置行间距

    CSS中通过line-height属性来实现行间距的设置,line-height的值表示的是两行文字之间基线的距离。

    文字的基线,指的是如果给文字加上下划线,那么上下划线就是文字的基线。

    Line-height的值设置为具体的数值,可以是相对数值,也可以设置为绝对数值,在静态页面中,文字大小固定时常常使用绝对数值,而对于论坛和博客这些用户可以自定义字体大小的页面,通常设置为相对数值,从而,可以随着用户自定义的字体大小改变相应的行间距。

    <html>
    <head>
        <title>
            行间距
        </title>
        <style type="text/css">
            p.one {
                font-size: 10pt;
                line-height: 8pt;
            }
    
            p.second { font-size: 18px; }
    
            p.third { font-size: 10px; }
    
            p.second, p.third { line-height: 1.5em; }
        </style>
    </head>
    
    <body>
    <p class="one">9月23日是“秋分”,我国古籍《春秋繁露、阴阳出入上下篇》中说:“秋分者,阴阳相半也,故昼夜均而寒暑平。”“秋分”的意思有二:一是太阳在这时到达黄径180.一天24小时昼夜均分,各12小时;二是按我国古代以立春、立夏、立秋、立冬为四季开始的季节划分法,秋分日居秋季90天之中,平分了秋季。</p>
    <p class="second">秋分时节,我国常见流域及其以北的广大地区,均夏侯进入了秋季,日平均气温都降到了22℃以下。北方冷气团开始具有一定的势力,大部分地区雨季刚刚结束,凉风习习,碧空万里,风和日丽,秋高气爽,丹桂飘香,蟹肥橘黄,秋分是美好宜人的时节。</p>
    <p class="third">秋季降温快的特点,使得秋收、秋耕、秋种的“三秋”大忙显得格外紧张。秋分棉花吐絮,烟叶也由绿变黄,正是收获的大好时机。华北地区已开始播种冬麦,长江流域及南部广大地区正忙着晚稻的收割,抢晴耕翻土地,准备油菜播种。</p>
    </body>
    </html>
    

    在这里插入图片描述
    代码如上,第一段文字采用了绝对数值,并且行间距设置的比文字大小还要小,因此,文字发生了部分重叠的现象。

    第二段和第三段,分别设置了不同的文字大小,但是由于使用了相对数值,因此,能够自动的调节行间距。

    CSS设置字间距

    CSS中通过letter-spacing属性来调整字间距,这个属性同样可以设置相对数值和绝对数值。

    <html>
    <head>
        <title>
            字间距
        </title>
        <style type="text/css">
            p.one {
                font-size: 10pt;
                letter-spacing: -2pt;
            }
            p.second { font-size: 18px; }
    
            p.third { font-size: 11px; }
    
            p.second, p.third { letter-spacing: .5em; }
        </style>
    </head>
    
    <body>
    <p class="one">文字间距1,负数</p>
    <p class="second">文字间距2,相对数值</p>
    <p class="third">文字间距3,相对数值</p>
    </body>
    </html>
    

    在这里插入图片描述

    代码如上,可以看到文字间距属性letter-spacing除了可以使用相对数值和绝对数值外,还可以使用负数来实现文字重叠的效果。


    div css文字字体行高行距 深入理解css行间距设置

    在文章中,要实现调节设置每行文字字体间距(行距)是使用line-height属性。
    要使得每行的文字一定的间距距离所以可以通过line-height样式实现。接下来通过DIV CSS实例实现字体文字上下间距。
    为了观察到行距设置效果,新建两个div盒子对象,分别div命名为“.divcss5-a”和“.divcss5-b”。

    1、div+css实例完整HTML代码:

    <!DOCTYPE html>
    <html>
     <head> 
      <meta charset="utf-8" /> 
      <title>divcss5 css行间距实例</title> 
      <style> 
            .divcss5-a{ line-height:22px}/* css 注释说明:设置行距行高22px */ 
            .divcss5-b{ line-height:40px}/* 设置行距行高22px */ 
        </style> 
     </head> 
     <body> 
      <div class="divcss5-a">
        我被设置行高22px
       <br /> 我行距为22培训
       <br /> DIVCSS5占位内容 
      </div> 
      <div class="divcss5-b">
        我被设置行高40px
       <br /> 我行距为40培训
       <br /> DIVCSS5占位内容 
      </div>   
     </body>
    </html>
    

    设置了两个盒子分别css行高间隔设置为22px40px,大家可以复制以上代码进行实践观察效果掌握css设置对象内字体文字上下行间距。掌握设置行间距行高代码是什么。

    2、css div行高行间距效果截图

    字体上下行间距css+div实例截图

    3、总结

    要实现上下换行文字行间距行高样式其实我们只用对文字所在对象设置line-height样式即可,一般行距值设置大于14px以上即可,根据css字体大小,css行高值不能小于字体值,不然行距设置值比字体大小值小后字体会重叠一部分,形成不兼容效果。


    https://blog.csdn.net/yenange/article/details/8251187

    https://www.cnblogs.com/fengzhentian/p/4576336.html

    https://www.cnblogs.com/nelsonlei/archive/2017/12/28/8134655.html

    展开全文
  • 、InnoDB数据结构 2.1 局部性原理 2.2 InnoDB的数据格式 三、InnoDB的格式 3.1 Compact格式 3.1.1 变长字段长度列表 3.1.2 NULL值列表 3.1.3 记录头信息 3.1.4 记录的真实数据 3.2 数据溢出问题 ...

    目录

    一、MySQL架构图

    二、InnoDB数据页结构

    2.1 局部性原理

    2.2 InnoDB的数据页格式

    三、InnoDB的行格式

    3.1 Compact行格式

    3.1.1 变长字段长度列表

    3.1.2 NULL值列表

    3.1.3 记录头信息

    3.1.4 记录的真实数据

    3.2 数据溢出问题

    3.2.1 行溢出的数据

    3.2.2 记录中的数据太多产生的溢出

    3.3 Dynamic和Compressed行格式

    四、MySQL索引底层数据结构与算法

    4.1 索引到底是什么

    4.1.1 索引的优点

    4.1.2 索引的缺点

    4.2 索引的常见模型

    4.2.1 哈希表

    4.2.2 有序表

    4.2.3 二叉树搜索树

    4.2.4 红黑树

    4.2.5 B树

    4.2.6 B+树

    4.2.7 B树与B+树的差别

    4.3 索引的代价

    4.3.1 空间上的代价

    4.3.2 时间上的代价

    4.4 索引模型总结


    一、MySQL架构图

    MySQL架构图

     

    存储引擎负责对表中的数据的进行读取和写入,常用的存储引擎有InnoDB、MyISAM、Memory等,不同的存储 引擎有自己的特性,数据在不同存储引擎中存放的格式也是不同的,比如Memory都不用磁盘来存储数据。

    在InnoDB中,数据会存储到磁盘上,在真正处理数据时需要先将数据加载到内存,表中读取某些记录时, InnoDB存储引擎不需要一条一条的把记录从磁盘上读出来,InnoDB采取的方式是:将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB,也就是说,当需要从磁盘中读数据时每一次最少将从磁盘中读取16KB的内容到内存中,每一次最少也会把内存中的16KB内容写到磁盘中。

     

    二、InnoDB数据页结构

    数据库之所以要以页结构来存储,这还要从操作系统中学的分页管理来说起。

    首先就就涉及到一个局部性原理

    2.1 局部性原理

    三种不同类型的局部性:

    • 时间局部性(Temporal Locality):如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。程序循环、堆栈等是产生时间局部性的原因。
    • 空间局部性(Spatial Locality):在最近的将来将用到的信息很可能与正在使用的信息在空间地址上是临近的。
    • 顺序局部性(Order Locality):在典型程序中,除转移类指令外,大部分指令是顺序进行的。顺序执行和非顺序执行的比例大致是5:1。此外,对大型数组访问也是顺序的。指令的顺序执行、数组的连续存放等是产生顺序局部性的原因。

     

    也就是说当CPU取到硬盘中的某一条数据或者执行了某一条指令,那么接下来大概率会再取它附近的数据或顺序执行后面的指令,或者该数据在未来的一段时间内会再次被访问,这样我们就先把该数据附近的数据全都放到一个页中,每次从硬盘往内存中取数据直接将一页的数据全部取出来放到内存,这要就将该数据附近的数据也取到了内存中,以后如果需要用就可以直接从内存中获取了,减少了IO操作,提高了性能效率。

    这个原理放到数据库中也是一样,数据库中的数据就是存到硬盘中的,但是执行查询操作,修改操作都是需要借助CPU来实现的。

    比如要查询a>10的数据,如果索引使用普通的平衡二叉搜索树结构,数据库就会把相关表中的数据一行行从硬盘读取到内存,然后再通过CPU来判断a字段是不是大于10来判断该行数据是不是我们想要的。每一次从硬盘中取出数据放到内存都是一次IO操作,这么频繁的IO操作就对导致效率极低。

     

    如果使用基于页结构来管理,过程就变成了这样:

    在硬盘中存储着以页为组织单位的索引树,查找a>10的数据,先从已经预加载到内存的根页开始判断,看看相关数据需要到哪一个子页去找,定位好了子页,进行一次IO操作,根据地址将指向的这个子页从硬盘中读取到内存中,这是一次IO操作,因为B+树是以也为组织单位,一页会有很多数据,而且还是一个多叉树,就会使树的高度很低,数的高度变低就会使IO操作大大减少。因为大量的比较操作已经在内存中的页里完成了,也就大大减少了IO操作(将数据从硬盘中读取到内存中的操作),提高了数据库的检索效率。关于B+树和在页内部的比较过程会在下文进行详解。

    操作系统页的大小默认是4K,数据库中页的大小默认时16K,也就是数据库每次从硬盘中取得数据都是以16KB为单位。

     

    2.2 InnoDB的数据页格式

    页是InnoDB管理存储空间的基本单位,一个页的大小默认是16KB。

    SHOW  GLOBAL  STATUS  like  'Innodb_page_size';

    页结构:

     

    名称

     

    中文名

     

    占用空间

     

    简单描述

     

    File Header

     

    文件头部

     

    38字节

     

    页的一些通用信息

     

    Page Header

     

    页面头部

     

    56字节

     

    数据页专有的一些信息

     

    Infimum + Supremum

     

    最小记录和最大记录

     

    26字节

     

    两个虚拟的行记录

     

    User Records

     

    用户记录

     

    不确定

     

    实际存储的行记录内容

     

    Free Space

     

    空闲空间

     

    不确定

     

    页中尚未使用的空间

     

    Page Directory

     

    页面目录

     

    不确定

     

    页中的某些记录的相对位置

     

    File Trailer

     

    文件尾部

     

    8字节

     

    校验页是否完整

     

    向页中插入数据

     

     

    • 各个数据页之间可以组成一个双向链表(就是B+树的各个页之间都按照索引值顺序用双向链表连接起来)
    • 每个数据页中的记录又可以组成一个单向链表
    • 每个数据页都会为存储在它里边儿的记录生成一个页目录,该目录页是用数组进行管理,在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录
    • 其他列(非主键)作为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录

     

    示意图:

    所以说,如果我们写select * from user where username = 'Java3y'这样没有进行任何优化的sql语句,默认会这样做:

    • 定位到记录所在的页
    • 需要遍历双向链表,找到所在的页
    • 从所在的页内中查找使用二分法查询目录找到相应的记录
    • 由于不是根据主键查询,只能遍历所在页的单链表了

    很明显,在数据量很大的情况下这样查找会很慢

     

     

    三、InnoDB的行格式

    一行记录可以以不同的格式存在InnoDB中,行格式分别是CompactRedundantDynamicCompressed行格式。

     

    我们可以在创建或修改表的语句中指定行格式:

    CREATE  TABLE   表名  (   列的信息 )  ROW_FORMAT= 行格式名称
    ALTER  TABLE  表名  ROW_FORMAT= 行格式名称

    行格式是表级别的,一个表就能设置一个行格式。

     

    3.1 Compact行格式

    由Compact行格式的示意图可以看出除了在右侧一次存储的真正的行数据,在左边还存有额外的信息。

     

    记录的额外信息

    这部分信息是服务器为了描述这条记录而不得不额外添加的一些信息,这些额外信息分为3类,分别是:

    • 变长字段长度列表
    • NULL值列表
    • 记录头信息

     

    3.1.1 变长字段长度列表

    MySQL支持一些变长的数据类型,比如VARCHAR(M)、VARBINARY(M)、TEXT类型,BLOB类型,这些数据类型修饰列称为变长字段,变长字段中存储多少字节的数据不是固定的,所以我们在存储真实数据的时候需要顺便把这些数据占用的字节数也存起来。在Compact行格式中,把所有变长字段的真实数据占用的字节长度都存放在记录的开头部位,从而形成一个变长字段长度列表。

    CHAR 是一种固定长度的类型, VARCHAR 则是一种可变长度的类型。VARCHAR(M), M 代表最大能存多少个字符。 ( MySQL5.0.3 以前是字节,以后就是字符 在新版本的mysql中char(n)和varchar(n)中的n表示的都是字符数 )
    注意:这里面存储的变长长度和字段顺序是反过来的。比如两个varchar字段在表结构的顺序是a(10),b(15)。那么在变长字段长度列表中存储的长度顺序就是15,10。是反过来的。

     

    3.1.2 NULL值列表

    Compact行格式会把可以为NULL的列统一管理起来,存一个标记为在NULL值列表中,如果表中没有允许存储 NULL 的列,则 NULL值列表也不存在了。之所以要存储NULL是因为数据都是需要对其的,如果没有标注出来NULL值得位置,就有可能在查询数据的时候出现混乱。如果使用一个特定的符号放到相应的数据位表示空置的话,虽然能达到效果,但是这样很浪费空间,所以直接就在行数据得头部开辟出一块空间专门用来记录该行数据那些是非空数据,哪些是空数据,格式如下:

    • 二进制位的值为1时,代表该列的值为NULL。
    • 二进制位的值为0时,代表该列的值不为NULL。

     

    例如:字段 a b c 其中a是主键,在某一行中存储的数依次是 a=1 b=null c=2

    那么Compact行格式中得NULL值列表中存的10。第一个1表示b是null,第二个0表示c不为null。这里之所以没有a是因为数据库会自动跳过主键,因为主键肯定是非NULL且唯一的,在NULL值列表的数据中就会自动跳过主键。

     

    3.1.3 记录头信息

    除了变长字段长度列表、NULL值列表之外,还有一个用于描述记录的记录头信息,它是由固定的5个字节组成。 5个字节也就是40个二进制位,不同的位代表不同的意思,如下表:

     

    名称

     

    大小(单

    位:bit

     

    描述

     

    预留位1

     

    1

     

    没有使用

     

    预留位2

     

    1

     

    没有使用

     

    delete_mask

     

    1

     

    标记该记录是否被删除

     

    min_rec_mask

     

    1

     

    B+树的每层非叶子节点中的最小记录都会添加该标记,目的是为了加快检索速度

     

    n_owned

     

    4

     

    表示当前记录拥有的记录数

     

    heap_no

     

    13

     

    表示当前记录在记录堆的位置信息

     

    record_type

     

    3

     

    表示当前记录的类型,0表示普通记录,1表示B+树非叶子节点记录,2表示最小记录,3表示最大记录

     

    next_record

     

    16

     

    表示下一条记录的相对位置

     

    3.1.4 记录的真实数据

    记录的真实数据除了我们自己定义的列的数据以外,还会有三个隐藏列:

     

    列名

     

    是否必须

     

    占用空间

     

    描述

     

    row_id

     

     

    6字节

     

    行ID,唯一标识一条记录

     

    transaction_id

     

     

    6字节

     

    事务ID

     

    roll_pointer

     

     

    7字节

     

    回滚指针

    实际上这几个列的真正名称其实是:DB_ROW_IDDB_TRX_IDDB_ROLL_PTR

    一个表没有手动定义主键,则会选取一个Unique键作为主键,如果连Unique键都没有定义的话,则会为表默认添加一个名为row_id的隐藏列作为主键。所以row_id是在没有自定义主键以及Unique键的情况下才会存在的。

    剩下的事务ID和回滚指针在【MySQL】MySQL的锁与事务隔离级别详解 会有讲解。

     

    3.2 数据溢出问题

    3.2.1 行溢出的数据

    VARCHAR(M)类型的列最多可以占用65535个字节。其中的M代表该类型最多存储的字符数量,如果我们使用 ascii字符集的话,一个字符就代表一个字节,我们看看VARCHAR(65535)是否可用:

    mysql>  CREATE  TABLE  varchar_size_demo(
        c  VARCHAR(65535)
     )  CHARSET=ascii  ROW_FORMAT=Compact;
    
    ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have  to  change  some  columns  to  TEXT  or  BLOBs

     报错信息表达的意思是:MySQL对一条记录占用的最大存储空间是有限制的,除BLOB或者TEXT类型的列之外, 其他所有的列(不包括隐藏列和记录头信息)占用的字节长度加起来不能超过65535个字节。这个65535个字节 除了列本身的数据之外,还包括一些其他的数据,以Compact行格式为例,比如说我们为了存储一个VARCHAR(M)类型的列,其实需要占用3部分存储空间:

    1. 真实数据
    2. 变长字段真实数据的长度
    3. NULL值标识

     

    如果该VARCHAR类型的列没有NOT NULL属性,那最多只能存储65532个字节的数据,因为变长字段的长度占用 2个字节,NULL值标识需要占用1个字节。

    mysql>  CREATE  TABLE  varchar_size_demo(
        c  VARCHAR(65532)
    )  CHARSET=ascii  ROW_FORMAT=Compact;
    
    Query OK, 0 rows affected (0.02 sec)
    
    CREATE  TABLE  
    varchar_size_demo( 
    	c  VARCHAR(65533)  not  null
    )  CHARSET=ascii  ROW_FORMAT=Compact; 
    Query OK, 0 rows affected (0.02 sec)
    

     

    3.2.2 记录中的数据太多产生的溢出

    一个页的大小一般是16KB,也就是16384字节,而一个VARCHAR(M)类型的列就最多可以存储65533个字节,这样就可能出现一个页存放不了一条记录。 在Compact和Reduntant行格式中,对于占用存储空间非常大的列,在记录的真实数据处只会存储该列的一部分数据,把剩余的数据分散存储在几个其他的页中,然后记录的真实数据处用20个字节存储指向这些页的地址(当然这20个字节中还包括这些分散在其他页面中的数据的占用的字节数),从而可以找到剩余数据所在的页。

     

    CompactReduntant行格式对于这种一个页都放不下一行数据的情况会进行分页存储,具体的格式就是在原本记录真实数据地行中先存一部分数据,然后后面添加一个指向另一个页的指针,剩余的数据就被存储到另一个页中。这称为页的扩展,格式如下:

     

     

    3.3 DynamicCompressed行格式

    这两种行格式类似于COMPACT行格式,只不过在处理行溢出数据时有点儿分歧,它们不会在记录的真实数据处存储一部分数据,而是把所有的数据都存储到其他页面中,只在记录的真实数据处存储其他页面的地址。另外, Compressed行格式会采用压缩算法对页面进行压缩。

     

    四、MySQL索引底层数据结构与算法

    4.1 索引到底是什么

    索引是帮助MySQL高效获取数据的排好序数据结构,索引存储在文件里。

    一句话简单来说,索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。一本 500 页的书,如果你想快速找到其中的某一个知识点,在不借助目录的情况下,那我估计你可得找一会儿。同样,对于数据库的表而言,索引其实就是它的“目录”。

     

    在讲解索引常见模型之前先复习一下磁盘存取原理:

    上文说过,索引一般以文件形式存储在磁盘上,索引检索需要磁盘I/O操作。与主存不同,磁盘I/O存在机械运动耗费,因此磁盘I/O的时间消耗是巨大的。

    一个磁盘由大小相同且同轴的圆形盘片组成,磁盘可以转动(各个磁盘必须同步转动)。在磁盘的一侧有磁头支架,磁头支架固定了一组磁头,每个磁头负责存取一个磁盘的内容。磁头不能转动,但是可以沿磁盘半径方向运动(实际是斜切向运动),每个磁头同一时刻也必须是同轴的,即从正上方向下看,所有磁头任何时候都是重叠的(不过目前已经有多磁头独立技术,可不受此限制)。

    盘片被划分成一系列同心环,圆心是盘片中心,每个同心环叫做一个磁道,所有半径相同的磁道组成一个柱面。磁道被沿半径线划分成一个个小的段,每个段叫做一个扇区,每个扇区是磁盘的最小存储单元。为了简单起见,我们下面假设磁盘只有一个盘片和一个磁头。

    当需要从磁盘读取数据时,系统会将数据逻辑地址传给磁盘,磁盘的控制电路按照寻址逻辑将逻辑地址翻译成物理地址,即确定要读的数据在哪个磁道,哪个扇区。为了读取这个扇区的数据,需要将磁头放到这个扇区上方,为了实现这一点,磁头需要移动对准相应磁道,这个过程叫做寻道,所耗费时间叫做寻道时间,然后磁盘旋转将目标扇区旋转到磁头下,这个过程耗费的时间叫做旋转时间

    每一次从磁盘读取数据都要靠磁盘的旋转,以及磁头的寻道来读取指定扇区的数据,这是一个很耗时的过程,也就是一次IO操作。所以提高搜索效率的重要手段就是减少IO操作次数。

    • 寻道时间(速度慢,费时)
    • 旋转时间(速度较快)
    磁盘结构图

     

    4.1.1 索引的优点

    1. 极大地加速了索引过程,减少IO次数
    2. 创建唯一索引,保证了数据库表中的唯一性
    3. 加速了表与表之间的连接
    4. 针对分组和排序检索时,能够显著减少查询查询中的分组和排序时间

     

    4.1.2 索引的缺点

    1. 索引表占据物理空间
    2. 数据表中的数据增加、修改、删除的同时需要去动态维护索引表,降低了数据的维护速度

     

    4.2 索引的常见模型

    索引的出现是为了提高查询效率,但是实现索引的方式却有很多种,所以这里也就引入了索引模型的概念。可以用于提高读写效率的数据结构很多,这里我先给你介绍几种常见、也比较简单的数据结构,它们分别是:

    • HASH
    • 有序表
    • 二叉树
    • 红黑树
    • BTREE
      • B
      • B+

     

    4.2.1 哈希表

    哈希表是一种以 - 值(key-value存储数据的结构,我们只要输入待查找的键即 key,就可以找到其对应的值即 Value。哈希的思路很简单,把值放在数组里,用一个哈希函数把 key 换算成一个确定的位置,然后把 value 放在数组的这个位置。

    不可避免地,多个 key 值经过哈希函数的换算,会出现同一个值的情况(哈希碰撞)。处理这种情况的一种方法是,拉出一个链表。

    假设,你现在维护着一个身份证信息和姓名的表,需要根据身份证号查找对应的名字,这时对应的哈希索引的示意图如下所示:

    哈希表示意图

     

    图中,User2 和 User4 根据身份证号算出来的值都是 N,但没关系,后面还跟了一个链表。假设,这时候你要查 ID_card_n2 对应的名字是什么,处理步骤就是:首先,将 ID_card_n2 通过哈希函数算出 N;然后,按顺序遍历,找到 User2。

    需要注意的是,图中四个 ID_card_n 的值并不是递增的,这样做的好处是增加新的 User 时速度会很快,只需要往后追加。但缺点是,因为不是有序的,所以哈希索引做区间查询的速度是很慢的。

    你可以设想下,如果你现在要找身份证号在[ID_card_X, ID_card_Y]这个区间的所有用户,就必须全部扫描一遍了。

    所以哈希也是有很多局限的:

    • 哈希索引是无序的所以不能用于排序order by,group by 后面
    • 由于存的不是索引值,而是映射而成的hash值,所以也不支持范围查找,前缀匹配和联合索引的最左匹配原则

    所以,哈希表这种结构适用于只有等值查询的场景,比如 Memcached 及其他一些 NoSQL 引擎。像一些范围查询的场景就不是很适用。

     

    InnoDB支持哈希索引,但是主流的还是使用B+树索引比较多,对于哈希索引,InnoDB引擎会根据索引值使用的频繁内部自动的在B+Tree索引上创建哈希索引,用户无法控制或者配置,不过可以关闭该优化特

     

    4.2.2 有序表

    有序数组在等值查询和范围查询场景中的性能就都非常优秀。还是上面这个根据身份证号查名字的例子,如果我们使用有序数组来实现的话,示意图如下所示:

    有序数组示意图

    这里我们假设身份证号没有重复,这个数组就是按照身份证号递增的顺序保存的。这时候如果你要查 ID_card_n2 对应的名字,用二分法就可以快速得到,这个时间复杂度是 O(log(N))。

    同时很显然,这个索引结构支持范围查询。你要查身份证号在[ID_card_X, ID_card_Y]区间的 User,可以先用二分法找到 ID_card_X(如果不存在 ID_card_X,就找到大于 ID_card_X 的第一个 User),然后向右遍历,直到查到第一个大于 ID_card_Y 的身份证号,退出循环。

    如果仅仅看查询效率,有序数组就是最好的数据结构了。但是,在需要更新数据的时候就麻烦了,你往中间插入一个记录就必须得挪动后面所有的记录,成本太高。

    所以,有序数组索引只适用于静态存储引擎,比如你要保存的是 2017 年某个城市的所有人口信息,这类不会再修改的数据。

     

    4.2.3 二叉树搜索树

    二叉搜索树也是课本里的经典数据结构了。还是上面根据身份证号查名字的例子,如果我们用二叉搜索树来实现的话,示意图如下所示:

    二叉搜索树示意图

    二叉搜索树的特点是:在普通二叉树的基础上,规定每个节点的左儿子小于父节点,父节点又小于右儿子。这样如果你要查 ID_card_n2 的话,按照图中的搜索顺序就是按照 UserA -> UserC -> UserF -> User2 这个路径得到。这个时间复杂度是 O(log(N))。

    但是如果只是一个二叉搜索树,如果一直按增序插入数据,那么这颗二叉搜索树就会失衡,导致树的高度很大,大大降低了查询效率,如下图:

    比如我想查询0007这个数,那么就需要经历6次比较查询才能得到结果。

     

    为了维持 O(log(N)) 的查询复杂度,你就需要保持这棵树是平衡二叉树(AVL)。为了做这个保证,更新的时间复杂度也是 O(log(N))。下面就引入一种平衡二叉树---红黑树

     

    4.2.4 红黑树

    首先,红黑树是一个二叉搜索树,它在每个节点增加了一个存储位记录节点的颜色,可以是RED,也可以是BLACK;通过任意一条从根到叶子简单路径上颜色的约束,红黑树保证最长路径不超过最短路径的二倍,因而近似平衡。它同时满足以下特性:

    1. 每个节点要么是黑色,要么是红色
    2. 根节点是黑色
    3. 如果节点是红色的,那么它的子节点必须是黑色的(反之,不一定需要成立)
    4. 从根节点到叶节点或空子节点的每条路径,都包含相同数目的黑色节

     

    那么为什么当满足以上性质时,就能保证最长路径不超过最短路径的二倍了呢?我们分析一下:

    当某条路径最短时,这条路径必然都是由黑色节点构成。当某条路径长度最长时,这条路径必然是由红色和黑色节点相间构成(性质4限定了不能出现两个连续的红色节点)。而性质5又限定了从任一节点到其每个叶子节点的所有路径必须包含相同数量的黑色节点。此时,在路径最长的情况下,路径上红色节点数量 = 黑色节点数量。该路径长度为两倍黑色节点数量,也就是最短路径长度的2倍。

    【数据结构】红黑树详解(插入与删除)

    可是红黑是还是一个二叉树,它的深度会随着数据量的增多急剧升高,这样查询的效率也会很低。但是实际的数据库系统几乎没有使用二叉查找树或其进化品种红黑树(red-black tree)实现的。

     

    树可以有二叉,也可以有多叉。多叉树就是每个节点有多个儿子,儿子之间的大小保证从左到右递增。二叉树是搜索效率最高的,但是实际上大多数的数据库存储却并不使用二叉树。其原因是,索引不止存在内存中,还要写到磁盘上。

    你可以想象一下一棵 100 万节点的平衡二叉树,树高 20。一次查询可能需要访问 20 个数据块。在机械硬盘时代,从磁盘随机读一个数据块需要 10 ms 左右的寻址时间。也就是说,对于一个 100 万行的表,如果使用二叉树来存储,单独访问一个行可能需要 20 个 10 ms 的时间,这个查询可真够慢的。

    为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。那么,我们就不应该使用二叉树,而是要使用“N 叉”树。这里,“N 叉”树中的“N”取决于数据块的大小。

    以 InnoDB 的一个整数字段索引为例,这个 N 差不多是 1200。这棵树高是 4 的时候,就可以存 1200 的 3 次方个值,这已经 17 亿了。考虑到树根的数据块总是在内存中的,一个 10 亿行的表上一个整数字段的索引,查找一个值最多只需要访问 3 次磁盘(IO操作)。其实,树的第二层也有很大概率在内存中,那么访问磁盘的平均次数就更少了。

    N 叉树由于在读写上的性能优点,以及适配磁盘的访问模式,已经被广泛应用在数据库引擎中了。这也就引入了后面的B树和B+树

     

    4.2.5 B

    B树也称B-树,它是一颗多路平衡查找树。我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数。当m取2时,就是我们常见的二叉搜索树。其实阶数和度数本质是一样的,因为m阶树规定每个结点最多有m-1个关键字。而度数规定的是一个节点内有多少个关键字,所以本质是一样的。下文会有度数和阶数的混用,请注意区分。

     

    B-Tree

    • (Degree)-节点的数据存储个数
    • 叶节点具有相同的深度
    • 叶节点的指针为空
    • 节点中的数据key从左到右递增排列

    为了描述B-Tree,首先定义一条数据记录为一个二元组[key, data],key为记录的键值,也就是索引值,对于不同数据记录,key是互不相同的;data为数据记录除key外的数据。除了key和data外,节点还存储了指向孩子节点的指针。那么B-Tree是满足下列条件的数据结构:

    1. 每个结点最多有m-1个关键字。
    2. 根结点最少可以只有1个关键字。
    3. 非根结点至少有Math.ceil(m/2)-1个关键字。
    4. 每个结点中的关键字都按照从小到大的顺序排列,每个关键字的左子树中的所有关键字都小于它,而右子树中的所有关键字都大于它。注意这里和B+树有点不同,B+树右子树时大于等于,因为B+树会冗余存储数据
    5. 所有叶子结点都位于同一层,或者说根结点到每个叶子结点的长度都相同。

     

    由于B-Tree的特性,在B-Tree中按key检索数据的算法非常直观:首先从根节点进行二分查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或找到null指针,前者查找成功,后者查找失败

    关于B-Tree有一系列有趣的性质,例如一个度为d的B-Tree,设其索引N个key,则其树高h的上限为logd((N+1)/2),检索一个key,其查找节点个数的渐进复杂度为O(logdN)。从这点可以看出,B-Tree是一个非常有效率的索引数据结构。

    另外,由于插入删除新的数据记录会破坏B-Tree的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持B-Tree性质。

     

    4.2.5.1 B树的插入操作

    插入操作是指插入一条记录,即(key, value)的键值对。如果B树中已存在需要插入的键值对,则用需要插入的value替换旧的value。若B树不存在这个key,则一定是在叶子结点中进行插入操作。

    1. 根据要插入的key的值,找到叶子结点并插入。
    2. 判断当前结点key的个数是否小于d,若满足则结束,否则进行第3步。
    3. 以结点中间的key为中心分裂成左右两部分,然后将这个中间的key插入到父结点中,这个key的左子树指向分裂后的左半部分,这个key的右子支指向分裂后的右半部分,然后将当前结点指向父结点,继续进行第3步。(页分裂

     

    下面以阶数为5的树为例,介绍B树的插入操作,在阶数为5的B树中,结点最多有4个key,最少有2个key

    a)在空树中插入39 

    此时根结点就一个key,此时根结点也是叶子结点

     

    b)继续插入22,97和41

    根结点此时有4个key

     

    c)继续插入53

    插入后超过了最大允许的关键字个数4,所以以key值为41为中心进行分裂,结果如下图所示,分裂后当前结点指针指向父结点,满足B树条件,插入操作结束。当阶数m为偶数时,需要分裂时就不存在排序恰好在中间的key,那么我们选择中间位置的前一个key或中间位置的后一个key为中心进行分裂即可

     

    d)依次插入13,21,40,同样会造成分裂,结果如下图所示。

     

    e)依次插入30,27, 33 ;36,35,34 ;24,29,结果如下图所示。

     

    f)插入key值为26的记录,插入后的结果如下图所示。

    当前结点需要以27为中心分裂,并向父结点进位27,然后当前结点指向父结点,结果如下图所示。

    进位后导致当前结点(即根结点)也需要分裂,分裂的结果如下图所示。

    分裂后当前结点指向新的根,此时无需调整。

     

    g)最后再依次插入key17,28,29,31,32的记录,结果如下图所示。

    在实现B树的代码中,为了使代码编写更加容易,我们可以将结点中存储记录的数组长度定义为m而非m-1,这样方便底层的结点由于分裂向上层插入一个记录时,上层有多余的位置存储这个记录。同时,每个结点还可以存储它的父结点的引用,这样就不必编写递归程序。

    一般来说,对于确定的m和确定类型的记录,结点大小是固定的,无论它实际存储了多少个记录。但是分配固定结点大小的方法会存在浪费的情况,比如key为28,29所在的结点,还有2个key的位置没有使用,但是已经不可能继续在插入任何值了,因为这个结点的前序key是27,后继key是30,所有整数值都用完了。所以如果记录先按key的大小排好序,再插入到B树中,结点的使用率就会很低,最差情况下使用率仅为50%。

    而且将数据和索引值都在节点中存放也会导致一个页能存储的索引节点不多,度数较低,使树得宽度不够大,高度变高,高度越高,检索效率越低。

     

     

    4.2.5.2 B树的删除操作

    删除操作是指,根据key删除记录,如果B树中的记录中不存对应key的记录,则删除失败。

    1. 如果当前需要删除的key位于非叶子结点上,则用后继key(这里的后继key均指后继记录的意思,也就是按照索引值的顺序,要删除节点的索引值的下一个索引值节点就是后继key)覆盖要删除的key,然后在后继key所在的子支中删除该后继key。此时后继key一定位于叶子结点上,这个过程和二叉搜索树删除结点的方式类似。删除这个记录后执行第2步
    2. 该结点key个数大于等于Math.ceil(m/2)-1,结束删除操作,否则执行第3步。
    3. 如果兄弟结点key个数大于Math.ceil(m/2)-1,则父结点中的key下移到该结点,兄弟结点中的一个key上移,删除操作结束。(页合并
      否则,将父结点中的key下移与当前结点及它的兄弟结点中的key合并,形成一个新的结点。原父结点中的key的两个孩子指针就变成了一个孩子指针,指向这个新结点。然后当前结点的指针指向父结点,重复上第2步。
      有些结点它可能即有左兄弟,又有右兄弟,那么我们任意选择一个兄弟结点进行操作即可。

     

    下面以5阶B树为例,介绍B树的删除操作,5阶B树中,结点最多有4个key,最少有2个key

    a)原始状态

     

    b)在上面的B树中删除21,删除后结点中的关键字个数仍然大于等2,所以删除结束。

     

    c)在上述情况下接着删除27。从上图可知27位于非叶子结点中,所以用27的后继替换它。从图中可以看出,27的后继为28,我们用28替换27,然后在28(原27)的右孩子结点中删除28。删除后的结果如下图所示。

    删除后发现,当前叶子结点的记录的个数小于2,而它的兄弟结点中有3个记录(当前结点还有一个右兄弟,选择右兄弟就会出现合并结点的情况,不论选哪一个都行,只是最后B树的形态会不一样而已),我们可以从兄弟结点中借取一个key。所以父结点中的28下移,兄弟结点中的26上移,删除结束。结果如下图所示。

     

    d)在上述情况下接着32,结果如下图。

    当删除后,当前结点中只key,而兄弟结点中也仅有2个key。所以只能让父结点中的30下移和这个两个孩子结点中的key合并,成为一个新的结点,当前结点的指针指向父结点。结果如下图所示。

    当前结点key的个数满足条件,故删除结束。

     

    e)上述情况下,我们接着删除key为40的记录,删除后结果如下图所示。

    同理,当前结点的记录数小于2,兄弟结点中没有多余key,所以父结点中的key下移,和兄弟(这里我们选择左兄弟,选择右兄弟也可以)结点合并,合并后的指向当前结点的指针就指向了父结点。

    同理,对于当前结点而言只能继续合并了,最后结果如下所示。

    合并后结点当前结点满足条件,删除结束。

     

    对于B树的检索效率低和节点使用率低的情况,最终引入了B+树来组织索引树,B+树也是现在存储引擎中使用最广泛的索引结构。

     

    4.2.6 B+

    B+Tree(B-Tree变种)

    • 非叶子节点不存储data,只存储key可以增大度
    • 叶子节点不存储指针
    • 顺序访问指针,提高区间访问的性能
    • B+树也规定叶子节点的深度必须一致

    B+Tree索引的性能分析

    • 一般使用磁盘I/O次数评价索引结构的优劣
    • 预读:磁盘一般会顺序向后读取一定长度的数据(页的整数倍)放入内存,比如会将根页预载入进内存。
    • 局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用
    • B+Tree节点的大小设为等于一个页,每次新建节点直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,就实现了一个节点的载入只需一次I/O
    • B+Tree的度d一般会超过100,因此h非常小(一般为35之间)

     

    4.2.6.1 B+树的定义

     

    各种资料上B+树的定义各有不同,一种定义方式是关键字个数和孩子结点个数相同。这里我们采取维基百科上所定义的方式,即关键字个数比孩子结点个数小1,这种方式是和B树基本等价的。上图就是一颗阶数为4的B+树。

    除此之外B+树还有以下的要求。

    1. B+树包含2种类型的结点:内部结点(也称索引结点)和叶子结点。根结点本身即可以是内部结点,也可以是叶子结点。根结点的关键字个数最少可以只有1个。非根节点的关键字最少有2个。
    2. B+树与B树最大的不同是内部结点不保存数据,只用于索引,所有数据(或者说记录)都保存在叶子结点中。
    3. m阶B+树表示了内部结点最多有m-1个关键字(或者说内部结点最多有m个子树),阶数m同时限制了叶子结点最多存储m-1个记录。
    4. 内部结点中的key都按照从小到大的顺序排列,对于内部结点中的一个key,左树中的所有key都小于它,右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。
    5. 每个叶子结点都存有相邻叶子结点的指针,叶子结点本身依关键字的大小自小而大顺序链接。

     

    4.2.6.2 B+树的插入操作

    1. 若为空树,创建一个叶子结点,然后将记录插入其中,此时这个叶子结点也是根结点,插入操作结束。
    2. 针对叶子类型结点:根据key值找到叶子结点,向这个叶子结点插入记录。插入后,若当前结点key的个数小于等于m-1,则插入结束。否则将这个叶子结点分裂成左右两个叶子结点,左叶子结点包含前m/2个记录,右结点包含剩下的记录,将第m/2+1个记录的key进位到父结点中(父结点一定是索引类型结点),进位到父结点的key左孩子指针向左结点,右孩子指针向右结点。将当前结点的指针指向父结点,然后执行第3步。
    3. 针对索引类型结点:若当前结点key的个数小于等于m-1,则插入结束。否则,将这个索引类型结点分裂成两个索引结点,左索引结点包含前(m-1)/2个key,右结点包含m-(m-1)/2个key,将第m/2个key进位到父结点中,进位到父结点的key左孩子指向左结点, 进位到父结点的key右孩子指向右结点。将当前结点的指针指向父结点,然后重复第3步。

     

    下面是一颗5阶B树的插入过程,5阶B数的结点最少2个key,最多4个key。

    a)空树中插入5

     

    b)依次插入8,10,15

     

    c)插入16

    插入16后超过了关键字的个数限制,所以要进行分裂(页分裂)。在叶子结点分裂时,分裂出来的左结点2个记录,右边3个记录,中间key成为索引结点中的key,分裂后当前结点指向了父结点(根结点)。这里就会冗余的存储10这个索引值。结果如下图所示。

    当然我们还有另一种分裂方式,给左结点3个记录,右结点2个记录,此时索引结点中的key就变为15。

     

    d)插入17

     

    e)插入18,插入后如下图所示

    当前结点的关键字个数大于5,进行分裂。分裂成两个结点,左结点2个记录,右结点3个记录,关键字16进位到父结点(索引类型)中,将当前结点的指针指向父结点。

    当前结点的关键字个数满足条件,插入结束。

     

    f)插入若干数据后


    g)在上图中插入7,结果如下图所示

    当前结点的关键字个数超过4,需要分裂。左结点2个记录,右结点3个记录。分裂后关键字7进入到父结点中,将当前结点的指针指向父结点,结果如下图所示。

    当前结点的关键字个数超过4,需要继续分裂。左结点2个关键字,右结点2个关键字,关键字16进入到父结点中,将当前结点指向父结点,根页最少的结点数可以是1。结果如下图所示。

    当前结点的关键字个数满足条件,插入结束。

     

    4.2.6.3 B+树的删除操作

    如果叶子结点中没有相应的key,则删除失败。否则执行下面的步骤

    1. 删除叶子结点中对应的key。删除后若结点的key的个数大于等于Math.ceil(m-1)/2 – 1,删除操作结束,否则执行第2步。
    2. 若兄弟结点key有富余(大于Math.ceil(m-1)/2 – 1),向兄弟结点借一个记录,同时用借到的key替换父结(指当前结点和兄弟结点共同的父结点)点中的key,删除结束。否则执行第3步。
    3. 若兄弟结点中没有富余的key,则当前结点和兄弟结点合并成一个新的叶子结点,并删除父结点中的key(父结点中的这个key两边的孩子指针就变成了一个指针,正好指向这个新的叶子结点),将当前结点指向父结点(必为索引结点),执行第4步(第4步以后的操作和B树就完全一样了,主要是为了更新索引结点)。
    4. 若索引结点的key的个数大于等于Math.ceil(m-1)/2 – 1,则删除操作结束。否则执行第5步
    5. 若兄弟结点有富余,父结点key下移,兄弟结点key上移,删除结束。否则执行第6步
    6. 当前结点和兄弟结点及父结点下移key合并成一个新的结点。将当前结点指向父结点,重复第4步。

    注意,通过B+树的删除操作后,索引结点中存在的key,不一定在叶子结点中存在对应的记录。

     

    下面是一颗5阶B树的删除过程,5阶B数的结点最少2个key,最多4个key。

    a)初始状态

     

    b)删除22,删除后结果如下图

    删除后叶子结点中key的个数大于等于2,删除结束

     

    c)删除15,删除后的结果如下图所示

    删除后当前结点只有一个key,不满足条件,而兄弟结点有三个key,可以从兄弟结点借一个关键字为9的记录,同时更新将父结点中的关键字由10也变为9,删除结束。

     

    d)删除7,删除后的结果如下图所示

    当前结点关键字个数小于2,(左)兄弟结点中的也没有富余的关键字(当前结点还有个右兄弟,不过选择任意一个进行分析就可以了,这里我们选择了左边的),所以当前结点和兄弟结点合并(页合并),并删除父结点中的key,当前结点指向父结点。

    此时当前结点的关键字个数小于2,兄弟结点的关键字也没有富余,所以父结点中的关键字下移,和两个孩子结点合并,结果如下图所示。

    目前B+树是应用最广泛的一种索引结构。

     

    4.2.7 B树与B+树的差别

    1. B树的同一键不会出现多次,可能在叶子节点上也可能在非叶子节点上;  b+树的键一定会出现在叶子节点上,同时也可能在非叶子节点上重复出现
    2. 简单的说,b+树的非叶子节点存储的都是键值,键值对应的具体数据都存储在叶子节点上。
    3. b数据的每个节点存储的是真实数据,会导致每个节点的存储的数据量变小,所以整个b树的高度会相对变高。随着数据量的变大,维护代价也增加; b+树的非叶子节点只存储的是键值,相对而言,一个非叶子节点存储的记录个数要比b树多的多。
    4. b+树是横向扩展,随着数据增加,会变成一个矮胖子,b树是纵向扩展,最终树的高度越来越高(高瘦子)。
    5. b树的查询效率与键在b树的位置有关系,在叶子节点的时候最大复杂度与b+树相同;b+树复杂度对某个建成的树是固定的
    6. b树的键的位置不固定并且整个树结构中只出现一次,增删改查操作复杂度逐渐加;b+树中非叶子节点对于叶子节点来说就像一个索引,增删改的时候只要找到键值(索引)的位置,再一层层的向下找即可,只有在遇到一个节点存储满了会对b+树分裂。
    7.  b树种所有的数据都只存储一份;b+树除存储了所有数据的叶子节点外,还有之存储键值数据的非叶子节点。所以,b+树比b树会多占存储空间,多占的空间就是b+树的非叶子节点的所有空间。

     

    4.3 索引的代价

    以目前应用最广泛的B+树为例。

    4.3.1 空间上的代价

    一个索引都对应一棵B+树,树中每一个节点都是一个数据页,一个页默认会占用16KB的存储空间,所以一个索引也是会占用磁盘空间的。建立的索引越多,在硬盘中冗余存放的数据也就越多,占用的空间就越大

     

    4.3.2 时间上的代价

    索引是对数据的排序,那么当对表中的数据进行增、删、改操作时,都需要去维护修改内容涉及到的B+树索引。 所以在进行增、删、改操作时可能需要额外的时间进行一些记录移动,页面分裂、页面回收等操作来维护好排序。所以建立索引前需要考虑它的维护成本

     

    4.4 索引模型总结

    不管是哈希还是有序数组,或者 N 叉树,它们都是不断迭代、不断优化的产物或者解决方案。数据库技术发展到今天,跳表、LSM 树等数据结构也被用于引擎设计中,这里我就不再一一展开了。

    你心里要有个概念,数据库底层存储的核心就是基于这些数据模型的。每碰到一个新数据库,我们需要先关注它的数据模型,这样才能从理论上分析出这个数据库的适用场景。

    在 MySQL 中,索引是在存储引擎层实现的,所以并没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样。而即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不同。由于 InnoDB 存储引擎在 MySQL 数据库中使用最为广泛,所以下面我就以 InnoDB 为例,该存储引擎最后选择了B+树作为索引结构。我们再重新梳理一下B+树的相关知识点。

    InnoDB存储引擎中,因为主键索引使用的是聚集索引,所以在插入数据的时候还需要额外的对索引树进行维护。在插入数据的时候,每插一条数据就会按照索引值的顺序来进行重新排序,将数据插入到它应该在的位置。这样其实插入效率也就变低了。因为InnoDB中聚集索引的数据和主键索引树存在一起,就在叶子节点,所以为了所有数据都是按照顺序排放,在插入到新数据的时候自然要重新组织数据的位置,会出现很多数据页的在存储器中的位置变换,为了保证B+树的性质需要进行页页分裂,这是一个很消耗性能的过程。所以在插入数据的时候按照主键值的顺序来进行插入,这样就会减少这种数据页位置的移动,提高插入效率

    B+树所有的非叶子结点的索引值在叶子节点都冗余存储了一份,而且叶子节点都是都是一个递增顺序,叶子节点之间都用指针连接了起来能够加快搜索效率。

    先创建表得时候就会先创建第一页,向数据库中插入数据的时候就会先往第一页插入,当第一页达到度数后,就会进行页分裂,,它的过程是这样的:先将这个第一页复制一份,然后作为左结点,然后再开辟一个右节点,再按照左小右大的原则将原本在第一页装不下的数据分别放到新建立的左右两个子页,然后将原来的第一页作为一个目录页。这样的作用就是保证B+树得第一页的地址是不会改变的,采用了一个永远都不会改变的查询入口,这样每次对这个表查询直接就从这个固定的地址开始查询就可以了。非根页这样分裂也能保证他们的父页指向他们的指针不需要变动,减轻维护B+树的成本。

    B+树的核心思想就是逐渐的开辟新的页通过增加树宽度来分摊一个页所存储的数据数。将数据页转化为目录页,然后将原本的数据放到有创建好的两个数据页中。一旦一个页超过的度数,也就是占用的大小已经超过了16K,那就会执行上面的过程,注意,第一页下面的子页最多也到指定的度数度数了,如果在变得多的就由下面的子页再接着去页分裂就行了,来分摊上面多出来的数据。由于B+数的原理,B+数是横向扩展,一层能存很多数据,所以它的树高最多也就4-5层。

    B+数比B数的优点就是将原本放在每个结点页的数据全都转移到了叶子节点,这个样子就能保证每个页能存更多的目录项,就能使树的横向扩展更大,树的宽度越大,深度降低,这样就能有效的提高搜索效率。因为毕竟深度越大,要进行的分支判断就越多,效率就越低。

    搜索二叉树每次都要从硬盘读取,要进行IO操作,深度越高效率越低。B树是把一个节点加载到内存中取查找,最多只读取磁盘三次,而且根节点是会预热加载到内存的

     

    B+树索引总结:

    1. 每个索引都对应一棵B+树。用户记录都存储在B+树的叶子节点,所有目录记录都存储在非叶子节点。
    2. InnoDB存储引擎会自动为主键(如果没有它会自动帮我们添加)建立聚簇索引,聚簇索引的叶子节点包含完整的用户记录。
    3. 可以为指定的列建立二级索引,二级索引的叶子节点包含的用户记录由索引列 + 主键组成,所以如果想通过二级索引来查找完整的用户记录的话,需要通过回表操作,也就是在通过二级索引找到主键值之后再到聚簇索引中查找完整的用户记录。
    4. B+树中页之间都是按照索引列值从小到大的顺序排序而组成了双向链表(B+树的每个页其实都按照索引顺序用双向链表连接了起来),而且每个页内的记录(不论是索引项还是数据项)都是按照索引列的值从小到大的顺序而形成了一个单链表。如果是联合索引的话,则页面和记录先按照联合索引前边的列排序,如果该列值相同,再按照联合索引后边的列排序。
    5. 通过索引查找记录是从B+树的根节点开始,一层一层向下搜索。由于每个页面都按照索引列的值建立了页目录,所以在这些页面中的查找非常快。每次都是将一页放入内存中去进行操作,所以页内部的检索非常快

    其他相关文章:【MySQL】MySQL的存储引擎和索引详解(聚集索引和非聚集索引)
                             【MySQL】InnoDB存储引擎,MyISAM存储引擎,聚集索引,非聚集索引,主键索引,二级索引他们之间的关系梳理
                             【MySQL】MySQL的锁与事务隔离级别详解 
                             【MySQL】MySQL分库分表详解
                             【MySQL】主从复制实现原理详解


    参考资料:《高性能MySQL》(第3版)
                        B树和B+树的插入、删除图文详解
                      《MySQL实战45讲》林晓斌

    展开全文
  • HTML的HTTP协议头信息中控制着页面在几地方的缓存信息,包括浏览器端,中间缓存服务器端(如:squid等),Web服务器端。本文讨论头信息 中带缓存控制信息的HTML页面(JSP/Servlet生成好出来的也是HTML页面)在中间缓存...

     HTML的HTTP协议头信息中控制着页面在几个地方的缓存信息,包括浏览器端,中间缓存服务器端(如:squid等),Web服务器端。本文讨论头信息 中带缓存控制信息的HTML页面(JSP/Servlet生成好出来的也是HTML页面)在中间缓存服务器中的缓存情况。

          HTTP协议中关于缓存的信息头关键字包括Cache-Control(HTTP1.1),Pragma(HTTP1.0),last-Modified,Expires等。

          HTTP1.0中通过Pragma 控制页面缓存,可以设置:Pragma或no-cache。网上有非常多的文章说明如何控制不让浏览器或中间缓存服务器缓存页面,通常设置的值为no- cache,不过这个值不这么保险,通常还加上Expires置为0来达到目的。但是如我们刻意需要浏览器或缓存服务器缓存住我们的页面这个值则要设置为 Pragma。

          HTTP1.1中启用Cache-Control 来控制页面的缓存与否,这里介绍几个常用的参数:

    • no-cache,浏览器和缓存服务器都不应该缓存页面信息;
    • public,浏览器和缓存服务器都可以缓存页面信息;
    • no-store,请求和响应的信息都不应该被存储在对方的磁盘系统中;
    • must-revalidate,对于客户机的每次请求,代理服务器必须想服务器验证缓存是否过时;

           Last-Modified只页面的最后生成时间,GMT格式;

           Expires过时期限值,GMT格式,指浏览器或缓存服务器在该时间点后必须从真正的服务器中获取新的页面信息;

           上面两个值在JSP中设置值为字符型的GMT格式,无法生效,设置long类型才生效;

     

    下面是一个测试例子:

    复制代码

     1 package com.servlet;
     2 
     3 import java.io.IOException;
     4 import java.io.PrintWriter;
     5 
     6 import javax.servlet.ServletException;
     7 import javax.servlet.http.HttpServlet;
     8 import javax.servlet.http.HttpServletRequest;
     9 import javax.servlet.http.HttpServletResponse;
    10 
    11 public class ServletA extends HttpServlet {
    12     @Override
    13     public void service(HttpServletRequest request, HttpServletResponse response)
    14             throws ServletException, IOException {
    15         response.setContentType("text/html");
    16         //servlet页面默认是不缓存的
    17         //本页面允许在浏览器端或缓存服务器中缓存,时限为20秒。
    18         //20秒之内重新进入该页面的话不会进入该servlet的
    19         java.util.Date date = new java.util.Date();    
    20         response.setDateHeader("Last-Modified",date.getTime()); //Last-Modified:页面的最后生成时间 
    21         response.setDateHeader("Expires",date.getTime()+20000); //Expires:过时期限值 
    22         response.setHeader("Cache-Control", "public"); //Cache-Control来控制页面的缓存与否,public:浏览器和缓存服务器都可以缓存页面信息;
    23         response.setHeader("Pragma", "Pragma"); //Pragma:设置页面是否缓存,为Pragma则缓存,no-cache则不缓存
    24 
    25         //不允许浏览器端或缓存服务器缓存当前页面信息。
    26         /*response.setHeader( "Pragma", "no-cache" );   
    27         response.setDateHeader("Expires", 0);   
    28         response.addHeader( "Cache-Control", "no-cache" );//浏览器和缓存服务器都不应该缓存页面信息
    29         response.addHeader( "Cache-Control", "no-store" );//请求和响应的信息都不应该被存储在对方的磁盘系统中;    
    30         response.addHeader( "Cache-Control", "must-revalidate" );*///于客户机的每次请求,代理服务器必须想服务器验证缓存是否过时;
    31 
    32         System.out.println("进入了servlet");
    33         response.getWriter().write("欢迎光临我的主页");
    34     }
    35 
    36     
    37 }

    复制代码

      如果需要在html页面上设置不缓存,这在<head>标签中加入如下语句:

    1 <meta http-equiv="pragma" content="no-cache">
    2 <meta http-equiv="cache-control" content="no-cache">
    3 <meta http-equiv="expires" content="0">   

     

    附:html页面中meta的作用

      meta是用来在HTML文档中模拟HTTP协议的响应头报文。meta 标签用于网页的<head>与</head>中,meta 标签的用处很多。meta 的属性有两种:name和http-equiv。name属性主要用于描述网页,对应于content(网页内容),以便于搜索引擎机器人查找、分类(目前几乎所有的搜索引擎都使用网上机器人自动查找meta值来给网页分类)。这其中最重要的是description(站点在搜索引擎上的描述)和keywords(分类关键词),所以应该给每页加一个meta值。比较常用的有以下几个:

      name 属性

      1、<meta name="Generator" contect="">用以说明生成工具(如Microsoft FrontPage 4.0)等;

      2、<meta name="KEYWords" contect="">向搜索引擎说明你的网页的关键词;

      3、<meta name="DEscription" contect="">告诉搜索引擎你的站点的主要内容;

      4、<meta name="Author" contect="你的姓名">告诉搜索引擎你的站点的制作的作者;

      5、<meta name="Robots" contect= "all|none|index|noindex|follow|nofollow">

      其中的属性说明如下:

      设定为all:文件将被检索,且页面上的链接可以被查询;

      设定为none:文件将不被检索,且页面上的链接不可以被查询;

      设定为index:文件将被检索;

      设定为follow:页面上的链接可以被查询;

      设定为noindex:文件将不被检索,但页面上的链接可以被查询;

      设定为nofollow:文件将不被检索,页面上的链接可以被查询。

      http-equiv属性

      1、<meta http-equiv="Content-Type" contect="text/html";charset=gb_2312-80">

    和 <meta http-equiv="Content-Language" contect="zh-CN">用以说明主页制作所使用的文字以及语言;

      又如英文是ISO-8859-1字符集,还有BIG5、utf-8、shift-Jis、Euc、Koi8-2等字符集;

      2、<meta http-equiv="Refresh" contect="n;url=http://yourlink">定时让网页在指定的时间n内,跳转到页面http://yourlink;

      3、<meta http-equiv="Expires" contect="Mon,12 May 2001 00:20:00 GMT">可以用于设定网页的到期时间,一旦过期则必须到服务器上重新调用。需要注意的是必须使用GMT时间格式;

      4、<meta http-equiv="Pragma" contect="no-cache">是用于设定禁止浏览器从本地机的缓存中调阅页面内容,设定后一旦离开网页就无法从Cache中再调出;

      5、<meta http-equiv="set-cookie" contect="Mon,12 May 2001 00:20:00 GMT">cookie设定,如果网页过期,存盘的cookie将被删除。需要注意的也是必须使用GMT时间格式;

      6、<meta http-equiv="Pics-label" contect="">网页等级评定,在IE的internet选项中有一项内容设置,可以防止浏览一些受限制的网站,而网站的限制级别就是通过meta属性来设置的;

      7、<meta http-equiv="windows-Target" contect="_top">强制页面在当前窗口中以独立页面显示,可以防止自己的网页被别人当作一个frame页调用;

      8、<meta http-equiv="Page-Enter" contect="revealTrans(duration=10,transtion= 50)">和<meta http-equiv="Page-Exit" contect="revealTrans(duration=20,transtion=6)">设定进入和离开页面时的特殊效果,这个功能即FrontPage中的“格式/网页过渡”,不过所加的页面不能够是一个frame页面。

    ----------------------------------------------------------------------

     

    http 头信息详解

     

    HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送WWW方式的数据,关于HTTP协议的详细内容请参考RFC2616。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求,请求头包含请求的方法、URI、协议版本、以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。服务器以一个状态行作为响应,相应的内容包括消息协议的版本,成功或者错误编码加上包含服务器信息、实体元信息以及可能的实体内容。 

    通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息。这两种类型的消息由一个起始行,一个或者多个头域,一个只是头域结束的空行和可选的消息体组成。HTTP的头域包括通用头,请求头,响应头和实体头四个部分。每个头域由一个域名,冒号(:)和域值三部分组成。域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。 

    通用头域 

    通用头域包含请求和响应消息都支持的头域,通用头域包含Cache-Control、 Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via。对通用头域的扩展要求通讯双方都支持此扩展,如果存在不支持的通用头域,一般将会作为实体头域处理。下面简单介绍几个在UPnP消息中使用的通用头域。 


    Cache-Control头域 

    Cache-Control指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置 Cache-Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令包括no-cache、no-store、max-age、 max-stale、min-fresh、only-if-cached,响应消息中的指令包括public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age。各个消息中的指令含义如下: 

    Public指示响应可被任何缓存区缓存。 

    Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。 

    no-cache指示请求或响应消息不能缓存 

    no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。 

    max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。 

    min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。 

    max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。 


    Date头域 

    Date头域表示消息发送的时间,时间的描述格式由rfc822定义。例如,Date:Mon,31Dec200104:25:57GMT。Date描述的时间表示世界标准时,换算成本地时间,需要知道用户所在的时区。 


    Pragma头域 

    Pragma头域用来包含实现特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1协议中,它的含义和Cache- Control:no-cache相同。 

    请求消息 

    请求消息的第一行为下面的格式: 

    MethodSPRequest-URISPHTTP-VersionCRLFMethod 表示对于Request-URI完成的方法,这个字段是大小写敏感的,包括OPTIONS、GET、HEAD、POST、PUT、DELETE、 TRACE。方法GET和HEAD应该被所有的通用WEB服务器支持,其他所有方法的实现是可选的。GET方法取回由Request-URI标识的信息。 HEAD方法也是取回由Request-URI标识的信息,只是可以在响应时,不返回消息体。POST方法可以请求服务器接收包含在请求中的实体信息,可以用于提交表单,向新闻组、BBS、邮件群组和数据库发送消息。 

    SP表示空格。Request-URI遵循URI格式,在此字段为星号(*)时,说明请求并不用于某个特定的资源地址,而是用于服务器本身。HTTP- Version表示支持的HTTP版本,例如为HTTP/1.1。CRLF表示换行回车符。请求头域允许客户端向服务器传递关于请求或者关于客户机的附加信息。请求头域可能包含下列字段Accept、Accept-Charset、Accept- Encoding、Accept-Language、Authorization、From、Host、If-Modified-Since、If- Match、If-None-Match、If-Range、If-Range、If-Unmodified-Since、Max-Forwards、 Proxy-Authorization、Range、Referer、User-Agent。对请求头域的扩展要求通讯双方都支持,如果存在不支持的请求头域,一般将会作为实体头域处理。 

    典型的请求消息: 

    GET http://download.microtool.de:80/somedata.exe 
    Host: download.microtool.de 
    Accept:*/* 
    Pragma: no-cache 
    Cache-Control: no-cache 
    Referer: http://download.microtool.de/ 
    User-Agent:Mozilla/4.04[en](Win95;I;Nav) 
    Range:bytes=554554- 

    上例第一行表示HTTP客户端(可能是浏览器、下载程序)通过GET方法获得指定URL下的文件。棕色的部分表示请求头域的信息,绿色的部分表示通用头部分。 


    Host头域 

    Host头域指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回。 


    Referer头域 

    Referer头域允许客户端指定请求uri的源资源地址,这可以允许服务器生成回退链表,可用来登陆、优化cache等。他也允许废除的或错误的连接由于维护的目的被追踪。如果请求的uri没有自己的uri地址,Referer不能被发送。如果指定的是部分uri地址,则此地址应该是一个相对地址。 


    Range头域 

    Range头域可以请求实体的一个或者多个子范围。例如, 

    表示头500个字节:bytes=0-499 

    表示第二个500字节:bytes=500-999 

    表示最后500个字节:bytes=-500 

    表示500字节以后的范围:bytes=500- 

    第一个和最后一个字节:bytes=0-0,-1 

    同时指定几个范围:bytes=500-600,601-999 

    但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200 (OK)。 


    User-Agent头域 

    User-Agent头域的内容包含发出请求的用户信息。 

    响应消息 

    响应消息的第一行为下面的格式: 

    HTTP-VersionSPStatus-CodeSPReason-PhraseCRLF 

    HTTP-Version表示支持的HTTP版本,例如为HTTP/1.1。Status- Code是一个三个数字的结果代码。Reason-Phrase给Status-Code提供一个简单的文本描述。Status-Code主要用于机器自动识别,Reason-Phrase主要用于帮助用户理解。Status-Code的第一个数字定义响应的类别,后两个数字没有分类的作用。第一个数字可能取5个不同的值: 

    1xx:信息响应类,表示接收到请求并且继续处理 

    2xx:处理成功响应类,表示动作被成功接收、理解和接受 

    3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理 

    4xx:客户端错误,客户请求包含语法错误或者是不能正确执行 

    5xx:服务端错误,服务器不能正确执行一个正确的请求 

    响应头域允许服务器传递不能放在状态行的附加信息,这些域主要描述服务器的信息和 Request-URI进一步的信息。响应头域包含Age、Location、Proxy-Authenticate、Public、Retry- After、Server、Vary、Warning、WWW-Authenticate。对响应头域的扩展要求通讯双方都支持,如果存在不支持的响应头域,一般将会作为实体头域处理。 

    典型的响应消息: 

    HTTP/1.0200OK 

    Date:Mon,31Dec200104:25:57GMT 

    Server:Apache/1.3.14(Unix) 

    Content-type:text/html 

    Last-modified:Tue,17Apr200106:46:28GMT 

    Etag:"a030f020ac7c01:1e9f" 

    Content-length:39725426 

    Content-range:bytes554554-40279979/40279980 

    上例第一行表示HTTP服务端响应一个GET方法。棕色的部分表示响应头域的信息,绿色的部分表示通用头部分,红色的部分表示实体头域的信息。 

    Location响应头 

    Location响应头用于重定向接收者到一个新URI地址。 

    Server响应头 

    Server响应头包含处理请求的原始服务器的软件信息。此域能包含多个产品标识和注释,产品标识一般按照重要性排序。 

    实体 

    请求消息和响应消息都可以包含实体信息,实体信息一般由实体头域和实体组成。实体头域包含关于实体的原信息,实体头包括Allow、Content- Base、Content-Encoding、Content-Language、 Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、 Etag、Expires、Last-Modified、extension-header。extension-header允许客户端定义新的实体头,但是这些域可能无法未接受方识别。实体可以是一个经过编码的字节流,它的编码方式由Content-Encoding或Content-Type定义,它的长度由Content-Length或Content-Range定义。 

    Content-Type实体头 

    Content-Type实体头用于向接收方指示实体的介质类型,指定HEAD方法送到接收方的实体介质类型,或GET方法发送的请求介质类型 Content-Range实体头 

    Content-Range实体头用于指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式: 

    Content-Range:bytes-unitSPfirst-byte-pos-last-byte-pos/entity-legth 

    例如,传送头500个字节次字段的形式:Content-Range:bytes0- 499/1234如果一个http消息包含此节(例如,对范围请求的响应或对一系列范围的重叠请求),Content-Range表示传送的范围, Content-Length表示实际传送的字节数。 

    Last-modified实体头 

    Last-modified实体头指定服务器上保存内容的最后修订时间。 
     

    应答 头  说明
    Allow服务器支持哪些请求方法(如GET、POST等)。
    Content-Encoding文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept-Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。
    Content-Length表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。
    Content-Type表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentTyep。 
    Date当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
    Expires应该在什么时候认为文档已经过期,从而不再缓存它?
    Last-Modified文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。
    Location表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。
    Refresh表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。 
    注意这种功能通常是通过设置HTML页面HEAD区的<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path">实现,这是因为,自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是,对于Servlet来说,直接设置Refresh头更加方便。 

    注意Refresh的意义是“N秒之后刷新本页面或访问指定页面”,而不是“每隔N秒刷新本页面或访问指定页面”。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则可以阻止浏览器继续刷新,不管是使用Refresh头还是<META HTTP-EQUIV="Refresh" ...>。 

    注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。
    Server服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置。
    Set-Cookie设置和页面关联的Cookie。Servlet不应使用response.setHeader("Set-Cookie", ...),而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。
    WWW-Authenticate客户应该在Authorization头中提供什么类型的授权信息?在包含401(Unauthorized)状态行的应答中这个头是必需的。例如,response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"")。 
    注意Servlet一般不进行这方面的处理,而是让Web服务器的专门机制来控制受密码保护页面的访问(例如.htaccess)。

     

     

     

    展开全文
  • 现在进行时的构成是:主语+be动词(am /... 磅值是指打印的字符的高度的度量单位的数值,1 磅等于 1/72 英寸,或大约等于 1 厘米的 1/28。 字号与磅值是定义字体大小的2种度量单位,如同 米 和 尺。磅值相对毫米的换算...
  • 1. 在DataGridView控件用法(一)中已经显示出列表数据,这时我们需要对每行数据记录进行编辑,需要添加“编辑”、“删除”、“查看”这样的超链接。代码如下: view source print? 1 //...
  • 是最古老的,最简单粗暴的格式了,现在基本上已经不用了,因为占用空间最多,从而导致内存碎片化最严重,是最低效的格式了(针对现在varchar字段使用的更多,而对于 varchar 字段改变长度的更新大部分情况下...
  • MySQL 是最流行的关系...假如这字符串没有那么多汉字,例如仅仅包含一‘中’,那么varchar(255)仅仅占用1字符(两字节)的储存空间;而char(255)则必须占用255字符长度的存储空间,哪怕里面只存储一汉字...
  • PHPExcel设置页边距 格式 颜色 大小等

    千次阅读 2018-11-12 09:59:53
    设置行/列顶部重复/左24 4.6.17。指定印刷面积24 4.6.18。设置单元格24 4.6.19。数字格式26 4.6.20。校准和自动换行26 4.6.21。设置一工作簿27的默认样式 4.6.22。造型单元格边框27 4.6.23。条件格式的单元格28 ...
  • 2021年全国大学生电子设计大赛注意问题11.05

    千次阅读 多人点赞 2021-11-05 08:17:39
    5,G题题目第一最后一的“激光笔光点”,请问对激光笔光点颜色有要求吗? 答:激光笔光点颜色选择红色。 6,有G题的CAD图吗? 答:只有jpg图。 7,发挥部分,飞行器降落在圆周上,是指降落在圆周范围内吗? 答...
  • Android 自定义和可下载字体

    千次阅读 2018-05-29 10:09:49
    好处是您可以作为一整体访问它,而不是将每个样式和字重的单个字体文件引用为单独的资源。 你现在可以创建一族。首先,来创建 OpenSans 的粗体 。 重复上面的步骤,将新的字体文件添加到项目的字体...
  • 28 提升开发幸福度的 VsCode 插件

    万次阅读 多人点赞 2020-11-02 08:50:20
    点赞再看,微信搜索 【大迁世界】 关注这没有大厂背景,但有着一股向上积极心态人。本文 GitHub https://github.com/qq449245884/xiaozhi 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。 双 1111...
  • 六万字 HTTP 必备干货学习,程序员不懂网络怎么,一篇HTTP入门不收藏都可惜!网络知识入门必备,学习起来!
  • 库就像是文件夹,库中可以有很多表 表就像是我们的excel表格文件一样 表中都可以存储很多数据 mysql中可以有很多不同的库,库中可以有很多不同的表 表中可以定义不同的列(字段), 表中可以根据结构去存储很...
  • 10万208道Java经典面试题总结(附答案)

    万次阅读 多人点赞 2021-08-01 16:05:55
    JDK中包含JRE,JDK中有一名为jre的目录,里面包含两文件夹bin和lib,bin就是JVM,lib就是JVM工作所需要的类库。 2、== 和 equals 的区别是什么? 对于基本类型,==比较的是值; 对于引用类型,==比较的是地址...
  • 内部存储器——③主存储器

    千次阅读 2020-03-20 16:36:26
    一、只读存储器ROM 即使电源断电,ROM中存储的信息也不会丢失。 (一)ROM的类型 ROM工作是只能读出,不能... 其中,M/l表示把M×N的空间分成(M/l)部分(称为页或区),每页(N/k)芯片。 地址分配: ①...
  • Oracle sqlplus的set命令详细使用和设置

    千次阅读 2019-09-24 18:19:48
    Oracle sqlplus的set命令详细使用和设置 SQL*Plus是Oracle提供的访问数据库服务器的...
  • 【进大厂必学】3W180张图学习Linux基础总结

    千次阅读 多人点赞 2021-05-22 10:39:50
    有着自己的小目标,有的想尝试互联网,所以现在基本上都快进行轮的复习了,有的同学备战公务员,凭着年轻这股劲儿向往自己理想的生活状态,无论怎么样,长路漫漫,走一步,算一步,一步都算数。 今天分享的这篇...
  • pb数据窗口设置操作

    千次阅读 2014-04-17 21:44:01
    1 使DataWindow列只能追加不能修改 如何使DataWindow中的数据只能追加新记录...将一列的 Protect 属性设置为: If( IsRowNew(), 0, 1) ) 在 PowerScript 中可以动态修改 Protect 属性: dw_1.Modify("column_nam
  • 玩大数据,没有这34工具怎么

    万次阅读 2018-07-17 13:27:53
    它核心价值在于,帮助Android/iOS开发者通过shareinstall提供的sdk,精确的获取app一次安装的分享(或推广)来源。  特点:以渠道链接取代渠道包,精确的定位APP的各个安装渠道;ShareinstallSDK量级较轻,应用...
  • word红头文件怎么制作

    千次阅读 2020-12-24 14:07:57
    选中“指定网格和字符风格”,将“每设置成“28字符,“每页设置成“22。然后点击【确定】关闭对话框。 依次点击【插入】-【页码】-【页面底端】,选择一种样式插入。 7 然后右键选择【设置页码格式...
  • Unity5.4.0是一非常重要的版本,在图形性能、跨平台支持方面、特别是VR多平台支持都有许多改进。透过Unity发行说明能够从总体上了解这版本的功能变换,对项目开发非常有帮助,因为工作需要自己临时翻译了一份...
  • 论文写作格式设置

    万次阅读 2018-07-05 19:35:33
    论文或设计说明书内容一般应由八个主要部分组成,依次为:题目,中、英文摘要,关键词,目录,文本主体,致谢,参考文献,附录(必要时)。各部分的具体要求如下: 1、题目 题目应该用极为精炼的文字把论文的主题...
  • 这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一适合你的列表创建一表格设定内容居中、居左、...
  • 目录形式有区别) 解析顶部导航栏分类和地点列表 由于现在该团购网站翻第二页就要求登录,咱们也没有打算真的要爬它。所以我通过多下载几分类链接,来模拟批量下载的效果。 下面准备解析出下面这些对应的标题: ...
  • QT tablewidget设置表头

    万次阅读 2019-04-23 19:27:39
    QT QTableWidget 用法总结 08-18QTableView表头填满控件,表头对齐 11-09Qt Table 的表头合并 09-27Qt... 10-10QT快速设置QTableWidget表头内容 06-08QTableWidget 使用代理画进度条 05-08QTableView,支持拖拽列,...
  • 要求通过系统下载的Excel都带上公司的水印,列宽调整为合适的宽度,并且设置为不可编辑,即只读。 即: 1:加水印; 2:调整列宽将单元格内容显示全; 3:设置只读; 解决方案思路介绍: 三点需求比较来说,以...
  •  命令显示每个可用处理器的使用情况,编号从 0 开始。命令  mpstat -P ALL  显示了每个处理器的平均使用率: # mpstat -P ALL 输出示例: Linux 2 .6 .18-128 .1 .14 .el5 (www03.nixcraft.in) 06 / 26 / ...
  • 01 一键替换全部A字体为B字体 当我们做好了一PPT,却突然发现里面的字体用的不够好的时候,【替换字体】功能可以帮我们省去一、甚至一文本框一文本框去修改字体的痛苦。替换字体功能点击【开始】选项.....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 60,143
精华内容 24,057
关键字:

如何设置每行28个字每页22行