索引缓存优点和使用

本文介绍如何利用索引缓存提高渲染效率,包括创建和填充顶点及索引缓存的过程,以及如何设置和渲染索引缓存数据。

 /*索引缓存的好处:

 (1).减少了顶点缓存的顶点个数(不需重复顶点)。
 (2).灵活修改图形形状(通过修改索引序列而不是顶点序列)。

 (3).提高渲染效率,runtime将多次用到的顶点数据放置在相近的地方Cache中(local reference)。*/

1.创建顶点缓存

Device->CreateIndexBuffer(

        36 * sizeof(WORD), // 索引缓存的字节数,个数是三角形的个数*3,索引缓存个数还是没有减少的。
        D3DUSAGE_WRITEONLY, // 使用类型为只读类型,获取指针然后写入数据,数据将会批量提交
        D3DFMT_INDEX16, // D3DFMT_INDEX16是16bit,D3DFMT_INDEX32是32bit
        D3DPOOL_MANAGED, // D3DPOOL类型,创建在SM中,需要时候拷贝到AGP内存/显存中
        &IB,

        0);//保留

    Device->CreateVertexBuffer(
        8 * sizeof(Vertex),
        D3DUSAGE_WRITEONLY,
        Vertex::FVF,
        D3DPOOL_MANAGED,
        &VB,
        0);

2.填充索引缓存
    // define unique vertices:
    Vertex* vertices;
    VB->Lock(0, 0, (void**)&vertices, 0);

    // vertices of a unit cube

   // 如果没有索引缓存,那么每个面需要2个三角形,每个三角形需要3个顶点,一个面6个顶点,6个面36个顶点。

   // 用了索引,那么只需要8个顶点就可以了,这样索引每个三角形需要3个,一共需要36个索引点。

// DX中三角形顺时针(左手定则)为正面, 逆时针为背面会被消隐,OGL则相反。

// 索引缓存点的个数是三角形片面数 * 3得到的个数
// 所有索引缓存表面从外往物体里看都是顺时针给出的(例如六面体背面的索引缓存顺序),索引缓存每连续三个为一个三角形即可。


   vertices[0] = Vertex(-1.0f, -1.0f, -1.0f);
    vertices[1] = Vertex(-1.0f,  1.0f, -1.0f);
    vertices[2] = Vertex( 1.0f,  1.0f, -1.0f);
    vertices[3] = Vertex( 1.0f, -1.0f, -1.0f);
    vertices[4] = Vertex(-1.0f, -1.0f,  1.0f);
    vertices[5] = Vertex(-1.0f,  1.0f,  1.0f);
    vertices[6] = Vertex( 1.0f,  1.0f,  1.0f);
    vertices[7] = Vertex( 1.0f, -1.0f,  1.0f);

    VB->Unlock();


    WORD* indices = 0;
    IB->Lock(0, 0, (void**)&indices, 0);

    // DX中三角形顺时针(左手定则)为正面, 逆时针为背面会被消隐;OGL则相反。

    // 索引缓存的三角形顺序都是顺时针的(平视该三角形)
    // front side
    indices[0]  = 0; indices[1]  = 1; indices[2]  = 2;
    indices[3]  = 0; indices[4]  = 2; indices[5]  = 3;

    // back side
    indices[6]  = 4; indices[7]  = 6; indices[8]  = 5;
    indices[9]  = 4; indices[10] = 7; indices[11] = 6;

    // left side
    indices[12] = 4; indices[13] = 5; indices[14] = 1;
    indices[15] = 4; indices[16] = 1; indices[17] = 0;

    // right side
    indices[18] = 3; indices[19] = 2; indices[20] = 6;
    indices[21] = 3; indices[22] = 6; indices[23] = 7;

    // top
    indices[24] = 1; indices[25] = 5; indices[26] = 6;
    indices[27] = 1; indices[28] = 6; indices[29] = 2;

    // bottom
    indices[30] = 4; indices[31] = 0; indices[32] = 3;
    indices[33] = 4; indices[34] = 3; indices[35] = 7;

    IB->Unlock();


3.设置索引缓存数据到流

Device->SetStreamSource(0, VB, 0, sizeof(Vertex));
Device->SetIndices(IB);

Device->SetFVF(Vertex::FVF);

4.渲染到表面缓存

// Draw cube.
 Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);

HRESULT DrawIndexedPrimitive(
  [in]  D3DPRIMITIVETYPE Type, // 图元的类型,不能使用D3DPT_POINTLIST
  [in]  INT BaseVertexIndex, // 顶点缓存的第一个顶点索引的偏移位置,偏移是因为当多个顶点缓存合并成一块全局缓存中时每块都有一个基准的offset值。
  [in]  UINT MinIndex, // 顶点缓存开始渲染的最小值
  [in]  UINT NumVertices, // 顶点的个数,第一个是 BaseVertexIndex + MinIndex
  [in]  UINT StartIndex, // 索引缓存开始的位置
  [in]  UINT PrimitiveCount // 图元的个数
);

实例片段,解释如何指定顶点的个数和索引的个数:

//////////////////////////////////////////////////////////////////////////////////////////////////
//
// File: cube.cpp
//
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0
//
// Desc: Provides an interface to create and render a cube.
//          
//////////////////////////////////////////////////////////////////////////////////////////////////

#include "cube.h"
#include "vertex.h"

Cube::Cube(IDirect3DDevice9* device)
{
    // save a ptr to the device
    _device = device;

// 因为启用了顶点法向量和纹理坐标,不能只用8个顶点,一个面有2个三角形中有2个顶点可以共享,所以是4个顶点,6个面24个顶点。
    _device->CreateVertexBuffer(
        24 * sizeof(Vertex),
        D3DUSAGE_WRITEONLY,
        FVF_VERTEX,
        D3DPOOL_MANAGED,
        &_vb,
        0);

    Vertex* v;
    _vb->Lock(0, 0, (void**)&v, 0);

    // build box

    // fill in the front face vertex data
    v[0] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
    v[1] = Vertex(-1.0f,  1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
    v[2] = Vertex( 1.0f,  1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
    v[3] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);

    // fill in the back face vertex data
    v[4] = Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
    v[5] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f);
    v[6] = Vertex( 1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
    v[7] = Vertex(-1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f);

    // fill in the top face vertex data
    v[8]  = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
    v[9]  = Vertex(-1.0f, 1.0f,  1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
    v[10] = Vertex( 1.0f, 1.0f,  1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);
    v[11] = Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);

    // fill in the bottom face vertex data
    v[12] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f);
    v[13] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f);
    v[14] = Vertex( 1.0f, -1.0f,  1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f);
    v[15] = Vertex(-1.0f, -1.0f,  1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);

    // fill in the left face vertex data
    v[16] = Vertex(-1.0f, -1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
    v[17] = Vertex(-1.0f,  1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
    v[18] = Vertex(-1.0f,  1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
    v[19] = Vertex(-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f);

    // fill in the right face vertex data
    v[20] = Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
    v[21] = Vertex( 1.0f,  1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
    v[22] = Vertex( 1.0f,  1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
    v[23] = Vertex( 1.0f, -1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f);

    _vb->Unlock();

// 索引缓存,索引都要记录每个三角形的顶点,6面体有12个三角形,36个索引顶点。
    _device->CreateIndexBuffer(
        36 * sizeof(WORD),
        D3DUSAGE_WRITEONLY,
        D3DFMT_INDEX16,
        D3DPOOL_MANAGED,
        &_ib,
        0);

    WORD* i = 0;
    _ib->Lock(0, 0, (void**)&i, 0);

    // fill in the front face index data
    i[0] = 0; i[1] = 1; i[2] = 2;
    i[3] = 0; i[4] = 2; i[5] = 3;

    // fill in the back face index data
    i[6] = 4; i[7]  = 5; i[8]  = 6;
    i[9] = 4; i[10] = 6; i[11] = 7;

    // fill in the top face index data
    i[12] = 8; i[13] =  9; i[14] = 10;
    i[15] = 8; i[16] = 10; i[17] = 11;

    // fill in the bottom face index data
    i[18] = 12; i[19] = 13; i[20] = 14;
    i[21] = 12; i[22] = 14; i[23] = 15;

    // fill in the left face index data
    i[24] = 16; i[25] = 17; i[26] = 18;
    i[27] = 16; i[28] = 18; i[29] = 19;

    // fill in the right face index data
    i[30] = 20; i[31] = 21; i[32] = 22;
    i[33] = 20; i[34] = 22; i[35] = 23;

    _ib->Unlock();
}

Cube::~Cube()
{
    if(_vb){_vb->Release(); _vb = 0;}
    if(_ib){_ib->Release(); _ib = 0;}
}

bool Cube::draw(D3DXMATRIX* world, D3DMATERIAL9* mtrl, IDirect3DTexture9* tex)
{
    if( world )
        _device->SetTransform(D3DTS_WORLD, world);
    if( mtrl )
        _device->SetMaterial(mtrl);
    if( tex )
        _device->SetTexture(0, tex);

    _device->SetStreamSource(0, _vb, 0, sizeof(Vertex));
    _device->SetIndices(_ib);
    _device->SetFVF(FVF_VERTEX);
    _device->DrawIndexedPrimitive(
        D3DPT_TRIANGLELIST,
        0,                  
        0,                  
        24,
        0,
        12);  

    return true;
}



<think>我们正在回答关于B+Tree索引优点的问题。根据引用内容,我们知道B+Tree索引是MySQL等数据库系统中常用的索引结构。与Hash索引、二叉搜索树(如红黑树)相比,B+Tree具有显著优势。下面我将结合引用内容,详细说明B+Tree索引优点。 ### 1. **树高度可控,适合磁盘存储** - B+Tree是一种多路平衡搜索树(引用[2]中提到),每个节点可以有多个子节点(通常为几百个)。这相比于二叉搜索树(每个节点最多两个子节点)大大降低了树的高度。 - 例如,对于千万级别的数据量,B+Tree的高度通常维持在3-5层(引用[2]),而红黑树的高度可能达到几十层。树高度低意味着磁盘I/O次数少(因为每次访问节点可能对应一次磁盘读取),这对数据库性能至关重要。 - 数学表达:设B+Tree的阶数为$m$(即每个节点最多$m$个子节点),则树高$h$与数据量$N$的关系为: $$ h \approx \log_m N $$ 当$m$较大时,$h$远小于二叉树的$O(\log_2 N)$。 ### 2. **范围查询高效** - B+Tree的数据全部存储在叶子节点,且叶子节点间通过指针连接成有序链表(引用[2])。这使得范围查询(如`WHERE id BETWEEN 100 AND 200`)非常高效:只需定位到起始值,然后顺序遍历链表即可。 - 相比之下,Hash索引仅支持等值查询(引用[1][3]),无法高效处理范围查询;二叉搜索树虽有序,但范围查询需要多次回溯父节点,效率较低。 ### 3. **数据有序性,避免全表扫描** - B+Tree索引本身是排序的(引用[2]),因此对于排序操作(`ORDER BY`)、分组操作(`GROUP BY`)以及最值查询(`MAX/MIN`)可以直接利用索引的有序性,避免全表扫描。 - 例如,查询最大值时,只需遍历最右侧叶子节点链表的末尾。 ### 4. **减少磁盘I/O,提高缓存命中率** - B+Tree的非叶子节点仅存储键值(不存储实际数据),因此单个节点可容纳更多键值(引用[2])。这使得非叶子节点可以缓存到内存中,减少磁盘访问。 - 实际数据(或数据指针)仅存在于叶子节点,保证了查询路径长度一致(稳定性能)。 ### 5. **自适应Hash优化(InnoDB特性)** - InnoDB存储引擎在特定条件下会自动将频繁访问的B+Tree索引转换为Hash索引(引用[3]),结合了Hash的等值查询速度B+Tree的范围查询优势。 ### 6. **对比其他索引的劣势** - **Hash索引**: - 优点:等值查询$O(1)$复杂度(引用[1])。 - 缺点:不支持范围查询、排序;哈希冲突影响性能;内存需求高(引用[3])。 - **二叉平衡树(如红黑树)**: - 优点:查询单个值$O(\log N)$。 - 缺点:树高较大(尤其海量数据时),导致磁盘I/O多;范围查询效率低于B+Tree(引用[2])。 ### 总结 B+Tree索引的核心优势在于**低树高**(减少磁盘I/O)、**有序性**(高效范围查询)**结构优化**(非叶子节点仅存键值)。这些特性使其成为数据库索引的标准选择。实际应用中,InnoDB的自适应Hash功能进一步提升了高频等值查询的性能[^3]。 §§相关问题§§ 1. B+Tree索引的节点分裂与合并机制如何实现平衡? 2. 在什么场景下应优先选择Hash索引而非B+Tree? 3. InnoDB的自适应Hash索引是如何工作的? 4. 如何通过B+Tree索引优化SQL查询性能? 5. B+Tree索引的页大小对性能有何影响? [^1]: 索引是对数据库表中一列或多列的值进行处理后的一种结构,使用索引可快速访问数据库表中的特定信息。本文主要对Hash索引B+tree索引的区别做说明。 [^2]: B+树是B-树的加强版,是一种多路平衡搜索树,既然它是多路平衡的,那么就不在像红黑树那样只有2个子节点了,既然有多个子节点,树的高度就可以控制了。 [^3]: 在MySQL中,支持hash索引的是Memory存储引擎。而InnoDB中具有自适应hash功能,hash索引是InnoDB存储引擎根据B+Tree索引在指定条件下自动构建的。</think>### B+Tree索引的核心优势详解 B+Tree索引是数据库系统的核心数据结构,相较于Hash索引、二叉搜索树等结构,其优势体现在以下关键维度: --- #### 1. **树高度可控,减少磁盘I/O** - **多路平衡设计**: B+Tree每个节点可存储多个键值(通常数百个),形成**多路平衡树**。设阶数为$m$,树高$h$与数据量$N$满足: $$ h \approx \log_m N $$ 例如:$m=500$时,10亿数据仅需$h=4$层,而二叉树需$\log_2 N \approx 30$层。 - **磁盘优化**: 数据库以页(通常4KB)为单位读写磁盘。B+Tree节点大小匹配磁盘页,**单次I/O可加载大量键值**,显著减少磁盘访问次数[^2]。 --- #### 2. **高效范围查询** - **叶子节点链表结构**: B+Tree所有数据存储在叶子节点,并通过指针**双向链接**(如图示)。 ```mermaid graph LR A[根节点] --> B[内部节点] A --> C[内部节点] B --> D[叶子节点 10→20→30] B --> E[叶子节点 40→50] D -->|指针| E ``` 范围查询(如`WHERE id BETWEEN 30 AND 50`)只需定位起始叶子节点,沿链表遍历即可[^2]。 - **对比劣势**: - **Hash索引**:仅支持等值查询,无法处理范围扫描[^1]。 - **二叉树**:范围查询需回溯父节点,效率低下。 --- #### 3. **查询性能稳定** - **路径等长特性**: B+Tree所有叶子节点位于同一深度,**任何查询的I/O次数恒等于树高$h$**。 - **对比红黑树**: 红黑树高度不平衡(最深路径可达最短路径2倍),查询性能波动大[^2]。 --- #### 4. **高缓存命中率** - **非叶节点纯索引**: 非叶子节点仅存储键值子节点指针(**不存实际数据**),使得: - 单节点可容纳更多键值 - 高频访问的根/非叶节点**常驻内存**,减少磁盘访问[^2]。 - **数据局部性**: 叶子节点存储连续数据,顺序访问时**预读机制**可批量加载相邻数据。 --- #### 5. **自适应优化能力(InnoDB)** - **动态Hash转换**: InnoDB监测高频等值查询时,**自动构建自适应Hash索引**,将B+Tree键值映射到Hash表,实现$O(1)$查询[^3]。 - **混合优势**: 保留B+Tree范围查询能力,同时获得Hash的等值查询速度。 --- #### 6. **对比其他索引的劣势总结** | 索引类型 | 等值查询 | 范围查询 | 树高控制 | 磁盘I/O优化 | |----------------|----------|----------|----------|-------------| | **B+Tree** | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | | **Hash** | ⭐⭐⭐⭐⭐ | ❌ | - | ⭐⭐ | | **红黑树** | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐ | ⭐⭐ | | **二叉搜索树** | ⭐⭐⭐ | ⭐ | ❌ | ❌ | > ⭐:优势等级(5星最优);❌:不支持/弱项 --- ### 总结 B+Tree的核心优势在于:**多路平衡控制树高**(减少I/O)、**叶子链表支持高效范围扫描**、**稳定查询路径**及**缓存友好设计**。结合InnoDB的自适应Hash,成为数据库索引的黄金标准。实际应用中,B+Tree在范围查询占比高的场景(如日志分析、时间序列数据)优势尤为显著[^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值