缓冲区与缓存(buffer与cache)

缓冲区与缓存是提高系统效率的关键技术。缓冲区用于存储输入输出设备间的临时数据,减少数据读写次数,如Python中的StringIO和BytesIO。缓存适用于数据实时性要求不高、性能要求高的场景,常见更新模式包括Cache Aside、Read/Write Through和Write Behind Caching。Python中可使用functools.lru_cache实现缓存功能,但不支持缓存过期和清除操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

缓冲区与缓存(buffer与cache)

1.缓冲区buffer

缓冲区(buffer),它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,显然缓冲区是具有一定大小的。

缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

  • 高速设备与低速设备的不匹配,势必会让高速设备花时间等待低速设备,我们可以在这两者之间设立一个缓冲区。

缓存区的作用

  1. 可以解除两者的制约关系,数据可以直接送往缓冲区,高速设备不用再等待低速设备,提高了计算机的效率。例如:我们使用打印机打印文档,由于打印机的打印速度相对较慢,我们先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时我们的CPU可以处理别的事情。

  2. 可以减少数据的读写次数,如果每次数据只传输一点数据,就需要传送很多次,这样会浪费很多时间,因为开始读写与终止读写所需要的时间很长,如果将数据送往缓冲区,待缓冲区满后再进行传送会大大减少读写次数,这样就可以节省很多时间。例如:我们想将数据写入到磁盘中,不是立马将数据写到磁盘中,而是先输入缓冲区中,当缓冲区满了以后,再将数据写入到磁盘中,这样就可以减少磁盘的读写次数,不然磁盘很容易坏掉。

简单来说,缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来存储数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。

Python中的缓冲

  • StringIO和BytesIO就使用了缓冲技术

缓存区的类型

缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。

  1. 全缓冲
    在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
  2. 行缓冲
    在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。
  3. 不带缓冲
    也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。

2.缓存cache

缓存是现在系统中必不可少的模块,并且已经成为了高并发高性能架构的一个关键组件。这篇博客我们来分析一下使用缓存的正确姿势

  • 提升性能
    绝大多数情况下,select 是出现性能问题最大的地方。一方面,select 会有很多像 join、group、order、like 等这样丰富的语义,而这些语义是非常耗性能的;另一方面,大多 数应用都是读多写少,所以加剧了慢查询的问题。
    分布式系统中远程调用也会耗很多性能,因为有网络开销,会导致整体的响应时间下降。为了挽救这样的性能开销,在业务允许的情况(不需要太实时的数据)下,使用缓存是非常必要的事情。
  • 缓解数据库压力
    当用户请求增多时,数据库的压力将大大增加,通过缓存能够大大降低数据库的压力。

缓存的适用场景

  • 对于数据实时性要求不高
    对于一些经常访问但是很少改变的数据,读明显多于写,适用缓存就很有必要。比如一些网站配置项。
  • 对于性能要求高
    比如一些秒杀活动场景。

缓存的三种模式

  • Cache Aside 更新模式
  • Read/Write Through 更新模式
  • Write Behind Caching 更新模式
    通俗一点来讲就是,同时更新缓存和数据库(Cache Aside 更新模式);先更新缓存,缓存负责同步更新数据库(Read/Write Through 更新模式);先更新缓存,缓存定时异步更新数据库(Write Behind Caching 更新模式)。这三种模式各有优劣,可以根据业务场景选择使用。
  • 详细请参考https://www.cnblogs.com/llzhang123/p/9037346.html

Python中的缓存

  • functools.lru_cache(maxsize=128,typed=False) #cache缓存。为函数添加缓存机制。通过一个字典缓存被装饰函数的调用和返回值。

    1. Least-recently-used装饰器。lru,最近最少使用。cache缓存
    2. maxsize:如果maxsize设置为None,则禁用LRU功能,并且缓存可以无限制增长。当maxsize是二的幂时,LRU功能执行得最好
    3. 如果typed设置为True,则不同类型的函数参数将单独缓存。例如f(3)和f(3.0)将被视为具有不同结果的不同调用
    4. 示例
    import functools
    import time
    
    @functools.lru_cache()
    def add(x,y):
        time.sleep(1)
        return x + y
    
    print(add(4,5))
    print(add(4.0,5))
    print(add(4,6))
    print("--------------")
    print(add(4,5)) #会使用缓存,无需等待,直接出结果
    print(add(4.0,5))  #会使用缓存,无序等待,直接出结果
    print(add(4,6)) #会使用缓存,无序等待,直接出结果
    print("-------------------")
    print(add(4,y=6)) #无法使用缓存
    print(add(x=4,y=6))  #无法使用缓存
    print(add(y=6,x=4)) #无法使用缓存
    

    funct_001

  • lru_cache装饰器

    1. 斐波拉契数列递归方法的改造
    import functools,datetime
    
    def fib(n):
        return 1 if n<3 else fib(n-1)+fib(n-2)
    
    @functools.lru_cache(maxsize=None)
    def fib2(n):
        return 1 if n<3 else fib2(n-1)+fib2(n-2)
    
    dt = datetime.datetime.now()
    print([fib2(i+1) for i in range(35)])
    dt1 = datetime.datetime.now()
    print("(fib2)使用lru_cache缓存技术,用时:{}".format((dt1-dt).total_seconds()))
    print([fib(i+1) for i in range(35)])
    dt2 = datetime.datetime.now()
    print("(fib)不使用lru_cache缓存技术,用时:{}".format((dt2-dt1).total_seconds()))
    

    funct_002

  • lru_cache装饰器应用

    1. 使用前提
      • 同样的函数参数一定得到同样的结果
      • 函数执行时间很长,且要多次执行
    2. 本质是函数调用函数=》返回值
    3. 缺点
      • 不支持缓存过期,key无法过期、失效
      • 不支持清除操作
      • 不支持分布式,是一个单机的缓存
    4. 适用场景,单机上需要空间换时间的地方,可以用缓存来将计算变成快速的查询
  • 实现一个cache装饰器,实现可过期被清除的功能

    1. 简化设计,函数的形参定义不包含可变位置参数、可变关键字参数和keyword-only参数 可以不考虑缓存大小,也不用考虑缓存满了之后的换出问题
import inspect,functools,datetime

def xdd_time(fn):
    """
    时间统计,统计函数执行时间
    :param fn: 需要装饰的函数
    :return: 包装后的fn
    """
    @functools.wraps(fn)
    def wrapper(*args,**kwargs):
        start = datetime.datetime.now()
        req = fn(*args,**kwargs)
        timeout = datetime.datetime.now() - start
        print("{}方法耗时:{},args={},kwargs={},\n执行结果:req={}".format(fn.__name__,timeout.total_seconds(),args,kwargs,req))
        return req
    return wrapper

def xdd_cache(duration = 5):
    def _xdd_cache(fn):
        """
            为函数生成缓存机制,具有有效时长功能
        :param fn:
        :param duration:缓存时长,单位秒,如果为None表示不计算时长,默认全部缓存
        :return:
        """
        xdd_dict = {} #定义一个字典,用来存参数与对应的计算结果
        sig = inspect.signature(fn) #对函数进行签名

        @functools.wraps(fn)
        def wrapper(*args,**kwargs):
            paramestr = getparamestr(args,kwargs) #根据参数获取字符串
            req = xdd_dict.get(paramestr,None)
            if req is None:
                req = fn(*args,**kwargs),datetime.datetime.now()
                xdd_dict[paramestr] = req
            elif not duration is None and (datetime.datetime.now() - req[1]).total_seconds() > duration: #超时
                print("缓存超时")
                req = fn(*args, **kwargs),datetime.datetime.now()
                xdd_dict[paramestr] = req
            return req[0]

        def getparamestr(args,kwargs)->str:
            """
             格式化参数,将参数格式化为指定格式的字符串
            :param args:
            :param kwargs:
            :return:
            """
            parames = sig.parameters
            pstr = ()
            isargs = True #标记是否还有位置参数
            kwdict = {}
            for k,(name,v) in enumerate(parames.items()):
                v:inspect.Parameter = v
                if name == "args": #位置参数已经全部匹配完成
                    isargs = False
                    pstr += ("args",tuple(args[k+1:])) #记录剩下的位置参数
                    continue
                if name == "kwargs": #关键字位置参数已经匹配完
                    pstr += ("kwargs",tuple((k,kwargs[k]) for k in kwargs.keys()-kwdict.keys()))
                    break
                if isargs:  # 位置参数还没匹配完
                    if k<len(args):
                        pstr += (name, args[k])
                    else: #实际传参中位置参数已经传完
                        kwval = kwargs.get(name,v.default)
                        kwdict[name] = kwval  # 记录已经出现的位置参数
                        pstr += (name,kwval)
                else:
                    kwval = kwargs.get(name, v.default)
                    kwdict[name] = kwval #记录已经出现的位置参数
                    pstr += (name,kwval)
                # print(k,name,v.name,v.annotation,v.default)
            return pstr
        return wrapper
    return _xdd_cache

# @gdy_cache
# def add(x,y:int=5,*args,z=4,b:int,c=6,**kwargs):
#     import time
#     time.sleep(3)
#     return x+y
@xdd_time
@xdd_cache()
def add(x=4,y=5):
    import time
    time.sleep(3)
    return x+y
print("开始计算")
print(1,add(4,5))
import time
time.sleep(5)
print(2,add(4))
# print(3,add(y=5))
# print(4,add(x=4,y=5))
# print(5,add(y=5,x=4))
# print(6,add())

homework190422_003

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值