redis深度历险 1 - Redis基础数据结构-001

Redis有5种基础数据结构,分别为: string (字符串)、list(列表)、set (集合)、hash (哈希)和zset (有序集合)。熟练掌握这5种基本数据结构的使用是Redis 知识最基础也最重要的部分,它也是在 Redis面试题中问到最多的内容。

1 字符串 string

字符串 string 是 Redis最简单的数据结构。Redis所有的数据结构都是以唯一的 key字符串作为名称,然后通过这个唯一 key 值来获取相应的 value数据。不同类型的数据结构的差异就在于value的结构不一样。
在这里插入图片描述
字符串结构使用非常广泛,一个常见的用途就是缓存用户信息。我们将用户信息结构体使用JSON序列化成字符串,然后将序列化后的字符串塞进Redis来缓存。同样,取用户信息会经过一次反序列化的过程。

在这里插入图片描述
Redis的字符串是动态字符串,是可以修改的字符串,内部结构实现上类似于Java 的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配,如图中所示,内部为当前字符串实际分配的空间 capacity一般要高于实际字符串长度len。当字符串长度小于1M 时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M 的空间。需要注意的是字符串最大长度为512M。

127.0.0.1:6379> set name codehole
OK
127.0.0.1:6379> get name
"codehole"
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> exists name
(integer) 0

批量键值对:可以批量对多个字符串进行读写,节省网络耗时开销。

127.0.0.1:6379> set name1 codehole
OK
127.0.0.1:6379> set name2 holycoder
OK
127.0.0.1:6379> mget name1 name2 name3
1) "codehole"
2) "holycoder"
3) (nil)
127.0.0.1:6379> mset name1 1-zs name2 2-zs name3 3-zs
OK
127.0.0.1:6379> mget name1 name2 name3
1) "1-zs"
2) "2-zs"
3) "3-zs"
127.0.0.1:6379>

过期和 set命令扩展:可以对key设置过期时间,到点自动删除,这个功能常用来控制缓存的失效时间。不过这个「自动删除」的机制是比较复杂的,如果你感兴趣,可以继续深入《朝生暮死——过期策略》

127.0.0.1:6379> set name zs
OK
127.0.0.1:6379> get name
"zs"
127.0.0.1:6379> expire name 5 # 设置5s的过期时间
(integer) 1
127.0.0.1:6379> get name # 等待5s之后
(nil)
127.0.0.1:6379> setex name 5 zs #5s后过期,等价于set + expire
OK
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> setnx name codehole # 如果name不存在就执行set创建
(integer) 1
127.0.0.1:6379> get name
"codehole"
127.0.0.1:6379> setnx name codeholeBK
(integer) 0
127.0.0.1:6379> get name
"codehole"

计数:如果value 值是一个整数,还可以对它进行自增操作。自增是有范围的,它的范围是signed long的最大最小值,超过了这个值,Redis会报错。

127.0.0.1:6379> set age 30
OK
127.0.0.1:6379> incr age
(integer) 31
127.0.0.1:6379> incrby age 5
(integer) 36
127.0.0.1:6379> incrby age -1
(integer) 35
127.0.0.1:6379> set codehole 9223372036854775807
OK
127.0.0.1:6379> incr codehole
(error) ERR increment or decrement would overflow
127.0.0.1:6379>

字符串是由多个字节组成,每个字节又是由8个 bit组成,如此便可以将一个字符串看成很多bit的组合,这便是 bitmap「位图」数据结构,位图的具体使用会放到后面的章节来讲。关于字符串的内部结构实现,请阅读《极度深寒——探索「字符串」内部》

2 list(列表)

Redis的列表相当于Java语言里面的LinkedList,注意它是链表而不是数组。这意味着list的插入和删除操作非常快,时间复杂度为O(1),但是索引定位很慢,时间复杂度为o(n),这点让人非常意外。

当列表弹出了最后一个元素之后,该数据结构自动被删除,内存被回收。

Redis 的列表结构常用来做异步队列使用。将需要延后处理的任务结构体序列化成字符串塞进Redis的列表,另一个线程从这个列表中轮询数据进行处理。

右边进【rpush 】左边出【lpop 】:队列

127.0.0.1:6379> rpush books python java sql golang
(integer) 4
127.0.0.1:6379> llen books
(integer) 4
127.0.0.1:6379> lpop books
"python"
127.0.0.1:6379> lpop books
"java"
127.0.0.1:6379> lpop books
"sql"
127.0.0.1:6379> lpop books
"golang"
127.0.0.1:6379> lpop books
(nil)
127.0.0.1:6379>

右边进【rpush】右边出【rpop】:栈

127.0.0.1:6379> rpush books java python sql golang
(integer) 4
127.0.0.1:6379> rpop books
"golang"
127.0.0.1:6379> rpop books
"sql"
127.0.0.1:6379> rpop books
"python"
127.0.0.1:6379> rpop books
"java"
127.0.0.1:6379> rpop books
(nil)
127.0.0.1:6379>

慢操作
lindex 相当于Java链表的get(int index)方法,它需要对链表进行遍历,性能随着参数index增大而变差。ltrim和字面上的含义不太一样,个人觉得它叫lretain(保留)更合适一些,因为ltrim跟的两个参数start_index和 end_index定义了一个区间,在这个区间内的值,ltrim要保留,区间之外统统砍掉。我们可以通过Itrim来实现一个定长的链表,这一点非常有用。index可以为负数,index=-1表示倒数第一个元素,同样index=-2表示倒数第二个元素。

127.0.0.1:6379> rpush books java python sql golang
(integer) 4
127.0.0.1:6379> lindex books 1
"python"
127.0.0.1:6379> lindex books 0
"java"
127.0.0.1:6379> lindex books -1
"golang"
127.0.0.1:6379> lrange books 0 -1
1) "java"
2) "python"
3) "sql"
4) "golang"
127.0.0.1:6379> lrange books 0 1
1) "java"
2) "python"
127.0.0.1:6379> lrange books 0 -1
1) "java"
2) "python"
3) "sql"
4) "golang"
127.0.0.1:6379> ltrim books 1 -1
OK
127.0.0.1:6379> lrange books 0 -1
1) "python"
2) "sql"
3) "golang"
127.0.0.1:6379> ltrim books 1 0  #这其实是清空了整个列表,因为区间范围长度为负
OK
127.0.0.1:6379> llen books
(integer) 0
127.0.0.1:6379>

快速列表
在这里插入图片描述
如果再深入一点,你会发现 Redis底层存储的还不是一个简单的 linkedlist,而是称之为快速链表quicklist的一个结构。

首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。

当数据量比较多的时候才会改成quicklist。因为普通的链表需要的附加指针空间太大,会比较浪费空间,而且会加重内存的碎片化。比如这个列表里存的只是 int类型的数据,结构上还需要两个额外的指针 prev和next。

所以 Redis 将链表和 ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。

关于列表的内部结构实现,请阅读此书第34节《极度深寒——探索「压缩列表」内部》和第35 节《极度深寒——探索「快速列表」内部》

3 hash (字典)

Redis的字典相当于Java语言里面的HashMap,它是无序字典。内部实现结构上同Java的 HashMap也是一致的,同样的数组+链表二维结构。第一维 hash的数组位置碰撞时,就会将碰撞的元素使用链表串接起来。

在这里插入图片描述
不同的是,Redis的字典的值只能是字符串,另外它们rehash 的方式不一样,因为Java的 HashMap在字典很大时,rehash是个耗时的操作,需要一次性全部 rehash。Redis为了高性能,不能堵塞服务,所以采用了渐进式rehash 策略

在这里插入图片描述
渐进式rehash 会在rehash的同时,保留新旧两个hash 结构,查询时会同时查询两个hash 结构,然后在后续的定时任务中以及 hash的子指令中,循序渐进地将旧hash 的内容一点点迁移到新的 hash结构中。

当hash移除了最后一个元素之后,该数据结构自动被删除,内存被回收。

hash结构也可以用来存储用户信息,不同于字符串一次性需要全部序列化整个对象,hash可以对用户结构中的每个字段单独存储。这样当我们需要获取用户信息时可以进行部分获取。而以整个字符串的形式去保存用户信息的话就只能一次性全部读取,这样就会比较浪费网络流量。

hash 也有缺点,hash 结构的存储消耗要高于单个字符串,到底该使用hash还是字符串,需要根据实际情况再三权衡。

127.0.0.1:6379> hset books java "think in java"
(integer) 1
127.0.0.1:6379> hset books golang "concurrency in go"
(integer) 1
127.0.0.1:6379> hset books python "pthon cookbook"
(integer) 1
127.0.0.1:6379> hgetall books
1) "java"
2) "think in java"
3) "golang"
4) "concurrency in go"
5) "python"
6) "pthon cookbook"
127.0.0.1:6379> hlen books
(integer) 3
127.0.0.1:6379> hget books java
"think in java"
127.0.0.1:6379> hset books java "think in java update"
(integer) 0
127.0.0.1:6379> hget books java
"think in java update"
127.0.0.1:6379> hmget books java python
1) "think in java update"
2) "pthon cookbook"
127.0.0.1:6379> hmset books java "think in java update-2" python "python cookbook update"
OK
127.0.0.1:6379> hmget books java python
1) "think in java update-2"
2) "python cookbook update"
127.0.0.1:6379>

同字符串一样,hash 结构中的单个子key也可以进行计数,它对应的指令是 hincrby, 和incr使用基本一样。

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hset user age 30
(integer) 1
127.0.0.1:6379> hget user age
"30"
127.0.0.1:6379> hincrby user age 10
(integer) 40
127.0.0.1:6379> hget user age
"40"

4 set (集合)

Redis 的集合相当于 Java 语言里面的 HashSet,它内部的键值对是无序的唯一的。它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值 NULL。

当集合中最后一个元素移除之后,数据结构自动删除,内存被回收。 set 结构可以用来存储活动中奖的用户 ID,因为有去重功能,可以保证同一个用户不会中奖两次。

127.0.0.1:6379> sadd books python
(integer) 1
127.0.0.1:6379> sadd books java
(integer) 1
127.0.0.1:6379> sadd books golang
(integer) 1
127.0.0.1:6379> sadd books java golang sql
(integer) 1
127.0.0.1:6379> smembers books # 注意顺序,和插入的并不一致,因为 set 是无序的
1) "java"
2) "sql"
3) "golang"
4) "python"
127.0.0.1:6379> sismember books java # 查询某个 value 是否存在,相当于 contains(0)
(integer) 1
127.0.0.1:6379> sismember books chineses
(integer) 0
127.0.0.1:6379> scard books
(integer) 4
127.0.0.1:6379> spop books
"java"
127.0.0.1:6379> scard books # 获取长度相当于 count()
(integer) 3

5 zset (有序列表)

zset 可能是 Redis 提供的最为特色的数据结构,它也是在面试中面试官最爱问的数据结构。它类似于 Java 的 SortedSet 和 HashMap 的结合体,一方面它是一个 set,保证了内部value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权重。它的内部实现用的是一种叫着「跳跃列表」的数据结构。

zset 中最后一个 value 被移除后,数据结构自动删除,内存被回收。 zset 可以用来存粉丝列表,value 值是粉丝的用户 ID,score 是关注时间。我们可以对粉丝列表按关注时间进行排序。

zset 还可以用来存储学生的成绩,value 值是学生的 ID,score 是他的考试成绩。我们可以对成绩按分数进行排序就可以得到他的名次。

127.0.0.1:6379> zadd books 9.0 "think in java"
(integer) 1
127.0.0.1:6379> zadd books 8.0 "think in python"
(integer) 1
127.0.0.1:6379> zadd books 8.9 "think in SQL"
(integer) 1
127.0.0.1:6379> zrange books 0 -1
1) "think in python"
2) "think in SQL"
3) "think in java"
127.0.0.1:6379> zrevrange books 0 -1
1) "think in java"
2) "think in SQL"
3) "think in python"
127.0.0.1:6379> zcard books
(integer) 3
127.0.0.1:6379> zscore books "think in SQL"
"8.9000000000000004"
127.0.0.1:6379> zrank books "think in SQL"
(integer) 1
127.0.0.1:6379> zrank books "think in java"
(integer) 2
127.0.0.1:6379> zrank books "think in python"
(integer) 0
127.0.0.1:6379> zrangebyscore books -inf 8.8 withscores
1) "think in python"
2) "8"
127.0.0.1:6379> zrangebyscore books -inf 9 withscores
1) "think in python"
2) "8"
3) "think in SQL"
4) "8.9000000000000004"
5) "think in java"
6) "9"
127.0.0.1:6379> zrem books "think in java"
(integer) 1
127.0.0.1:6379> zrange books 0 -1
1) "think in python"
2) "think in SQL"

zset 内部的排序功能是通过「跳跃列表」数据结构来实现的,它的结构非常特殊,也比较复杂。

因为 zset 要支持随机的插入和删除,所以它不好使用数组来表示。我们先看一个普通的链表结构。

在这里插入图片描述
我们需要这个链表按照score值进行排序。这意味着当有新元素需要插入时,要定位到特定位置的插入点,这样才可以继续保证链表是有序的。通常我们会通过二分查找来找到插入点,但是二分查找的对象必须是数组,只有数组才可以支持快速位置定位,链表做不到,那该怎么办?

想想一个创业公司,刚开始只有几个人,团队成员之间人人平等,都是联合创始人。随着公司的成长,人数渐渐变多,团队沟通成本随之增加。这时候就会引入组长制,对团队进行划分。每个团队会有一个组长。开会的时候分团队进行,多个组长之间还会有自己的会议安排。公司规模进一步扩展,需要再增加一个层级 —— 部门,每个部门会从组长列表中推选出一个代表来作为部长。部长们之间还会有自己的高层会议安排。

跳跃列表就是类似于这种层级制,最下面一层所有的元素都会串起来。然后每隔几个元素挑选出一个代表来,再将这几个代表使用另外一级指针串起来。然后在这些代表里再挑出二级代表,再串起来。最终就形成了金字塔结构。 想想你老家在世界地图中的位置:亚洲–>中国->安徽省->安庆市->枞阳县->汤沟镇->田间村->xxxx 号,也是这样一个类似的结构。

在这里插入图片描述「跳跃列表」之所以「跳跃」,是因为内部的元素可能「身兼数职」,比如上图中间的这个元素,同时处于 L0、L1 和 L2 层,可以快速在不同层次之间进行「跳跃」。
定位插入点时,先在顶层进行定位,然后下潜到下一级定位,一直下潜到最底层找到合适的位置,将新元素插进去。你也许会问,那新插入的元素如何才有机会「身兼数职」呢?

跳跃列表采取一个随机策略来决定新元素可以兼职到第几层。

首先 L0 层肯定是 100% 了,L1 层只有 50% 的概率,L2 层只有 25% 的概率,L3层只有 12.5% 的概率,一直随机到最顶层 L31 层。绝大多数元素都过不了几层,只有极少数元素可以深入到顶层。列表中的元素越多,能够深入的层次就越深,能进入到顶层的概率就会越大。

这还挺公平的,能不能进入中央不是靠拼爹,而是看运气。

6 容器型数据结构的通用规则

list/set/hash/zset这四种数据结构是容器型数据结构,它们共享下面两条通用规则:

  1. create if not exists
    如果容器不存在,那就创建一个,再进行操作。比如rpush操作刚开始是没有列表的Redis就会自动创建一个,然后再rpush进去新元素。
  2. drop if no elements
    如果容器里元素没有了,那么立即删除元素,释放内存。这意味着lpop操作到最后个元素,列表就消失了。

Redis所有的数据结构都可以设置过期时间,时间到了,Redis会自动删除相应的对象。需要注意的是过期是以对象为单位,比如一个hash 结构的过期是整个hash 对象的过期,而不是其中的某个子 key。

还有一个需要特别注意的地方是如果一个字符串已经设置了过期时间,然后你调用了set方法修改了它,它的过期时间会消失。

127.0.0.1:6379> set codehole yoyo
OK
127.0.0.1:6379> expire codehole 600
(integer) 1
127.0.0.1:6379> ttl codehole
(integer) 594
127.0.0.1:6379> set codehole yoyo
OK
127.0.0.1:6379> ttl codehole
(integer) -1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/132641.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【论文阅读】MARS:用于自动驾驶的实例感知、模块化和现实模拟器

【论文阅读】MARS:用于自动驾驶的实例感知、模块化和现实模拟器 Abstract1 Introduction2 Method2.1 Scene Representation2.3 Towards Realistic Rendering2.4 Optimization3.1 Photorealistic Rendering3.2 Instance-wise Editing3.3 The blessing of moduler des…

【深度学习】 Python 和 NumPy 系列教程(十八):Matplotlib详解:2、3d绘图类型(4)3D曲面图(3D Surface Plot)

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 0. 设置中文字体 1. 3D线框图(3D Line Plot) 2. 3D散点图(3D Scatter Plot) 3. 3D条形图(3D Bar Plot) 4. 3D曲面图…

VMware vCenter 从6.7跨版本升级至7.0U3N

本文尝试使用 vCenter Server Appliance 管理界面 (VAMI) 进行对vCenter Server Appliance7应用进行小版本升级,从6.7.0.47000升级到7.0.3.01600(7.0U3N)。 一、升级前的准备工作 1、检查当前运行环境(当前为6.7.0.47000&#x…

【数据结构-树】AVL树

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

MFC中嵌入显示opencv窗口

在MFC窗体中建立一个Picture Control控件,用于显示opencv窗口 在属性中设置图片控件的资源ID为IDC_PIC1 主要的思路: 使用GetWindowRect可以获取图片控件的区域 使用cv::resizeWindow可以设置opencv窗口的大小,适合图片控件的大小 使用cvGetWindowHandle函数可以获取到ope…

Flutter 通过BottomSheetDialog实现抖音打开评论区,内容自动上推、缩放效果

一、先来看下实现的效果 实现上面的效果需要解决俩个问题 当列表进行向下滑动到顶部的时候,继续滑动可以让弹窗向下收起来弹出上下拖动的时候,视图内容跟着上下移动、缩放大小 二、实现弹窗上下滑动的时候,动态改变内容区的位置和大小 通过…

PPT 生成整数序列字典序的r-组合算法

生成整数序列字典序的r-组合算法 一、PPT效果展示二、问题2.1 简述2.2 算法简述2.3 例子 三、PPT实现 一、PPT效果展示 二、问题 2.1 简述 给定一个整数序列 (1,2,3,…n),输出其所有字典序的r-组合,注意事项&#xf…

前端html原生页面兼容多端H5和移动端适配方案

目录 图片代码最后 图片 是一个注册页面 代码 自己查看效果 注意: 单位全部用rem这样才能保证兼容性适配多端&#xff0c;px转rem转换公式 1px 1/37.5rem 所以想要20px应该对应20/37.5 0.53rem <!DOCTYPE html> <html lang"en"><head><met…

关于时空数据的培训 GAN:实用指南(第 01/3 部分)

第 1 部分&#xff1a;深入了解 GAN 训练中最臭名昭著的不稳定性。 一、说明 GAN 是迄今为止最受欢迎的深度生成模型&#xff0c;主要是因为它们最近在图像生成任务上产生了令人难以置信的结果。然而&#xff0c;GAN并不容易训练&#xff0c;因为它们的基本设计引入了无数的不稳…

可变参数JAVA

public class Main {public static void main(String[] args) {//方法形参的个数是可以变化的//格式&#xff1a;属性类型...名字System.out.println(getSum(1,2,3,4,5,6,7,8));}//通过键值对对象来遍历&#xff1b;public static int getSum(int a,int...args){//可变参数;int…

ArcGIS 10.7安装教程!

软件介绍&#xff1a;ArcGIS是一款专业的电子地图信息编辑和开发软件&#xff0c;提供一种快速并且使用简单的方式浏览地理信息&#xff0c;无论是2D还是3D的信息。软件内置多种编辑工具&#xff0c;可以轻松的完成地图生产全过程&#xff0c;为地图分析和处理提供了新的解决方…

【蓝桥杯选拔赛真题60】Scratch旋转风车 少儿编程scratch图形化编程 蓝桥杯选拔赛真题解析

目录 scratch旋转风车 一、题目要求 编程实现 二、案例分析 1、角色分析

Linux自动化构建项目工具——Makefile/makefile

目录 一&#xff0c;背景知识 二&#xff0c;makefile/Makefile的编写 1.创建makefile/Makefile文件 2.在Makefile文件里写编译代码 3.伪目标——.PHONY 1.伪目标的特点 2.怎样实现总是被执行 4.Makefile/makefile文件的不同编写风格 1.背景知识 2.改写 一&#xff0c;背…

goaccess 日志分析 nginx

分析命令&#xff1a; goaccess -a -d -f /mnt/winshare/access-2023070112.log -p goaccess.conf -o /mydata/nginx/html/2023070112_new.html分析日志时的参数 goaccess使用参数详解-a 开启 UserAgent 列表。开启后会降低解析速度 -c 在程序开始运行时显示 日志/日期 配…

nbcio-boot移植到若依ruoyi-nbcio平台里一formdesigner部分(三)

因为这个版本的若依plus不支持本地文件上传&#xff0c;所以需要增加这些本地上传文件的后端代码 和前端代码修改。 1、后端部分 先配置跳过测试吧&#xff0c;平时编译也不需要这个 <!--添加配置跳过测试--><plugin><groupId>org.apache.maven.plugins<…

新增动态排序图、桑基图、AntV组合图,DataEase开源数据可视化分析平台v1.18.10发布

2023年9月14日&#xff0c;DataEase开源数据可视化分析平台正式发布v1.18.10版本。 这一版本的功能升级包括&#xff1a;数据集方面&#xff0c;对字段管理的后台保存做了相关优化&#xff0c;降低了资源消耗&#xff1b;仪表板方面&#xff0c;对联动、查询结果以及过滤组件等…

分享!JetBrains IDE中的GitLab支持

GitLab是流行的基于git的软件开发和部署平台之一&#xff0c;虽然很长一段时间以来&#xff0c;所有基本git操作都已经可以通过GitLab实现&#xff0c;但GitLab集成仍是JetBrains社区的一大最热门请求。为此&#xff0c;JetBrains团队今年与GitLab联手提供了这种类型的集成。 …

PWA及小程序在系统生态方面的支持对比

PWA代表“渐进式网络应用”&#xff08;Progressive Web Application&#xff09;。它是一种结合了网页和移动应用程序功能的技术概念。PWA旨在提供类似于原生应用程序的用户体验&#xff0c;包括离线访问、推送通知、后台同步等功能&#xff0c;同时又具有网页的优势&#xff…

SQL SERVER 中无法删除table ‘biao’,因为它不存在或者您不具备相应的权限

删除table表 1.删除表示提示&#xff1a;SQL SERVER 中无法删除table ‘biao’&#xff0c;因为它不存在或者您不具备相应的权限。2.原因3.解决方法3.1 图3.2 图3.3 图3.4 图 1.删除表示提示&#xff1a;SQL SERVER 中无法删除table ‘biao’&#xff0c;因为它不存在或者您不具…

Linux基础入门

一、操作系统安装方法 1、使用u盘安装 工具&#xff08;前提条件&#xff09;&#xff1a; <1>u盘 <2>镜像文件iso/msdn.itellyou.cn <3>把u盘做成PE&#xff1a;大白菜/老毛桃/winPE/软碟通/ultralSO 设置BIOS&#xff1a;通过u盘启动 安装系统&…