首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >每日一博 - 漫谈流控小妙招

每日一博 - 漫谈流控小妙招

作者头像
小小工匠
发布于 2023-09-02 10:50:42
发布于 2023-09-02 10:50:42
24600
代码可运行
举报
文章被收录于专栏:小工匠聊架构小工匠聊架构
运行总次数:0
代码可运行

Pre

深入理解分布式技术 - 限流

并发编程-25 高并发处理手段之消息队列思路 + 应用拆分思路 + 应用限流思路

SpringBoot - 优雅的实现【流控】

概述

限流作为一种流量控制策略 (通常会和熔断、降级搭配在一起使用,来避免瞬时的大量请求对系统造成负荷,来达到保护服务平稳运行的目的)旨在维护系统的稳定性。然而,限流也带来了平衡用户满意度与系统稳定性之间的挑战。

限流策略的核心意义: 限流是一种管理流量的方法,通过设置最大请求率或并发连接数,以防止系统被过多请求压垮。随着数字服务的普及,限流变得尤为重要,因为高流量可能导致系统崩溃,影响整体可用性。 限流与用户体验的平衡: 限流策略的挑战之一是如何在保持系统稳定的同时提供令人满意的用户体验。过于严格的限流可能导致用户等待时间过长,降低用户满意度。但没有限流,系统可能会被过度请求而崩溃,影响用户所有人。

我们这里将深入探讨限流的集中实现方式

实现方式

固定窗口算法(计数器法)

定义

固定窗口算法通过在单位时间内维护一个计数器,能够限制在每个固定的时间段内请求通过的次数,以达到限流的效果。

实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 @Slf4j
public class FixedWindowRateLimiter {
    // 时间窗口大小,单位毫秒
    private long windowSize;
    // 允许通过请求数
    private int maxRequestCount;

    // 当前窗口通过的请求计数
    private AtomicInteger count = new AtomicInteger(0);
    // 窗口右边界
    private long windowBorder;

    public FixedWindowRateLimiter(long windowSize, int maxRequestCount) {
        this.windowSize = windowSize;
        this.maxRequestCount = maxRequestCount;
        windowBorder = System.currentTimeMillis() + windowSize;
    }

    /**
     * 尝试获取许可
     *
     * @return 若获取成功则返回 true,否则返回 false
     */
    public synchronized boolean tryAcquire() {
        long currentTime = System.currentTimeMillis();

        // 如果窗口边界小于当前时间,则表示窗口重置
        if (windowBorder < currentTime) {
            log.info("窗口重置");
            // 重新计算新的窗口边界,确保窗口大小不变
            do {
                windowBorder += windowSize;
            } while (windowBorder < currentTime);
            // 重置计数器
            count = new AtomicInteger(0);
        }

        // 如果请求计数小于允许的最大请求数
        if (count.intValue() < maxRequestCount) {
            log.info("获取许可成功");
            // 计数增加并返回成功
            count.incrementAndGet();
            return true;
        } else {
            log.info("获取许可失败");
            return false;
        }
    }
}

通过构造方法中的参数指定时间窗口大小以及允许通过的请求数量,当请求进入时先比较当前时间是否超过窗口上边界,未越界且未超过计数器上限则可以放行请求。

测试

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public static void main(String[] args) throws InterruptedException {
       // 在1000毫秒内通过5个请求
        FixedWindowRateLimiter fixedWindowRateLimiter  = new FixedWindowRateLimiter(1000, 5);

        for (int i = 0; i < 10; i++) {
            if (fixedWindowRateLimiter.tryAcquire()) {
                System.out.println("执行任务");
            }else{
                System.out.println("被限流");
                TimeUnit.MILLISECONDS.sleep(300);
            }
        }
    }

缺点 :临界问题

假设有一个恶意用户,在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求,那么其实这个用户在1秒里面,瞬间发送了200个请求。我们刚才规定的是1分钟最多100个请求,也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求,可以瞬间超过我们的速率限制。用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。

刚才的问题其实是因为我们统计的精度太低。那么如何很好地处理这个问题呢?或者说,如何将临界问题的影响降低呢?我们可以看下面的滑动窗口算法


滑动窗口算法

定义

滑动窗口算法在固定窗口的基础上,进行了一定的升级改造。它的算法的核心在于将时间窗口进行了更精细的分片,将固定窗口分为多个小块,每次仅滑动一小块的时间。

并且在每个时间段内都维护了单独的计数器,每次滑动时,都减去前一个时间块内的请求数量,并再添加一个新的时间块到末尾,当时间窗口内所有小时间块的计数器之和超过了请求阈值时,就会触发限流操作。

实现

该算法的实现,核心就是通过一个int类型的数组循环使用来维护每个时间片内独立的计数器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SlidingWindowRateLimiter {
    // 时间窗口大小,单位毫秒
    private long windowSize;
    // 分片窗口数
    private int shardNum;
    // 允许通过请求数
    private int maxRequestCount;
    // 各个窗口内请求计数
    private int[] shardRequestCount;
    // 请求总数
    private int totalCount;
    // 当前窗口下标
    private int shardId;
    // 每个小窗口大小,毫秒
    private long tinyWindowSize;
    // 窗口右边界
    private long windowBorder;

    public SlidingWindowRateLimiter(long windowSize, int shardNum, int maxRequestCount) {
        this.windowSize = windowSize;
        this.shardNum = shardNum;
        this.maxRequestCount = maxRequestCount;
        shardRequestCount = new int[shardNum];
        tinyWindowSize = windowSize / shardNum;
        windowBorder = System.currentTimeMillis();
    }

    /**
     * 尝试获取许可
     *
     * @return 若获取成功则返回 true,否则返回 false
     */
    public synchronized boolean tryAcquire() {
        long currentTime = System.currentTimeMillis();
        
        // 如果当前时间超过窗口右边界,进行窗口滑动操作
        if (currentTime > windowBorder) {
            do {
                shardId = (++shardId) % shardNum;
                totalCount -= shardRequestCount[shardId];
                shardRequestCount[shardId] = 0;
                windowBorder += tinyWindowSize;
            } while (windowBorder < currentTime);
        }

        // 如果请求总数小于允许的最大请求数
        if (totalCount < maxRequestCount) {
            log.info("获取许可成功。窗口ID:{}", shardId);
            shardRequestCount[shardId]++;
            totalCount++;
            return true;
        } else {
            log.info("获取许可失败。窗口ID:{}", shardId);
            return false;
        }
    }
}

测试

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 对第一个例子中的规则进行修改,每1秒允许100个请求通过不变,在此基础上再把每1秒等分为10个0.1秒的窗口。
 SlidingWindowRateLimiter slidingWindowRateLimiter
                = new SlidingWindowRateLimiter(1000, 10, 10);
        TimeUnit.MILLISECONDS.sleep(800);

        for (int i = 0; i < 15; i++) {
            boolean acquire = slidingWindowRateLimiter.tryAcquire();
            if (acquire){
                System.out.println("执行任务");
            }else{
                System.out.println("被限流");
            }
            TimeUnit.MILLISECONDS.sleep(10);
        }

程序启动后,在先休眠了一段时间后再发起请求,可以看到在0.9秒到1秒的时间窗口内放行了6个请求,在1秒到1.1秒内放行了4个请求,随后就进行了限流,解决了在固定窗口算法中相邻时间窗口内允许通过大量请求的问题

滑动窗口算法通过将时间片进行分片,对流量的控制更加精细化,但是相应的也会浪费一些存储空间,用来维护每一块时间内的单独计数,并且还没有解决固定窗口中可能出现的流量激增问题。

漏桶算法

定义

为了应对流量激增的问题,后续又衍生出了漏桶算法,用专业一点的词来说,漏桶算法能够进行流量整形和流量控制

漏桶是一个很形象的比喻,外部请求就像是水一样不断注入水桶中,而水桶已经设置好了最大出水速率,漏桶会以这个速率匀速放行请求,而当水超过桶的最大容量后则被丢弃。

实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Slf4j
public class LeakyBucketRateLimiter {
    // 桶的容量
    private int capacity;
    // 桶中现存水量
    private AtomicInteger water = new AtomicInteger(0);
    // 开始漏水时间
    private long leakTimeStamp;
    // 水流出的速率,即每秒允许通过的请求数
    private int leakRate;

    public LeakyBucketRateLimiter(int capacity, int leakRate) {
        this.capacity = capacity;
        this.leakRate = leakRate;
    }

    /**
     * 尝试获取许可
     *
     * @return 若获取成功则返回 true,否则返回 false
     */
    public synchronized boolean tryAcquire() {
        long currentTime = System.currentTimeMillis();

        // 如果桶中没有水,重新开始计算
        if (water.get() == 0) {
            log.info("开始漏水");
            leakTimeStamp = currentTime;
            water.incrementAndGet();
            return water.get() < capacity;
        }

        // 先漏水,计算已漏水量
        int leakedWater = (int) ((currentTime - leakTimeStamp) / 1000 * leakRate);
        log.info("上次漏水时间:{}, 当前时间:{}. 已漏水量:{}", leakTimeStamp, currentTime, leakedWater);

        // 如果已漏水量不为0,更新桶中水量
        if (leakedWater != 0) {
            int leftWater = water.get() - leakedWater;
            // 可能水已漏光,设为0
            water.set(Math.max(0, leftWater));
            leakTimeStamp = currentTime;
        }
        log.info("剩余容量:{}", capacity - water.get());

        // 如果桶还有容量,获取许可
        if (water.get() < capacity) {
            log.info("获取许可成功");
            water.incrementAndGet();
            return true;
        } else {
            log.info("获取许可失败");
            return false;
        }
    }
}

测试

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 LeakyBucketRateLimiter leakyBucketRateLimiter
                = new LeakyBucketRateLimiter(3, 1);
        for (int i = 0; i < 15; i++) {
            if (leakyBucketRateLimiter.tryAcquire()) {
                System.out.println("执行任务");
            } else {
                System.out.println("被限流");
            }
            TimeUnit.MILLISECONDS.sleep(500);
        }

先初始化一个漏桶,设置桶的容量为3,每秒放行1个请求,在代码中每500毫秒尝试请求1次

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
09:01:58.404 [main] INFO com.artisan.LeakyBucketRateLimiter - start leaking
执行任务
09:01:58.911 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530118410, currentTime:1693530118911. LeakedWater:0
09:01:58.914 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:2
09:01:58.915 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire success
执行任务
09:01:59.422 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530118410, currentTime:1693530119422. LeakedWater:1
09:01:59.422 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:2
09:01:59.422 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire success
执行任务
09:01:59.934 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530119422, currentTime:1693530119934. LeakedWater:0
09:01:59.934 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:1
09:01:59.934 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire success
执行任务
09:02:00.444 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530119422, currentTime:1693530120444. LeakedWater:1
09:02:00.444 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:1
09:02:00.444 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire success
执行任务
09:02:00.976 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530120444, currentTime:1693530120975. LeakedWater:0
09:02:00.976 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:0
09:02:00.976 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire fail
被限流
09:02:01.489 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530120444, currentTime:1693530121489. LeakedWater:1
09:02:01.490 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:1
09:02:01.490 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire success
执行任务
09:02:02.002 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530121490, currentTime:1693530122002. LeakedWater:0
09:02:02.002 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:0
09:02:02.002 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire fail
被限流
09:02:02.515 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530121490, currentTime:1693530122515. LeakedWater:1
09:02:02.515 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:1
09:02:02.515 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire success
执行任务
09:02:03.016 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530122515, currentTime:1693530123016. LeakedWater:0
09:02:03.016 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:0
09:02:03.016 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire fail
被限流
09:02:03.531 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530122515, currentTime:1693530123531. LeakedWater:1
09:02:03.531 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:1
09:02:03.531 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire success
执行任务
09:02:04.042 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530123531, currentTime:1693530124042. LeakedWater:0
09:02:04.042 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:0
09:02:04.042 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire fail
被限流
09:02:04.553 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530123531, currentTime:1693530124553. LeakedWater:1
09:02:04.553 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:1
09:02:04.553 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire success
执行任务
09:02:05.066 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530124553, currentTime:1693530125066. LeakedWater:0
09:02:05.066 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:0
09:02:05.066 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire fail
被限流
09:02:05.580 [main] INFO com.artisan.LeakyBucketRateLimiter - lastTime:1693530124553, currentTime:1693530125580. LeakedWater:1
09:02:05.580 [main] INFO com.artisan.LeakyBucketRateLimiter - 剩余容量:1
09:02:05.580 [main] INFO com.artisan.LeakyBucketRateLimiter - tryAcquire success
执行任务

Process finished with exit code 0

漏桶算法的缺点,不管当前系统的负载压力如何,所有请求都得进行排队,即使此时服务器的负载处于相对空闲的状态,这样会造成系统资源的浪费。由于漏桶的缺陷比较明显,所以在实际业务场景中,使用的比较少。

令牌桶算法

定义

令牌桶算法是基于漏桶算法的一种改进,主要在于令牌桶算法能够在限制服务调用的平均速率的同时,还能够允许一定程度内的突发调用。

它的主要思想是系统以恒定的速度生成令牌,并将令牌放入令牌桶中,当令牌桶中满了的时候,再向其中放入的令牌就会被丢弃。而每次请求进入时,必须从令牌桶中获取一个令牌,如果没有获取到令牌则被限流拒绝。

假设令牌的生成速度是每秒100个,并且第一秒内只使用了70个令牌,那么在第二秒可用的令牌数量就变成了130,在允许的请求范围上限内,扩大了请求的速率。当然,这里要设置桶容量的上限,避免超出系统能够承载的最大请求数量。

实现

Guava中的RateLimiter就是基于令牌桶实现的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>29.0-jre</version>
</dependency>
每秒产生5个令牌
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 void acquireTest(){
        RateLimiter rateLimiter=RateLimiter.create(5);
        for (int i = 0; i < 10; i++) {
            double time = rateLimiter.acquire();
            log.info("等待时间:{}s",time);
        }
    }

可以看到 每200ms左右产生一个令牌并放行请求,也就是1秒放行5个请求,使用RateLimiter能够很好的实现单机的限流。

应对突发流量

前面提到的突发流量情况,令牌桶是怎么解决的呢?RateLimiter中引入了一个预消费 的概念。

翻译一下:

申请令牌的数量 不同不会影响这个申请令牌这个动作本身的响应时间,acquire(1)和acquire(1000)这两个请求会消耗同样的时间返回结果,但是会影响下一个请求的响应时间。

如果一个消耗大量令牌的任务到达空闲 的RateLimiter,会被立即批准执行,但是当下一个请求进来时,将会额外等待一段时间,用来支付前一个请求的时间成本。

举个例子: 当一个系统处于空闲状态时,突然来了1个需要消耗100个令牌的任务,那么白白等待100秒是毫无意义的浪费资源行为,那么可以先允许它执行,并对后续请求进行限流时间上的延长,以此来达到一个应对突发流量的效果。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   void acquireMultiTest(){
        RateLimiter rateLimiter=RateLimiter.create(1);

        for (int i = 0; i <3; i++) {
            int num = 2 * i + 1;
            log.info("获取{}个令牌", num);
            double cost = rateLimiter.acquire(num);
            log.info("获取{}个令牌结束,耗时{}s",num,cost);
        }
    }

可以看到,在第二次请求时需要3个令牌,但是并没有等3秒后才获取成功,而是在等第一次的1个令牌所需要的1秒偿还后,立即获得了3个令牌得到了放行。

同样,第三次获取5个令牌时等待的3秒是偿还的第二次获取令牌的时间,偿还完成后立即获取5个新令牌,而并没有等待全部重新生成完成。

平滑预热

RateLimiter还具有平滑预热功能,下面的代码就实现了在启动3秒内,平滑提高令牌发放速率到每秒5个的功能

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    void acquireSmoothly(){
        RateLimiter rateLimiter =RateLimiter.create(5,3, TimeUnit.SECONDS);
        long startTimeStamp = System.currentTimeMillis();
        for (int i = 0; i < 15; i++) {
            double time = rateLimiter.acquire();
            log.info("等待时间:{}s, 总时间:{}ms" ,time,System.currentTimeMillis()-startTimeStamp);
        }
    }

可以看到,令牌发放时间从最开始的500ms多逐渐缩短,在3秒后达到了200ms左右的匀速发放。

网关限流

https://docs.spring.io/spring-cloud-gateway/docs/3.1.8/reference/html/#the-requestratelimiter-gatewayfilter-factory

https://docs.spring.io/spring-cloud-gateway/docs/3.1.8/reference/html/#the-redis-ratelimiter

流量整形中间件限流

https://sentinelguard.io/zh-cn/docs/introduction.html

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-09-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
索引的初探(一)
以前听做DBA的朋友说索引能解决数据库百分之八十的问题,我也开始简单的写几篇关于索引的随笔,顺便来总结一下我理解的索引以及相关的知识,毕竟进步在于总结。 简介:      索引是数据库中一个排序的数据结构,以协助快速查询、更新数据库表中数据。我的理解就像是一本书,没有目录你也可以正常阅读,但是想要直接去读某个章节则需要全书寻找,太耽误时间了。所以索引就想目录一样帮助我们去直接定位文章的章节(数据的位置)。 索引结构:    在SQL Server中,索引(聚集索引)存储结构是一样的,都是B树。如图所示:
用户1217611
2018/01/30
5110
索引的初探(一)
SQL Server 重新组织生成索引
概述 无论何时对基础数据执行插入、更新或删除操作,SQL Server 数据库引擎都会自动维护索引。随着时间的推移,这些修改可能会导致索引中的信息分散在数据库中(含有碎片)。当索引包含的页中的逻辑排序(基于键值)与数据文件中的物理排序不匹配时,就存在碎片。碎片非常多的索引可能会降低查询性能,导致应用程序响应缓慢,所以在日常的维护工作当中就需要对索引进行检查对那些填充度很低碎片量大的索引进行重新生成或重新组织,但是在这个过程也需要注意一些小的细节,否则会产生错误。 正文 语法内容载自SQL Server联机
逸鹏
2018/04/11
3K0
SQL Server 重新组织生成索引
浅析一个sql server数据库事务死锁问题
一个学生管理系统,数据库是sql server,有一个Web API用于创建student。student对象的表结构如下:
Bruce Li
2020/02/20
1.3K0
MSSQL之八 实现视图与索引
l 视图可以使用户只关心他感兴趣的某些特定数据和他们所负责的特定任务,而那些不需要的或者无用的数据则不在视图中显示。
张哥编程
2024/12/13
2880
MSSQL之八 实现视图与索引
SQL基础(九)视图与索引实战演练
虽然索引很大程度上提高了查询速度,但同时也会降低更新表的速度,如:对表进行 insert、update 和 delete。这是因为更新表时,不仅要保存数据,还要保存一下索引文件
攻城狮杰森
2022/06/03
4680
SQL Server 索引和视图
索引(Index)是数据库中一种用于加速对表中数据的检索速度的数据结构。索引类似于书籍的目录,它提供了一种快速查找数据行的方法,避免了全表扫描的开销。通过使用索引,数据库系统可以直接定位到符合特定搜索条件的数据,而不必逐行遍历整个表。
神秘泣男子
2024/06/03
2900
SQL Server 索引和视图
聚集索引:SQL Server 进阶 Level 3
作者:David Durant,2013/01/25(首次发布于:2011/06/22) 关于系列 本文是属于Stairway系列:Stairway to SQL Server Indexes 索引是数据库设计的基础,并告诉开发人员使用数据库关于设计者的意图。不幸的是,当性能问题出现时,索引往往被添加为事后考虑。这里最后是一个简单的系列文章,应该使他们快速地使任何数据库专业人员“快速” 这个阶段的前面的层次提供了一般索引和非聚集索引的概述。它以下面关于SQL Server索引的关键概念结束。当请求到达您的数
Woodson
2018/07/19
1.3K0
MySQL 聚集与非聚集索引
在 InnoDB 中如果没有定义主键,会选择第一个非空唯一索引来代替。如果没有这样的索引,InnoDB 会自动生成一个不可见的列名为 ROW_ID,索引名为 GEN_CLUST_INDEX 的聚簇索引,该列是一个 6 字节的自增数值,随着插入而自增。
恋喵大鲤鱼
2024/01/28
2K0
MySQL 聚集与非聚集索引
MySQL 索引
它与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:
network_dream
2023/11/13
1840
MySql的索引学习和使用;(本人觉得足够详细)
索引:对数据库中一列或多列的值进行排序的一种结构 作用:使用索引可以快速访问数据库表中特定信息(加速检索表中的数据)
默 语
2024/11/20
2080
MySql的索引学习和使用;(本人觉得足够详细)
浅谈 SQL Server 查询优化与事务处理
之前我们简单了解了各种查询的用法,然而在实际开发中还会用到一些比较高级的数据处理和查询,包括索引、视图、存储过程和触发器。从而能够更好地实现对数据库的操作、诊断及优化。
小手冰凉
2019/09/10
2.2K0
浅谈 SQL Server 查询优化与事务处理
SQL Server的索引碎片
无论是什么关系型数据库,尤其在OLTP系统中,索引是提升数据访问速度的常用方式之一,但是不同类型的数据库,对索引碎片的处理可能会略有不同。
bisal
2021/09/06
1.7K0
SQL Server的索引碎片
深入解析MySQL索引:本质、分类、选择及使用原则
索引,作为数据库中的一种核心数据结构,其本质在于通过改变数据结构来加快查询效率。可以将索引理解为数据库中的一种“目录”或“路标”,它帮助数据库系统快速定位到需要查询的数据行,从而大大提高数据检索的速度。索引的本质就是一张特殊的表,前面是索引的关键字,后面是这个关键字存放的地址。当数据量庞大时,查找索引比查找全部内容要快得多,而且索引表数据量小,非常节省计算机资源。
小马哥学JAVA
2025/02/20
3610
SQL Server 性能优化之——T-SQL TVF和标量函数
上一篇介绍了关于“临时表、表变量和Union优化”这次转向关注定义函数——也就是表-值函数、标量函数。 UDF(用户定义函数,User defined Function)对于集中精力处理业务逻辑很方便,因为可以在UDF中指定一组业务逻辑,其中可以设计多个存储过程和一些特定的查询语句。但是,由于UDF对CPU的大量请求可能导致性能下降 1. TVF(表-值行数Table-Valued Functions) 一般情况,当使用TVF与一个对象内联接,如果该对象没有索引将会导致TVF像索引扫描或表扫描一样做扫描操作
数据分析
2018/03/01
1.7K0
聚集索引和非聚集索引(转)
索引是关系型数据库中给数据库表中一列或多列的值排序后的存储结构,SQL的主流索引结构有B+树以及Hash结构,聚集索引以及非聚集索引用的是B+树索引。
SuperHeroes
2019/03/12
1.1K0
SQL之视图与索引[通俗易懂]
##视图 人们在使用数据库时,并不是直接对数据源表进行操作,通常人们只关心源表的部分数据,因此为了使得用户在查询时方便,用不着在每次查询时都编写复杂的代码(比如连接等),可以事先将用户要使用的查询结果通过视图定义在数据库中,这样人们在进行查询时只需查看视图即可,简化了用户的操作,同时使得数据同源数据分离,提高了安全性。 1.视图的创建 语法: create view view_name as select_states [with check option] 视图创建注意事项: 1.视图的名称必须唯一,不能与表名重复 2.视图通常只能定义在当前数据库中,分区视图除外 3.可以在视图上定义视图 4.视图中的select定义部分不能包含order by,compute、compute by、default语句 5.不能创建临时视图,也不能创建临时表上的视图 6.当视图中的某一列是计算列等,或者有重名列,则视图必须为每个列名命一个唯一的名称 例子: 创建一个查询student表中人员所选课程成绩大于80分的视图 代码:
全栈程序员站长
2022/09/16
9140
SQL之视图与索引[通俗易懂]
SQL Server 深入解析索引存储(下)
概述 非聚集索引与聚集索引具有相同的 B 树结构,它们之间的显著差别在于以下两点: 基础表的数据行不按非聚集键的顺序排序和存储。 非聚集索引的叶层是由索引页而不是由数据页组成。 既可以使用聚集索引来为表或视图定义非聚集索引,也可以根据堆来定义非聚集索引。非聚集索引中的每个索引行都包含非聚集键值和行定位符。此定位符指向聚集索引或堆中包含该键值的数据行。 非聚集索引行中的行定位器或是指向行的指针,或是行的聚集索引键,如下所述: 如果表是堆(意味着该表没有聚集索引),则行定位器是指向行的指针。该指针由文件标
逸鹏
2018/04/11
9900
SQL Server 深入解析索引存储(下)
Windows server 2016——查询优化与事务处理
本系列文章将会讲解SQL server 中 查询优化与事务处理,了解使用索引工具,使用视图,存储过程,触发器等操作。
网络豆
2023/10/17
4540
Windows server 2016——查询优化与事务处理
sql server 聚集索引,非聚集索引,Identity ,gudi,主键的概念和比较
可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。
SpringSun
2022/05/26
1.2K0
sql server 聚集索引,非聚集索引,Identity ,gudi,主键的概念和比较
阅读查询计划:SQL Server 索引进阶 Level 9
David Durant,2011/10/05 关于系列 本文是属于Stairway系列:Stairway to SQL Server Indexes 索引是数据库设计的基础,并告诉开发人员使用数据库关于设计者的意图。不幸的是,当性能问题出现时,索引往往被添加为事后考虑。这里最后是一个简单的系列文章,应该使他们快速地使任何数据库专业人员“快速” 在整个阶段,我们经常说某个查询以某种方式执行,我们引用生成的查询计划来支持我们的陈述。 Management Studio显示的估计和实际查询计划可以帮助您确定索引
Woodson
2018/07/19
1.2K0
相关推荐
索引的初探(一)
更多 >
LV.1
这个人很懒,什么都没有留下~
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档