• Redis简单介绍


    目录

     Redis介绍

     redis 为什么这么快

    redis 应用场景(了解)

    下载安装

    ​编辑    win版本下载地址

    redis服务    

    pycharm 操作redis

     redis连接池

    ​编辑使用连接池链接

    redis 数据库

     字符串

     hash 

    列表操作

    redis管道

    redis其他操作

    django中集成redis

    方式一: 直接使用

    方式二: 使用第三方模块django-redis

    下载模块:

    方式三: 借助于django的缓存使用redis

     Redis介绍

    redis是一款非关系型的nosql数据库【存数据的地方,使用内存存储,速度非常快, 可以持久化存储数据数据【从内存同步到硬盘】。 

    数据类型丰富【5大数据类型:字符串,列表,哈希(字典),集合,有序集合】,以key-value形式存储【根本没有表的结构,相当于咱们的字典】
        
        -nosql:指非关系型数据库:1 不限于SQL  2 没有sql
        


     redis 为什么这么快

        -1 高性能的网络模型:IO多路复用的epoll模型,承载住非常高的并发量
        -2 纯内存操作,避免了很多io
        -3 单线程架构,避免了线程间切换的消耗
            -6.x之前:单线程,单进程
            -6.x以后,多线程架构,数据操作还是使用单线程,别的线程做数据持久化,其他操作

    redis 应用场景(了解)


        1 当缓存数据库使用,接口缓存,提高接口响应速度
            -请求进到视图---》去数据查询[多表查询,去硬盘取数据:速度慢]----》转成json格式字符串---》返回给前端
            -请求进到视图---》去redis[内存]----》取json格式字符串---》返回给前端
        2 做计数器:单线程,不存在并发安全问题
            -统计网站访问量
            -个人站点浏览量
            -文章阅读量
            
        3 去重操作:集合
        4 排行榜:有序集合
            -阅读排行榜
            -游戏金币排行榜
            
        5 布隆过滤器
        6 抽奖
        7 消息队列

    下载安装

    redis是一款开源软件:使用c语言写的---【编译型语言,在操作系统运行,要编译成可执行文件,由于采用了IO多路复用的epoll模型,所以它不支持windows,只有linux操作系统支持epoll】

    微软官方:改了,编译成可执行的安装包,下载一路下一步安装
        -版本没有最新

    官网:https://redis.io/
        -下载完是源代码:c语言源码 :

    https://redis.io/download/#redis-stack-downloads

        -最稳定:6.x
        -最新7.x

    中文网:

    http://redis.cn/download.html


        -上面最新只到5.x
        


        
    win版本下载地址


       最新5.x版本 https://github.com/tporadowski/redis/releases/
       最新3.x版本 https://github.com/microsoftarchive/redis/releases
        下载完一路下一步即可,具体可参照:https://www.cnblogs.com/liuqingzheng/p/9831331.html
        

    redis服务
        

    win装完会有redis服务
        -启动服务,手动停止
        -客户端链接:

    redis-cli -h 127.0.0.1 -p 6379

     -简单命令:

    1.     set name yietong
    2.         get name
    3.         ping

           
        -停掉服务:
            -去win服务点关闭
            -客户端关闭:

    shutdown

     

         redis本质也是cs架构的软件
    redis 服务器端
    redis 客户端 
        -redis-cli
        -图形化工具:redis-desktop-manager
        -python操作
        

    pycharm 操作redis

    安装包

    pip3 install redis
    1. from redis import Redis
    2. conn=Redis( host="localhost",port=6379)
    3. # conn.set('name','yietong')
    4. print(conn.get('name'))
    5. conn.close()

     redis连接池

    POOL.py

    1. import redis
    2. pool = redis.ConnectionPool(max_connections=200, host='127.0.0.1', port=6379)

    redis-pool-demo.py

    1. # 直接连接
    2. from redis import Redis
    3. from threading import Thread
    4. # 直接链接
    5. def get_name_from_redis():
    6. conn = Redis(host="localhost", port=6379)
    7. print(conn.get('name'))
    8. conn.close()
    9. for i in range(100):
    10. t=Thread(target=get_name_from_redis)
    11. t.start()
    12. import time
    13. time.sleep(10)


    使用连接池链接

    1. import redis
    2. from POOL import pool
    3. def get_name_from_redis():
    4.     # 创建一个连接池,保证它是单例,全局只有一个pool对象:使用模块导入方式实现单例
    5.  
    6.     conn = redis.Redis(connection_pool=pool) #m每执行一次会从池中取一个链接,如果没有,等待
    7.     res=conn.get('name')
    8.     print(res)
    9.     conn.close()
    10. for i in range(100):
    11.     t=Thread(target=get_name_from_redis)
    12.     t.start()
    13. import time
    14. time.sleep(10)

    redis 数据库

    Redis 支持的数据类型概述

    Redis 是一个数据结构服务器。 Redis 的核心是提供一系列本机数据类型,可帮助您解决从缓存到队列再到事件处理的各种问题。

     字符串


    Redis 字符串是最基本的 Redis数据类型,表示字节序列。

    # 字符串
    1 set(name, value, ex=None, px=None, nx=False, xx=False)
    2 setnx(name, value)
    3 psetex(name, time_ms, value)
    4 mset(*args, **kwargs)
    4 get(name)
    5 mget(keys, *args)
    6 getset(name, value)
    7 getrange(key, start, end)
    8 setrange(name, offset, value)
    9 setbit(name, offset, value)
    10 getbit(name, offset)
    11 bitcount(key, start=None, end=None)
    12 strlen(name)
    13 incr(self, name, amount=1)
    14 incrbyfloat(self, name, amount=1.0)
    15 decr(self, name, amount=1)
    16 append(key, value)
    ##   记住的:get set strlen

    set()

    1. def set(
    2.     self,
    3.     name: KeyT,
    4.     value: EncodableT,
    5.     ex: Union[ExpiryT, None] = None,  # 过期时间(秒)
    6.     px: Union[ExpiryT, None] = None,  # 过期时间(毫秒)
    7.     nx: bool = False,  # 如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果。
    8.     xx: bool = False,  # 如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值。
    9.     keepttl: bool = False,  # 如果为True,保留与该键关联的生存时间。
    10.     get: bool = False,  # 如果为True,将键name处的值设置为value并返回旧值存储在key中,如果key不存在则为None。
    11.     exat: Union[AbsExpiryT, None] = None,  # 设置指定的Unix时间,该键将在该时间到期,单位为秒。
    12.     pxat: Union[AbsExpiryT, None] = None,  # 设置指定的Unix时间,该键将在该时间到期,单位为毫秒。
    1. import redis
    2. conn = redis.Redis()
    3. conn.set('name', 'yietong')
    4. conn.set('age', 22)
    5. conn.close()

    setnx()

    1. # 相当于set(..., nx=True)
    2. def setnx(self, name: KeyT, value: EncodableT) -> ResponseT:
    1. conn.setnx('name','xxx')  # 存在不修改,执行没效果
    2. conn.setnx('hobby','pingpang')

    psetex()

    1. # 本质就是 set(..., px)设置时间(毫秒)
    2. def psetex(self, name: KeyT, time_ms: ExpiryT, value: EncodableT):
    1. conn.psetex('name', 3000, 'yietong')

    mset()

    1. # 批量设置
    2. def mset(self, mapping: Mapping[AnyKeyT, EncodableT]) -> ResponseT:
    conn.mset({'name': 'yietong', 'age': 22, 'xxx': 'yyy'})

    get()

    def get(self, name: KeyT) -> ResponseT:
    1. # 获取值,取到是bytes格式
    2. # 可以在实例化redis对象时指定参数decode_responses=True
    3. res = conn.get('name')
    4. print(res)  # b'yietong'

    mget()

    1. # 批量获取
    2. def mget(self, keys: KeysT, *args: EncodableT) -> ResponseT:
    1. res = conn.mget('name', 'age')
    2. print(res)  # [ b'yietong', b'22']

    getset()

    1. # 先获取在设置
    2. def getset(self, name: KeyT, value: EncodableT) -> ResponseT:
    1. res = conn.getset('age', 30)
    2. print(res)  # b'22'

    getrange()

    1. # 取的是字节,左闭右闭区间。
    2. def getrange(self, key: KeyT, start: int, end: int) -> ResponseT:
    1. res = conn.getrange('name',0,3)
    2. print(res)  # b'yietong'

    setrange()

    1. '''
    2. 从offset开始重新设置值,如果``offset``加上``value``的长度超过原始值的长
    3. 度,新值将比以前大。如果``offset``超过了原始值的长度,则用null填充。
    4. '''
    5. def setrange(self, name: KeyT, offset: int, value: EncodableT) -> ResponseT:
    1. res = conn.setrange('name',10, 'goodmorning')
    2. print(res)  # 22

    setbit()

    1. # 设置或清除存储在键的字符串值中的偏移位。
    2. # 根据值设置或清除位,该值可以是 0 或 1.
    3. def setbit(self, name: KeyT, offset: int, value: int) -> ResponseT:

    bitcount()

    1. # 返回` ' key ' `的值中设置的位数。可选``start``和``end``参数表示要考虑哪些字节
    2. def bitcount(
    3.     self,
    4.     key: KeyT,
    5.     start: Union[int, None] = None,
    6.     end: Union[int, None] = None,
    7.     mode: Optional[str] = None,
    8. ) -> ResponseT
    1. res = conn.bitcount('hobby', 0, 6)
    2. print(res)  # 28

    strlen()

    1. # 返回` ' name ' `的值中存储的字节数
    2. def strlen(self, name: KeyT) -> ResponseT:
    print(conn.strlen('hobby'))  # 8

    incr()

    def incr(self, name: _Key, amount: int = ...) -> int: ...
    1. res = conn.incr('age', amount=2)
    2. print(res)

    incrbyfloat()

    def incrbyfloat(self, name: KeyT, amount: float = 1.0) -> ResponseT:

    decr()

    def decr(self, name, amount: int = ...) -> int: ...
    print(conn.decr('age'))

    append()
    将字符串’ ’ value ’ ‘附加到' key '的值上。如果“键”,不存在value创建它。返回值’ ’ key ’ '的新长度。

    def append(self, key: KeyT, value: EncodableT) -> ResponseT:
    print(conn.append('name', 'hahaha'))  # 14
    

     hash 

    '''
    1 hset(name, key, value)
    2 hmset(name, mapping)
    3 hget(name,key)
    4 hmget(name, keys, *args)
    5 hgetall(name)
    6 hlen(name)
    7 hkeys(name)
    8 hvals(name)
    9 hexists(name, key)
    10 hdel(name,*keys)
    11 hincrby(name, key, amount=1)
    12 hincrbyfloat(name, key, amount=1.0)
    13 hscan(name, cursor=0, match=None, count=None)
    14 hscan_iter(name, match=None, count=None)
    '''

    ##   记住:hset hget  hexists hincrby hlen


    hset() 

    1. '''
    2. 在hash `name`中设置`key`为`value`,
    3. ``mapping``接受一个由键值对组成的字典
    4. 添加到hash ``name``中。
    5. ``items``接受一个键值对列表作为参数
    6. 添加到hash ``name``中。
    7. 返回添加的字段数。
    8. '''
    9. def hset(
    10.      self,
    11.      name: str,
    12.      key: Optional[str] = None,
    13.      value: Optional[str] = None,
    14.      mapping: Optional[dict] = None,
    15.      items: Optional[list] = None,
    16.  ) -> Union[Awaitable[int], int]:
    1. res = conn.hset('info', 'name', 'yietong', mapping={'age': 12, 'xx': 'yy'})
    2. print(res)  # 3

    hmset()
    将key设置为对应哈希name中的值,mapping字典中的键和值。

    def hmset(self, name: str, mapping: dict) -> Union[Awaitable[str], str]:
    1. res = conn.hmset('info',{'xx':'xx', 'yy':'yy'})
    2. print(res)
    3. #  DeprecationWarning: Redis.hmset() is deprecated. Use Redis.hset() instead.
    4. #  res = conn.hmset('info',{'xx':'xx', 'yy':'yy'})
    5. # 已被弃用,使用hset()即可。

    hget()

    返回哈希值key中的值name

    1. def hget(
    2.     self, name: str, key: str
    3. ) -> Union[Awaitable[Optional[str]], Optional[str]]:

    1. res = conn.hget('info', 'xx')
    2. print(res)  # b'xx'

    hmget()

    返回与keys顺序相同的值的列表。

    def hmget(self, name: str, keys: List, *args: List) -> Union[Awaitable[List], List]:
    1. res = conn.hmget('info', ('xx', 'yy'))
    2. print(res)  # [b'xx', b'yy']

    hgetall()

    返回一个Python字典,由散列的名称/值对组成

    def hgetall(self, name: str) -> Union[Awaitable[dict], dict]:
    1. res = conn.hgetall('info')
    2. print(res)  # {b'name': b'yietong', b'age': b'22', b'xx': b'xx', b'yy': b'yy'}

    hlen()

    返回哈希值name中的元素个数

    def hlen(self, name: str) -> Union[Awaitable[int], int]:
    1. res = conn.hlen('info')
    2. print(res)  # 4

    hkeys()

    返回hash name 中的键列表

    def hkeys(self, name: str) -> Union[Awaitable[List], List]:
    1. res = conn.hkeys('info')
    2. print(res)  # [b'name', b'age', b'xx', b'yy']

    hvals()

    返回hash name中的值列表

    def hvals(self, name: str) -> Union[Awaitable[List], List]:
    1. res = conn.hvals('info')
    2. print(res)  # [b'yietong', b'18', b'xx', b'yy']

    hexists()

    返回一个布尔值,表示key是否存在于哈希name中。

    def hexists(self, name: str, key: str) -> Union[Awaitable[bool], bool]:
    1. res = conn.hexists('info','xx')
    2. print(res)  # True

    hdel()

    从hash name中删除keys

    def hdel(self, name: str, *keys: List) -> Union[Awaitable[int], int]:
    1. res = conn.hdel('info','xx', 'yy')
    2. print(res)  # 2

    hincrby()

    将hash name中的key的值增加amount

    1. def hincrby(
    2.     self, name: str, key: str, amount: int = 1
    3. ) -> Union[Awaitable[int], int]:
    1. conn.hset('ingo','age',18)
    2. res = conn.hincrby('info','age',2)
    3. print(res)  # 20

    hincrbyfloat()

    将hash name中的key的值增加amount

    1. def hincrby(
    2.     self, name: str, key: str, amount: int = 1
    3. ) -> Union[Awaitable[int], int]:

    hscan()

    在哈希表中递增地返回键值对切片,同样返回一个游标。
    表示扫描位置。
    match允许按模式过滤键
    count允许提示最小返回次数

    1. def hscan(
    2.     self,
    3.     name: KeyT,
    4.     cursor: int = 0,
    5.     match: Union[PatternT, None] = None,
    6.     count: Union[int, None] = None,
    7. ) -> ResponseT:

    count不准确,有点上下浮动。

    1. res = conn.hscan('test', cursor=0, count=7)
    2. print(res)
    3. print(len(res[1]))
    4. print(res[0])
    5. res = conn.hscan('test', cursor=res[0], count=7)
    6. print(res)
    7. print(len(res[1]))
    8. print(res[0])

    hscan_iter()

    使用HSCAN命令创建一个迭代器,这样客户端就不需要迭代,需要记住光标的位置。
    match允许按模式过滤键
    count允许提示最小返回次数

    1. def hscan_iter(
    2.      self,
    3.      name: str,
    4.      match: Union[PatternT, None] = None,
    5.      count: Union[int, None] = None,
    6.  ) -> Iterator:
    7.     cursor = "0"
    8.     while cursor != 0:
    9.         cursor, data = self.hscan(name, cursor=cursor, match=match, count=count)
    10.         yield from data.items()

    1. res = conn.hscan_iter('test')
    2. print(res)  # 生成器
    3. for i in res:
    4.     print(i)
    5. conn.close()


    列表操作

    1 lpush(name,values)
    2 lpushx(name,value)
    3 rpushx(name, value) 表示从右向左操作
    4 llen(name)
    5 linsert(name, where, refvalue, value))
    6 lset(name, index, value)
    7 lrem(name, value, num)
    8 lpop(name)
    9 lindex(name, index)
    10 lrange(name, start, end)
    11 ltrim(name, start, end)
    12 rpoplpush(src, dst)
    13 blpop(keys, timeout)
    14 brpoplpush(src, dst, timeout=0)
    15 自定义增量迭代


    lpush()

    将values推到列表的头部name

    def lpush(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
    1. res = conn.lpush('redis_list', 1, 2, 3, 4, 5)
    2. print(res)  # 5

    lpushx()

    如果name存在,将value推到name的头部

    def lpushx(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
    1. res = conn.lpushx('redis_list', 6, 7, 8)
    2. print(res)  # 8

    rpush()和rpushx()

    与上面两个相反,推到列表的尾部。

    llen()

    返回列表的长度name

    def llen(self, name: str) -> Union[Awaitable[int], int]:
    1. res = conn.llen('redis_list')
    2. print(res)  # 8

    linsert()

    在列表中插入元素,该元素存储在基准值基准值之前或之后。
    如果key不存在,则认为它是一个空列表,不会执行任何操作。
    如果key存在但没有保存列表值,则返回错误。

    1. def linsert(
    2.      self, name: str, where: str, refvalue: str, value: str
    3.  ) -> Union[Awaitable[int], int]:
    1. res = conn.linsert('redis_list', 'before', '3', 'new')
    2. print(res)  # 9

    lset()

    将list name的index元素设置为value

    def lset(self, name: str, index: int, value: str) -> Union[Awaitable[str], str]:
    1. res = conn.lset('redis_list', 0, 'lset')
    2. print(res)  # True

    lrem()

    删除第一次count等于value的元素从存储在name中的列表中。

    def lrem(self, name: str, count: int, value: str) -> Union[Awaitable[int], int]:
    1. res = conn.lrem('redis_list', 1, '5')
    2. print(res)  # 1

    lpop()

    删除并返回列表name的第一个元素。

    默认情况下,该命令从开头弹出一个元素从列表中。当提供可选的count参数时,响应将由多数量的元素组成,这取决于列表的长度。

    从 Redis 版本 6.2.0 开始:添加了参数count。

    def lpop(self, name: str, count: Optional[int] = None) -> Union[str, List, None]:
    1. res = conn.lpop('redis_list')
    2. print(res)  # b'lset'

    lindex()

    从列表name中返回位置index
    支持负索引,从列表末尾开始

    1. def lindex(
    2.      self, name: str, index: int
    3.  ) -> Union[Awaitable[Optional[str]], Optional[str]]:
    1. res = conn.lindex('redis_list', 0)
    2. res1 = conn.lindex('redis_list', -1)

    lrange()

    返回列表name之间的一个切片
    定位start和end
    start和end也可以是负数
    Python切片表示法

    def lrange(self, name: str, start: int, end: int) -> Union[Awaitable[list], list]:
    1. res = conn.lrange('redis_list',0,-1)
    2. print(res)

    ltrim()

    修剪列表name,删除所有不在切片内的
    在start和end之间

    start和end也可以是负数

    Python切片表示法

    def ltrim(self, name: str, start: int, end: int) -> Union[Awaitable[str], str]:
    res = conn.ltrim('redis_list',1,-2)

    rpoplpush()

    从src列表中取出一个值RPOP,然后在dst列表上LPUSH。并返回rpop的值。

    def rpoplpush(self, src: str, dst: str) -> Union[Awaitable[str], str]:
    1. conn.lpush('new_list', '1')
    2. res = conn.rpoplpush('redis_list', 'new_list')
    3. print(res)  # b'new'

    blpop()

    从第一个非空列表中取出一个值

    在 keys 列表中命名。

    如果keys中的列表都没有值可以LPOP,则阻塞timeout秒,或者直到一个值被推送给一个值名单的。

    如果timeout为0,则无限期阻塞。

    1. def blpop(
    2.      self, keys: List, timeout: Optional[int] = 0
    3.  ) -> Union[Awaitable[list], list]:
    1. res = conn.blpop('redis_list')
    2. print(res)

    brpoplpush()

    BRPOPLPUSH是RPOPLPUSH 的阻塞变体。 当包含元素时,此命令的行为与RPOPLPUSH 完全相同。 当在MULTI/EXEC块中使用时,此命令的行为与RPOPLPUSH 完全相同。 当为空时,Redis 将阻止连接,直到另一个客户端 推到它或直到到达。 零点可用于无限期阻止。

    1. def brpoplpush(
    2.      self, src: str, dst: str, timeout: Optional[int] = 0
    3.  ) -> Union[Awaitable[Optional[str]], Optional[str]]:
    1. conn.lpush('redis_list', 1, 2, 3, 4)
    2. res = conn.brpoplpush('redis_list', 'new_list')
    3. print(res)

    redis管道

    MySQL事务的四大特性为ACID:隔离性,一致性,原子性,永久性。 

    redis数据库是否支持事务呢?

    redis事务机制留一保证一致性和隔离性, 无法保证持久性,打死你hi对于redis而言,本身他是应该内存数据库,所以持久化不是必须属性, 原子性需要自己进行检测,尽可能保证。 

    redis不像mysql一样支持强事务, 事务的四大特性只能满足其中一部分, 但是通过redis的管道可以实现部分事务。

    redis通过管道,来保证命令要么都成功,要么都失败, 完成十五的一致性,但是管道只能哟弄个在单实例,集群的环境中,不支持pipline

    1. import redis
    2. conn = redis.Redis() # 实例化
    3. pipline = conn.pipeline(transaction=True)
    4. pipline.decr('a', 2) # a减2
    5. raise Exception('我崩了')
    6. pipline.incr('b', 2) # b加2
    7. pipline.execute()
    8. conn.close()

    redis其他操作

    集合,有序集合, redis模块提供的方法API

    通用操作: 五大类行都支持

    1. import redis
    2. conn = redis.Redis()
    3. # 1 delete(*names)
    4. conn.delete('age', 'name')
    5. # 2 exists(name)
    6. res=conn.exists('xx')
    7. print(res) # 0
    8. # 3 keys(pattern='*')
    9. res=conn.keys('*o*')
    10. # res=conn.keys('?o*')
    11. print(res)
    12. # 4 expire(name ,time)
    13. conn.expire('test_hash',3)
    14. # 5 rename(src, dst) # 对redis的name重命名为
    15. conn.rename('xx','xxx')
    16. # 6 move(name, db) # 将redis的某个值移动到指定的db下
    17. # 默认操作都是0 库,总共默认有16个库
    18. conn.move('xxx',2)
    19. # 7 randomkey() 随机获取一个redis的name(不删除)
    20. res=conn.randomkey()
    21. print(res)
    22. # 8 type(name) 查看类型
    23. res = conn.type('aa') # list hash set
    24. print(res)
    25. conn.close()

    django中集成redis

    方式一: 直接使用

    1. from user.POOL import pool
    2. import redis
    3. def index(request):
    4. conn = redis.Redis(connection_pool=pool)
    5. conn.incr('page_view')
    6. res = conn.get('page_view')
    7. return HttpResponse('被你看了%s次' % res)

    方式二: 使用第三方模块django-redis

    下载模块:

     pip3 install django=redis

    配置文件配置

    1. CACHES = {
    2. "default": {
    3. "BACKEND": "django_redis.cache.RedisCache",
    4. "LOCATION": "redis://127.0.0.1:6379/0",
    5. "OPTIONS": {
    6. "CLIENT_CLASS": "django_redis.client.DefaultClient",
    7. "CONNECTION_POOL_KWARGS": {"max_connections": 100}
    8. # "PASSWORD": "123",
    9. }
    10. }
    11. }

    使用

    1. from django_redis import get_redis_connection
    2. def index(request):
    3. conn = get_redis_connection(alias="default") # 每次从池中取一个链接
    4. conn.incr('page_view')
    5. res = conn.get('page_view')
    6. return HttpResponse('被你看了%s次' % res)

    方式三: 借助于django的缓存使用redis

    如果配置文件配置了CACHES,以后django的缓存,数据直接放在redis中

    直接使用cache.set设置值,【可以上传过期时间】

    使用cache.get来获取值。 其强大之处在于: 可以直接缓存任意的python对象,因为其底层使用的pickle实现的。pickle可以序列化python的任意数据类型

    访问test 

  • 相关阅读:
    vue-cli搭建一个新项目及基础配置
    程序运行时增加语音提示
    解决ntfs-3g-mount: mount failed硬盘无法挂载的问题
    JVM 内存设置大小(Xms Xmx PermSize MaxPermSize 区别)
    Python-进程和线程
    96-Java的打印流、打印流重定向、补充知识:Properties、commons-io框架
    网卡配置文件丢失
    直播预约|Flink + StarRocks 实时数据分析新范式
    《论文阅读》开放域对话系统——外部信息融入对话的方法
    LeetCode 0792. 匹配子序列的单词数
  • 原文地址:https://blog.csdn.net/weixin_67531112/article/details/127847630