《Redis设计与实现》笔记

news2024/11/18 9:22:22

第二章:简单动态字符串

        1.Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串( simple dynamic string,SDS)的抽象类型,并将SDS用作 Redis的默认字符串表示。

        Redis里面,C字符串只会作为字符串字面量( string literal)用在一些无须对字符串值进行修改的地方,比如打印日志:

        redislog (REDIS WARNING,"Redis is now ready to exit, bye bye..");

        2.空间预分配用于优化SDS的字符串增长操作:当SDS的API对一个SDS进行修改,并且需要对SDS进行空间扩展的时候,程序不仅会为SDS分配修改所必须要的空间,还会为SDS分配额外的未使用空间。 

        其中,额外分配的未使用空间数量由以下公式决定:

  • 如果对SDS进行修改之后,SDS的长度(也即是1en属性的值)将小于1MB,那么程序分配和1en属性同样大小的未使用空间,这时SDS1en属性的值将和free属性的值相同。举个例子,如果进行修改之后,SDS的1en将变成13字节,那么程序也会分配13字节的未使用空间,SDS的buf数组的实际长度将变成13+13+1=27字节(额外的一字节用于保存空字符)。
  • 如果对SDS进行修改之后,SDS的长度将大于等于1MB,那么程序会分配1MB的未使用空间。举个例子,如果进行修改之后,SDS的len将变成30MB,那么程序会分配1MB的未使用空间,SDS的buf数组的实际长度将为30MB+1MB+1byte。

        通过空间预分配策略,可以减少连续执行字符串增长操作所需的内存重分配次数。

        3.惰性空间释放用于优化SDS的字符串缩短操作:当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用free属性将这些字节的数量记录起来,并等待将来使用。

        通过惰性空间释放策略,SDS避免了缩短字符串时所需的内存重分配操作,并为将来可能有的增长操作提供了优化。

        与此同时,SDS也提供了相应的API,让我们可以在有需要时,真正地释放SDS的未使用空间,所以不用担心惰性空间释放策略会造成内存浪费。

        4.SDS的API都是二进制安全的。

第三章:链表

        1.integers列表键的底层实现就是一个链表,链表中的每个节点都保存了一个整数值。除了链表键之外,发布与订阅、慢查询、监视器等功能也用到了链表, Redis服务器本身还使用链表来保存多个客户端的状态信息,以及使用链表来构建客户端输出缓冲区output buffer)。

 

        

        2.list结构为链表提供了表头指针head、表尾指针tail,以及链表长度计数器len,而dup、free和 match成员则是用于实现多态链表所需的类型特定函数:

  • dup函数用于复制链表节点所保存的值;
  • free函数用于释放链表节点所保存的值;
  • match函数则用于对比链表节点所保存的值和另一个输入值是否相等。

        3.Redis的链表实现的特性可以总结如下:

  • 双端:链表节点带有prev和next指针,获取某个节点的前置节点和后置节点的复杂度都是O(1)。
  • 无环:表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NULL为终点。
  • 带表头指针和表尾指针:通过1ist结构的head指针和tai1指针,程序获取链表的表头节点和表尾节点的复杂度为O(1)。
  • 带链表长度计数器:程序使用1ist结构的len属性来对1ist持有的链表节点进行计数,程序获取链表中节点数量的复杂度为O(1)。
  • 多态:链表节点使用void*指针来保存节点值,并且可以通过1ist结构的dup、free、 match三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。

第四章:字典

        1.Redis字典所使用的哈希表由dict.h/dictht结构定义:

table属性是一个数组,数组中的每个元素都是一个指向dict.h/dicentra结构的指针,每个 dictentry结构保存着一个键值对。size属性记录了哈希表的大小,也即是tab1e数组的大小,而used属性则记录了哈希表目前已有节点(键值对)的数量。sizemask属性的值总是等于size-1,这个属性和哈希值一起决定一个键应该被放到table数组的哪个索引上面。

        2.哈希表节点使用 dictentry结构表示,每个 dictentry结构都保存着一个键值对:

key属性保存着键值对中的键,而ⅴ属性则保存着键值对中的值,其中键值对的值可以是一个指针,或者是一个uint64t整数,又或者是一个int64t整数。

next属性是指向另一个哈希表节点的指针,这个指针可以将多个哈希值相同的键值对连接在一次,以此来解决键冲突( collision)的问题。 

        3.Redis中的字典由dict.h/dict结构表示:

type属性和 privata属性是针对不同类型的键值对,为创建多态字典而设置的:

  • type属性是一个指向 ditype结构的指针,每个 dicttype结构保存了一簇用于操作特定类型键值对的函数, Redis会为用途不同的字典设置不同的类型特定函数。
  • 而 privata属性则保存了需要传给那些类型特定函数的可选参数。

ht属性是一个包含两个项的数组,数组中的每个项都是一个 dicth哈希表,一般情况下,字典只使用ht[0]哈希表,ht[1哈希表只会在对ht[0哈希表进行 rehash时使用。

除了ht[1]之外,另一个和 rehash有关的属性就是 rehashidx,它记录了hash目前的进度,如果目前没有在进行 rehash,那么它的值为-1。

Redis计算哈希值和索引值的方法如下:

        4.当字典被用作数据库的底层实现,或者哈希键的底层实现时, Redis使用 Murmurhash2算法来计算键的哈希值。

Murmurhash算法最初由 Austin Appleby于2008年发明,这种算法的优点在于,即使输入的键是有规律的,算法仍能给出一个很好的随机分布性,并且算法的计算速度也非常快。

Murmurhash算法目前的最新版本为 Murmurhash3,而 Redis使用的是 Murmurhashi2,关于Murmurhash算法的更多信息可以参考该算法的主页:http:/code.google.com/p/smasher/。

        5.当有两个或以上数量的键被分配到了哈希表数组的同一个索引上面时,我们称这些键发生了冲突( collision)。

Redis的哈希表使用链地址法( separate chaining)来解决键冲突,每个哈希表节点都有个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表连接起来,这就解决了键冲突的问题。

        6.随着操作的不断执行,哈希表保存的键值对会逐渐地增多或者减少,为了让哈希表的负载因子( load factor)维持在一个合理的范围之内,当哈希表保存的键值对数量太多或者太少时,程序需要对哈希表的大小进行相应的扩展或者收缩。

扩展和收缩哈希表的工作可以通过执行 rehash(重新散列)操作来完成, Redis对字典的哈希表执行 rehash的步骤如下:

        1).为字典的ht[1]哈希表分配空间,这个哈希表的空间大小取决于要执行的操作,以及h[O当前包含的键值对数量(也即是ht[0].used属性的值):

  • 如果执行的是扩展操作,那么ht[1]的大小为第一个大于等于ht[0].used*2 的2的n次方幂
  • 如果执行的是收缩操作,那么ht[1的大小为第一个大于等于ht[0].used 的2的n次方幂

        2).将保存在ht[0]中的所有键值对 rehash到ht[1]上面: rehash指的是重新计算键的哈希值和索引值,然后将键值对放置到ht[1]哈希表的指定位置上。

        3).当ht[0]包含的所有键值对都迁移到了ht[1]之后(ht[0]变为空表),释放ht[0],将ht[1]设置为ht[0],并在ht[1]新创建一个空白哈希表,为下一次 rehash做准备。

        6.哈希表的扩展与收缩 

当以下条件中的任意一个被满足时,程序会自动开始对哈希表执行扩展操作:

1).服务器目前没有在执行 BGSAVE命令或者 BGREWRITEAOF命令,并且哈希表的负载因子大于等于1。

2).服务器目前正在执行 BGSAVE命令或者 BGREWRITEAOF命令,并且哈希表的负载因子大于等于5。

其中哈希表的负载因子可以通过公式:

#负载因子=哈希表已保存节点数量/哈希表大小

load_factor =ht[0].used/ht[0].size计算得出。

例如,对于一个大小为4,包含4个键值对的哈希表来说,这个哈希表的负载因子为:

load_factor =4/4=1

又例如,对于一个大小为512,包含256个键值对的哈希表来说,这个哈希表的负载因子为:

load_factor =256/ 512=0.5

根据 BGSAVE命令或 BGREWRITEAOF命令是否正在执行,服务器执行扩展操作所需的负载因子并不相同,这是因为在执行 BGSAVE命令或 BGREWRITEAOF命令的过程中, Redis需要创建当前服务器进程的子进程,而大多数操作系统都采用写时复制(copy-onwrite)技术来优化子进程的使用效率,所以在子进程存在期间,服务器会提高执行扩展操作所需的负载因子,从而尽可能地避免在子进程存在期间进行哈希表扩展操作,这可以避免不必要的内存写入操作,最大限度地节约内存。

另一方面,当哈希表的负载因子小于0.1时,程序自动开始对哈希表执行收缩操作。

        7.渐进式rehash

上一节说过,扩展或收缩哈希表需要将ht[0]里面的所有键值对 rehash到ht[1里面,但是,这个 rehash动作并不是一次性、集中式地完成的,而是分多次、渐进式地完成的。

这样做的原因在于,如果ht[0]里只保存着四个键值对,那么服务器可以在瞬间就将这些键值对全部 rehash到ht[1];但是,如果哈希表里保存的键值对数量不是四个,而是四百万、四千万甚至四亿个键值对,那么要一次性将这些键值对全部 rehash到ht[1]的话庞大的计算量可能会导致服务器在一段时间内停止服务。

因此,为了避免 rehash对服务器性能造成影响,服务器不是一次性将ht[0j里面的所有键值对全部 rehash到ht[1],而是分多次、渐进式地将ht[0]里面的键值对慢慢地 rehash到ht[1]。

以下是哈希表渐进式 rehash的详细步骤:

1)为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两个哈希表。

2)在字典中维持一个索引计数器变量 rehashidx,并将它的值设置为0,表示 rehash工作正式开始。

3)在 rehash进行期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在 rehashidx索引上的所有键值对rehash到ht[1],当 rehash工作完成之后,程序将 rehashidx属性的值增一。

4)随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有键值对都会被rehash至ht[1],这时程序将 rehashidx属性的值设为-1,表示 rehash操作已完成。渐进式 rehash的好处在于它采取分而治之的方式,将 rehash键值对所需的计算工作均摊到对字典的每个添加、删除、查找和更新操作上,从而避免了集中式 rehash而带来的庞大计算量。

        8.渐进式 rehash执行期间的哈希表操作

因为在进行渐进式 rehash的过程中,字典会同时使用ht[0]和ht[1]两个哈希表,所以在渐进式 rehash进行期间,字典的删除( delete)、查找(fnd)、更新( update)等操作会在两个哈希表上进行。例如,要在字典里面查找一个键的话,程序会先在ht[0]里面进行查找,如果没找到的话,就会继续到ht[1]里面进行查找,诸如此类。

另外,在渐进式 rehash执行期间,新添加到字典的键值对一律会被保存到ht[1]里面,而ht[0]则不再进行任何添加操作,这一措施保证了ht[0]包含的键值对数量会只减不增,并随着 rehash操作的执行而最终变成空表。

第五章:跳跃表

        1.跳跃表( skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。

跳跃表支持平均O(logN、最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。

在大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树要来得更为简单,所以有不少程序都使用跳跃表来代替平衡树。

Redis使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员(member)是比较长的字符串时, Redis就会使用跳跃表来作为有序集合键的底层实现。

图5-1展示了一个跳跃表示例,位于图片最左边的是 zskiplist结构,该结构包含以下属性:

  • header:指向跳跃表的表头节点。 
  • tail:指向跳跃表的表尾节点。
  • level:记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内)。
  • length:记录跳跃表的长度,也即是,跳跃表目前包含节点的数量(表头节点不计算在内)。

位于skiplist结构右方的是四个zskiplistNode结构,该结构包含以下属性:

  • 层( level):节点中用L1、L2、L3等字样标记节点的各个层,L1代表第一层,L2代表第二层,以此类推。每个层都带有两个属性:前进指针和跨度。前进指针用于访问位于表尾方向的其他节点,而跨度则记录了前进指针所指向节点和当前节点的距离。在上面的图片中,连线上带有数字的箭头就代表前进指针,而那个数字就是跨度。当程序从表头向表尾进行遍历时,访问会沿着层的前进指针进行。
  • 后退( backward)指针:节点中用BW字样标记节点的后退指针,它指向位于当前节点的前一个节点。后退指针在程序从表尾向表头遍历时使用。
  • 分值( score):各个节点中的1.0、2.0和3.0是节点所保存的分值。在跳跃表中节点按各自所保存的分值从小到大排列。
  • 成员对象(obj):各个节点中的o1、o2和o3是节点所保存的成员对象。

注意表头节点和其他节点的构造是一样的:表头节点也有后退指针、分值和成员对象,不过表头节点的这些属性都不会被用到,所以图中省略了这些部分,只显示了表头节点的各个层。

        2.跳跃表节点的实现由redis.h/zskiplistNode结构定义:

1.层

跳跃表节点的level数组可以包含多个元素,每个元素都包含一个指向其他节点的指针,程序可以通过这些层来加快访问其他节点的速度,一般来说,层的数量越多,访问其他节点的速度就越快。

每次创建一个新跳跃表节点的时候,程序都根据幂次定律(power law,越大的数出现的概率越小)随机生成一个介于1和32之间的值作为level数组的大小,这个大小就是层的“高度”。

2.前进指针

每个层都有一个指向表尾方向的前进指针(level[i]. forward属性),用于从表头向表尾方向访问节点。

3.跨度层的跨度

  • (level[i].span属性)用于记录两个节点之间的距离口两个节点之间的跨度越大,它们相距得就越远。
  • 指向NULL的所有前进指针的跨度都为0,因为它们没有连向任何节点。

4.后退指针


节点的后退指针( backward属性)用于从表尾向表头方向访问节点:跟可以一次跳过多个节点的前进指针不同,因为每个节点只有一个后退指针,所以每次只能后退至前一个节点。

5.分值和成员
节点的分值( score属性)是一个doub1e类型的浮点数,跳跃表中的所有节点都按分值从小到大来排序。


节点的成员对象(obj属性)是一个指针,它指向一个字符串对象,而字符串对象则保存着一个SDS值。


在同一个跳跃表中,各个节点保存的成员对象必须是唯一的,但是多个节点保存的分值却可以是相同的:分值相同的节点将按照成员对象在字典序中的大小来进行排序,成员对象较小的节点会排在前面(靠近表头的方向),而成员对象较大的节点则会排在后面(靠近表尾的方向)。

        3.仅靠多个跳跃表节点就可以组成一个跳跃表,但通过使用一个 skip1ist结构来持有这些节点,程序可以更方便地对整个跳跃表进行处理,比如快速访问跳跃表的表头节点和表尾节点,或者快速地获取跳跃表节点的数量(也即是跳跃表的长度)等信息。

header和tail指针分别指向跳跃表的表头和表尾节点,通过这两个指针,程序定位表头节点和表尾节点的复杂度为O(1)。
通过使用1 ength属性来记录节点的数量,程序可以在O()复杂度内返回跳跃表的长度。
level属性则用于在O(1)复杂度内获取跳跃表中层高最大的那个节点的层数量,注意表头节点的层高并不计算在内。

第六章:整数集合

        1.整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时, Redis就会使用整数集合作为集合键的底层实现。

整数集合(intset)是Redis用于保存整数值的集合抽象数据结构,它可以保存类型为int16_t、int32_t或者int64_t的整数值,并且保证集合中不会出现重复元素。
每个intset.h/intset结构表示一个整数集合:

contents数组是整数集合的底层实现:整数集合的每个元素都是 contents数组的个数组项(item),各个项在数组中按值的大小从小到大有序地排列,并且数组中不包含任何重复项。

length属性记录了整数集合包含的元素数量,也即是 contents数组的长度。
虽然 intset结构将 contents属性声明为int8t类型的数组,但实际上 contents数组并不保存任何int8t类型的值, contents数组的真正类型取决于encoding属性的值:

        2.每当我们要将一个新元素添加到整数集合里面,并且新元素的类型比整数集合现有所有元素的类型都要长时,整数集合需要先进行升级( upgrade),然后才能将新元素添加到整数集合里面。

升级整数集合并添加新元素共分为三步进行:

  • 1)根据新元素的类型,扩展整数集合底层数组的空间大小,并为新元素分配空间。
  • 2)将底层数组现有的所有元素都转换成与新元素相同的类型,并将类型转换后的元素放置到正确的位上,而且在放置元素的过程中,需要继续维持底层数组的有序性质不变。
  • 3)将新元素添加到底层数组里面。

        3.因为每次向整数集合添加新元素都可能会引起升级,而每次升级都需要对底层数组中已有的所有元素进行类型转换,所以向整数集合添加新元素的时间复杂度为O(N)。

        4.升级之后新元素的摆放位置

因为引发升级的新元素的长度总是比整数集合现有所有元素的长度都大,所以这个新元素的值要么就大于所有现有元素,要么就小于所有现有元素:

  • 在新元素小于所有现有元素的情况下,新元素会被放置在底层数组的最开头(索引0);
  • 在新元素大于所有现有元素的情况下,新元素会被放置在底层数组的最末尾(索引length-1)。

        5.升级的好处
整数集合的升级策略有两个好处,一个是提升整数集合的灵活性,另一个是尽可能地节约内存。

        6.降级

整数集合不支持降级操作,一旦对数组进行了升级,编码就会一直保持升级后的状态。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

操作系统第三章习题及答案(汤子瀛第四版)

第三章 1.高级调度与低级调度的主要任务是什么?为什么要引入中级调度? 答:高级调度的主要任务是根据某种算法,把外存上处于后备队列中的那些作业调入内存。低级调度是保存处理机的现场信息,按某种算法先取…

【UE4】打包失败 Failed to build UATTempProj.proj

我抄我自己之 https://zhuanlan.zhihu.com/p/586117443 前两天编 UE4.27 源码内存和CPU直接全是100%爆满,甚至还报错:c1060, the compiler is out of heap,设置虚拟内存也不不好使,发现16GB内存不配编 UE,火速换了64G之…

Kotlin或将超越Go?

知名软件行业分析公司 RedMonk 发布了 2022 年 6 月(第三季度)编程语言排行榜。 RedMonk 编程语言排行榜通过追踪编程语言在 GitHub 和 Stack Overflow 上的代码使用情况与讨论数量,统计分析后进行排序,其旨在深入了解潜在的语言…

你真的知道Spring Security安全框架吗?

1、什么是安全管理框架? 解决系统安全问题的框架。如果没有安全框架,我们需要手动处理每个资源的访问控制,非常麻烦。使 用安全框架,我们可以通过配置的方式实现对资源的访问限制。 安全框架,简单说是对访问权限进行控…

Android Material Design之MaterialButton(一)

按规矩先上效果图 资源引入 implementation com.google.android.material:material:1.4.0关键属性 属性描述app:backgroundTint背景着色app:backgroundTintMode着色模式app:strokeColor描边颜色app:strokeWidth描边宽度app:cornerRadius圆角大小app:rippleColor按压水波纹颜色…

【Java八股文总结】之计算机网络

文章目录计算机网络一、基础1、网络体系结构2、HTTP协议、TCP协议、UDP协议比较3、网络协议4、WebSocket和Socket的区别?5、常见的端口及其对应的服务?6、从浏览器输入URL到页面展示发生了什么?(★★★★★)1、DNS域名…

MySQL纯代码复习(下)

前言 本文章的语言描述会比上篇多一些 数据库的创建修改与删除 标识符命名规则 数据库名、表名不得超过30个字符,变量限制为29个必须只能包含A-Z,a-z,0-9,_等63个字符数据库名、表名、字段名等对象名中间不要包含空格同一个My…

Twitter引流如何开发客户

要想在twitter平台上取得效果,你需要先了解twitter的算法规则,去迎合平台,推特群推王给出以下5条建议,让你发布的帖子更容易被推荐。 这里Twitter群推王可以给大家讲一下,关于推特平台的算法排名: Twitter…

nginx降权+安装php

nginx降权 使用普通用户启动Nginx 为什么要让nginx服务使用普通用户 默认情况下,nginx的master进程使用的是root用户,worker进程使用的是nginx指定的普通用户,使用root用户跑nginx的master进程有两个大问题: (1&#x…

课程设计-天天象棋作弊软件判别

目录 1.作弊开挂可能迹象 2.设计作弊检测系统灵感 3.设计作弊检测系统思路 3.1反作弊系统应对策略框架 4.感想体悟 1.作弊开挂可能迹象 1.非实名认证; 2.头像:美女; 3.名称: (1)一串英文字母; (2)非正常中文名…

【深度分解网络:显著性分析:IVIF】

Infrared and Visible Image Fusion Based on Deep Decomposition Network and Saliency Analysis (基于深度分解网络和显著性分析的红外与可见光图像融合) 传统的图像融合侧重于选择一种有效的分解方法从源图像中提取代表性特征,并试图找到…

2022年海运行业研究报告

第一章 行业概况 海洋运输又称“国际海洋运输”,提供海上客运或者货运服务的行业。是国际物流中最主要的运输方式。它是指使用船舶通过海上航道在不同国家和地区的港口之间运送货物的一种方式,在国际货物运输中使用最广泛。国际贸易总运量中的2/3以上&a…

功率放大器和电压放大器的区别是什么意思

很多人经常会在后台咨询小编功率放大器和电压放大器的区别有哪些?今天就来为大家科普一下功率放大器和电压放大器的知识内容,希望大家下次能够区分,并且可以正常地选择和使用功率放大器。 图:功率放大电路与电压放大电路对比 功率…

Opengl ES之YUV数据渲染

YUV回顾 记得在音视频基础知识介绍中,笔者专门介绍过YUV的相关知识,可以参考: 《音视频基础知识-YUV图像》 YUV数据量相比RGB较小,因此YUV适用于传输,但是YUV图不能直接用于显示,需要转换为RGB格式才能显…

简单的股票行情演示(一) - 实时标的数据

一、概述二、效果展示三、实现代码 1、行情数据中心2、数据拉取模块3、基础服务模块4、UI展示四、相关文章原文链接:简单的股票行情演示(一) - 实时标的数据 一、概述 很长一段时间都有一个想法,使用QCP去做一个行情展示小事例&…

TiDB Cloud

TiDB Cloud 为什么选择TiDB 分布式数据库-多租户混合工作负载-在同一个数据库中 事务型:基于行的数据分析型:基于列的数据 弹性比例: 缩小-减少节点横向扩展-添加节点 基于“RAFT”的高可用性 每个数据段的3个可用区进行复制 多租户 什么…

DataGridXL 2.0 for JavaScript Crack

你的web开发好了,客户说我习惯用excel这样的表格,你们是否能开发像电子表格一样的功能? Web 应用程序的类似 Excel 的体验---DataGridXL 2.0 for JavaScript Crack 你已经构建了一个 Web 应用程序,但你的用户坚持使用 Excel。 类似…

一、ROS2简介

ros2相关简介 ROS2的前身是ROS,ROS即机器人操作系统(Robot Operating System)。但是ROS本身并不是一个操作系统,而是一个软件库和工具集。 Ros的出现解决了机器人各个组件的通信问题,后来越来越多的机器人算法也集成到…

漫谈信息模型(1)

简单地表达复杂的世界,这是各类思想家近千年来的追求。如何将人类在世界上观察到的结果进行概念化表达?又如何描述人造的复杂工具?这种探索成为人类文明进步的一个重要的驱动力。计算机的出现,推动了人类对现实事物进行概念化的描…

技术开发87

技术开发87 业务内容: . 冲床加工,高速冲床加工,省力化机械制作 . 铣床加工,食品机械制作 . 轮廓加工,钢丝加工 . 冲床模具制作 . 溶接 公司简介: 资本金:1000万日元(约66元人…