当前位置: 首页 > news >正文

java架构知识点-中间件

一、缓存

为什么要使用缓存
(一)性能 我们在碰到需要执行耗时特别久,且结果不频繁变动的 SQL,就特别适合将运行结果放入 缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应

(二)并发 如下图所示,在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis 做一个缓冲操作,让请求先访问到 redis ,而不是直接访问数据库。
优秀的缓存系统 Redis
Redis 是完全开源免费的,用 C 语言编写的,遵守 BSD 协议,是一个高性能的 (key/value) 分布式内存数据库,基于内存运行并支持持久化的NoSQL 数据库,是当前最热门的 NoSql 数据库之一 , 也被人们称为数据结构服务器
Redis 相比同类的其他产品,具有如下优点:
Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用
Redis 不仅仅支持简单的 key-value 类型的数据,同时还提供 list set zset hash 等数据结构的存储 Redis支持数据的备份,即 master-slave 模式的数据备份
redis 为什么这么快
主要是以下三点
1.纯内存操作
2.单线程操作,避免了频繁的上下文切换
3.采用了非阻塞 I/O 多路复用机制

我们的 redis-client 在操作的时候,会产生具有不同事件类型的 socket 。在服务端,有
一段 I/0 多路复用程序,将其置入队列之中。然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器 中。 需要说明的是,这个I/O 多路复用机制, redis 还提供了 select epoll evport kqueue 等多路复用函数库,
 
redis 的数据类型,以及每种数据类型的使用场景
( )String 这个其实没啥好说的,最常规的 set/get 操作, value 可以是 String 也可以是数字。一般做
一些复杂的计数
功能的缓存。
(二 )hash 这里 value 存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做 单点登录 的时候,就 是用这种数据结构存储用户信息,以cookieId 作为 key ,设置 30 分钟为缓存过期时间,能很好的模拟出类似 session 的效果。
( )list 使用 List 的数据结构,可以 做简单的消息队列的功能 。另外还有一个就是,可以利用 lrange 命令, 做基于 redis 的分页功能 ,性能极佳,用户体验好。 欢迎关注公众号:老男孩的架构路,后
( )set 因为 set 堆放的是一堆不重复值的集合。所以可以做 全局去重的功能 。为什么不用 JVM 自带的 Set 进行去重?
因为我们的系统一般都是集群部署,使用JVM 自带的 Set ,比较麻烦,难道为了一个做一个全局去重,再起一个公共 服务,太麻烦了。 另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的 * * 喜好等功能**
( )sorted set
sorted set 多了一个权重参数 score, 集合中的元素能够按 score 进行排列。可以做 排行榜应用,取 TOP N 操作 。另外,参照另一篇《分布式之延时任务方案解析》,该文指出了sorted set 可以用来做 延时任务 。最后一个应用就是可以做范围查找
redis 的过期策略以及内存淘汰机制
分析 : 这个问题其实相当重要,到底 redis 有没用到家,这个问题就可以看出来。比如你 redis 只能存 5G 数据,可是你写了10G ,那会删 5G 的数据。怎么删的,这个问题思考过么?还有,你的数据已经设置了过期时间,但是时间到了,内存占用率还是比较高,有思考过原因么? 回答 : redis 采用的是定期删除 + 惰性删除策略。
为什么不用定时删 除策略 ?
定时删除 , 用一个定时器来负责监视 key, 过期则自动删除。虽然内存及时释放,但是十分消耗 CPU 资源。在大并发请求下,CPU 要将时间应用在处理请求,而不是删除 key, 因此没有采用这一策略 .
定期删除 + 惰性删除是如何 工作的呢 ?
定期删除, redis 默认每个 100ms 检查,是否有过期的 key, 有过期 key 则删除。需要说明的是, redis 不是每个100ms 将所有的 key 检查一次,而是随机抽取进行检查 ( 如果每隔 100ms, 全部 key 进行检查, redis 岂不是卡 死) 。因此,如果只采用定期删除策略,会导致很多 key 到时间没有删除。 于是,惰性删除派上用场。也就是说在你获取某个key 的时候, redis 会检查一下,这个 key 如果设置了过期时间那么是否过期了?如果过期了此时就会删 除。
采用定期删除 + 惰性删除就没其他问题了么 ? 不是的,如果定期删除没删除 key 。然后你也没即时去请求 key , 也就是说惰性删除也没生效。这样,redis 的内存会越来越高。那么就应该采用 内存淘汰机制 。 在 redis.conf 中有一 行配置
# maxmemory-policy volatile-lru
该配置就是配内存淘汰策略的 ( 什么,你没配过?好好反省一下自己 )
1 noeviction :当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。
2 allkeys-lru :当内存不足以容纳新写入数据时,在键空间中,移除最 近最少使用的key 推荐使用,目前项目在用这种。
3 allkeys-random :当内存不足以容纳新写入数据时,在键空间中,随机移除某个key 应该也没人用吧,你不删最少使用 Key, 去随机删。
4 volatile-lru :当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key 这种情况一般是把 redis 既当缓存,又 做持久化存储的时候才用。不推荐
5 volatile-random :当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key 依然不推荐
6 volatile-ttl :当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key 优先移除。 不推荐 ps :如果没有设置 expire key, 不满足先决条件 (prerequisites); 那么 volatile-lru, volatile-random volatile-ttl 策略的行为 , noeviction( 不删除 ) 基本上一致。
渐进式 ReHash
渐进式 rehash 的原因
整个 rehash 过程并不是一步完成的,而是分多次、渐进式的完成。如果哈希表中保存着数量巨大的键值对时,若一次进行rehash ,很有可能会导致服务器宕机。
渐进式 rehash 的步骤
ht[1] 分配空间,让字典同时持有 ht[0] ht[1] 两个哈希表
维持索引计数器变量 rehashidx ,并将它的值设置为 0 ,表示 rehash 开始 每次对字典执行增删改查时,将ht[0] rehashidx 索引上的所有键值对 rehash ht[1] ,将 rehashidx +1
ht[0] 的所有键值对都被 rehash ht[1] 中,程序将 rehashidx 的值设置为 -1 ,表示 rehash 操作完成
注:渐进式 rehash 的好处在于它采取分为而治的方式,将 rehash 键值对的计算均摊到每个字典增删改查操作,避免了集中式rehash 的庞大计算量。
缓存穿透
概念访问一个不存在的 key ,缓存不起作用,请求会穿透到 DB ,流量大时 DB 会挂掉。
解决方案
采用布隆过滤器,使用一个足够大的 bitmap ,用于存储可能访问的 key ,不存在的 key 直接被过滤;
访问 key 未在 DB 查询到值,也将空值写进缓存,但可以设置较短过期时间。
缓存雪崩
大量的 key 设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时 DB 请求量大、压力骤增,引起雪崩。
解决方案
可以给缓存设置过期时间时加上一个随机值时间,使得每个 key 的过期时间分布开来,不会集中在同一时刻失效;
采用限流算法,限制流量;
采用分布式锁,加锁访问。
                                                                二、消息队列
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题
实现高性能,高可用,可伸缩和最终一致性架构 使用较多的消息队列有ActiveMQ RabbitMQ ZeroMQ Kafka MetaMQ RocketMQ
消息队列应用场景
以下介绍消息队列在实际应用中常用的使用场景。异步处理,应用解耦,流量削锋和消息通讯四个场景
异步处理
场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种 1. 串行的方式; 2. 并行方式
1 )串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端
 
2 )并行方式:将注册信息写入数据库成功后,发送注
 
假设三个业务节点每个使用 50 毫秒钟,不考虑网络等其他开销,则串行方式的时间是 150 毫秒,并行的时间可能是
100 毫秒。
因为 CPU 在单位时间内处理的请求数是一定的,假设 CPU1 秒内吞吐量是 100 次。则串行方式 1 秒内 CPU 可处理的请 求量是7 次( 1000/150 )。并行方式处理的请求量是 10 次( 1000/100

小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?
 
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是 50 毫秒。注册邮件,发送短信写入消 息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50 毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS 。比串行提高了 3 倍,比并行提高了两倍
应用解耦
场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。

流量削锋
流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛
应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。 可以控制活动的人数可以缓解短时间内高流量压垮应用
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面秒杀业务根据消息队列中的请求信息,再做后续处理
日志处理
日志处理是指将消息队列用在日志处理中,比如 Kafka 的应用,解决大量日志传输的问题。
 
日志采集客户端,负责日志数据采集,定时写受写入 Kafka 队列 Kafka消息队列,负责日志数据的接收,存储和转发 日志处理应用:订阅并消费kafka 队列中的日志数据
以下是新浪 kafka 日志处理应用案例:转自( http://cloud.51cto.com/art/201507/484338.htm

 

 
消息的幂等处理
由于网络原因,生产者可能会重复发送消息,因此消费者方必须做消息的幂等处理,常用的解决方案有:
1. 查询操作:查询一次和查询多次,在数据不变的情况下,查询结果是一样的。 select 是天然的幂等操作;
2. 删除操作:删除操作也是幂等的,删除一次和多次删除都是把数据删除。 ( 注意可能返回结果不一样,删除的 数据不存在,返回0 ,删除的数据多条,返回结果多个 )
3. 唯一索引 ,防止新增脏数据。比如:支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建资金账户多个,那么给资金账户表中的用户ID 加唯一索引,所以一个用户新增成功 一个资金账户记录。要点:唯一索引或唯一组合索引来防止新增数据存在脏数据(当表存在唯一索引,并发 时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可);
4. token 机制 ,防止页面重复提交。业务要求: 页面的数据只能被点击提交一次;发生原因: 由于重复点击或者网络重发,或者nginx 重发等情况会导致数据被重复提交;解决办法: 集群环境采用 token redis(redis 单线程的,处理需要排队) ;单 JVM 环境:采用 token redis token jvm 内存。处理流程:
1. 数据提交前要向服务的申请token token 放到 redis jvm 内存, token 有效时间;
2. 提交后后台校验 token ,同时删除 token,生成新的 token 返回。 token 特点:要申请,一次有效性,可以限流。注意: redis 要用删除操作来判 断token ,删除成功代表 token 校验通过,如果用 select+delete 来校验 token ,存在并发问题,不建议使用;
5. 悲观锁 —— 获取数据的时候加锁获取。 select * from table_xxx where id='xxx' for update; 注意: id 字段一 定是主键或者唯一索引,不然是锁表,会死人的悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用;
6. 乐观锁 —— 乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。乐观锁的实现方式多种多样可以通过version 或者其他状态条件: 1. 通过版本号实现 update table_xxx set
name=#name#,version=version+1 where version=#version# 如下图 ( 来自网上 ) 2. 通过条件限制 update
table_xxx set avai_amount=avai_amount-#subAmount# where avai_amount-#subAmount# >= 0 要求: quality-#subQuality# >= ,这个情景适合不用版本号,只更新是做数据安全校验,适合库存模型,扣份额和回滚份额,性能更高;
update table_xxx set name=#name#,version=version+1 where id=#id# and version=#version#
update table_xxx set avai_amount=avai_amount-#subAmount# where id=#id# and avai_amount-
#subAmount# >= 0
7. 分布式锁 —— 还是拿插入数据的例子,如果是分布是系统,构建全局唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统(redis zookeeper) ,在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路,引入多多个系统,也就是分布式系统中得解决思路。要点:某个长流程处理过程要求不能并发执行,可以在流程执行之前根据某个标志( 用户 ID+ 后缀等) 获取分布式锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成
后,释放分布式锁 ( 分布式锁要第三方系统提供 )
8.select + insert—— 并发不高的后台系统,或者一些任务 JOB ,为了支持幂等,支持重复执行,简单的处理方法是,先查询下一些关键数据,判断是否已经执行过,在进行业务处理,就可以了。注意:核心高并发流程不要用这种方法;
消息的按序处理
同上,消息的按序也不能完全依靠于 TCP在说到消息中间件的时候,我们通常都会谈到一个特性:消息的顺序消费问题。这个问题看起来很简单:Producer 发送消息1, 2, 3 。。。 Consumer 1, 2, 3 。。。顺序消费。
但实际情况却是:无论 RocketMQ ,还是 Kafka ,缺省都不保证消息的严格有序消费!
这个特性看起来很简单,但为什么缺省他们都不保证呢?
严格的顺序消费 有多么困难
下面就从 3 个方面来分析一下,对于一个消息中间件来说, 严格的顺序消费 有多么困难,或者说不可能。
发送端
发送端不能异步发送,异步发送在发送失败的情况下,就没办法保证消息顺序。
比如你连续发了 1 2 3 。 过了一会,返回结果 1 失败, 2, 3 成功。你把 1 再重新发送 1 遍,这个时候顺序就乱掉 了。
存储端
对于存储端,要保证消息顺序,会有以下几个问题:
1 )消息不能分区。也就是 1 topic ,只能有 1 个队列。在 Kafka中,它叫做 partition ;在 RocketMQ 中,它叫做 queue 。 如果你有多个队列,那同 1 topic 的消息,会分散到多个分区里面,自然不能保证顺序。
2 )即使只有 1 个队列的情况下,会有第 2 个问题。该机器挂了之后,能否切换到其他机器?也就是高可用问题。
比如你当前的机器挂了,上面还有消息没有消费完。此时切换到其他机器,可用性保证了。但消息顺序就乱掉了。 要想保证,一方面要同步复制,不能异步复制;另1 方面得保证,切机器之前,挂掉的机器上面,所有消息必须消 费完了,不能有残留。很明显,这个很难!!!
接收端
对于接收端,不能并行消费,也即不能开多线程或者多个客户端消费同 1 个队列。
总结
 
从上面的分析可以看出,要保证消息的严格有序,有多么困难!
发送端和接收端的问题,还好解决一点,限制异步发送,限制并行消费。但对于存储端,机器挂了之后,切换的问 题,就很难解决了。
你切换了,可能消息就会乱;你不切换,那就暂时不可用。这 2 者之间,就需要权衡了。
业务需要全局有序吗?
通过上面分析可以看出,要保证一个 topic 内部,消息严格的有序,是很困难的,或者说条件是很苛刻的。 那怎么办呢?我们一定要使出所有力气、用尽所有办法,来保证消息的严格有序吗?
这里就需要从另外一个角度去考虑这个问题:业务角度。正如在下面这篇博客中所说的:
http://www.jianshu.com/p/453c6e7ffff81c
实际情况中: ( 1 )不关注顺序的业务大量存在; ( 2 ) 队列无序不代表消息无序。
三、搜索引擎
概述
全文搜索就是对文本数据的一种搜索方式,文本数据的都多,可以分为顺序搜索法和索引搜索法,,全文检索使用的是索引搜索法
特点(优势):
做了相关度排序
对文本中的关键字做了高亮显示
摘要截取
只关注文本,不考虑语义
搜索效果更加精确 —— 基于单词搜索,比如搜索 Java 的时候找不到 JavaScript ,因为它们是不同的两个单词
使用场景:
替换数据库的模糊查询,提高查询速度,降低数据库压力,增强了查询效率
数据库模糊查询缺点:查询速度慢,左模糊和全模糊会使索引失效,没有相关度排序,没有对文本中关键字
做高亮显示,搜索效果不好 全文检索是搜索引擎的基础 只对“ 指定领域 的网站进行索引和搜索,即垂直搜索 可以在word pdf 等各种各样的数据格式中检索内容 其他场合,比如输入法等
倒排索引
正向索引 的结构如下:
文档 1” ID > 单词 1 :出现次数,出现位置列表;单词 2 :出现次数,出现位置列表; …………
文档 2” ID > 此文档出现的关键词列表。

 

 
当用户在主页上搜索关键词 华为手机 时,假设只存在正向索引( forward index ),那么就需要扫描索引库中的
所有文档,找出所有包含关键词 华为手机 的文档,再根据打分模型进行打分,排出名次后呈现给用户。 因为互联
网上收录在搜索引擎中的文档的数目是个天文数字, 这样的索引结构根本无法满足实时返回排名结果的要求。
所以,搜索引擎会将正向索引重新构建为倒排索引 ,即把文件 ID 对应到关键词的映射转换为 关键词到文件 ID 的映
,每个关键词都对应着一系列的文件,这些文件中都出现这个关键词。
得到 倒排索引 的结构如下:
关键词 1” 文档 1” ID 文档 2” ID …………
关键词 2” :带有此关键词的文档 ID 列表。
 
将原文档传给分次组件 (Tokenizer)
分词组件 (Tokenizer) 会做以下几件事情 ( 此过程称为 Tokenize)
1. 将文档分成一个一个单独的单词。
2. 去除标点符号。
3. 去除停词 (Stop word)
所谓停词 (Stop word) 就是一种语言中最普通的一些单词,由于没有特别的意义,因而大多数情况下不能成为搜索
的关键词,因而创建索引时,这种词会被去掉而减少索引的大小。
英语中挺词 (Stop word) 如: “the”,“a” “this” 等。
对于每一种语言的分词组件 (Tokenizer) ,都有一个停词 (stop word) 集合。
经过分词 (Tokenizer) 后得到的结果称为词元 (Token)
在我们的例子中,便得到以下词元 (Token)
“Students” “allowed” “go” “their” “friends” “allowed” “drink” “beer” “My” “friend” “Jerry” “went
“school” “see” “his” “students” “found” “them” “drunk” “allowed”
将得到的词元 (Token) 传给语言处理组件 (Linguistic Processor)
语言处理组件 (linguistic processor) 主要是对得到的词元 (Token) 做一些同语言相关的处理。
对于英语,语言处理组件 (Linguistic Processor) 一般做以下几点:
1. 变为小写 (Lowercase)
2. 将单词缩减为词根形式,如 “cars ” “car ” 等。这种操作称为: stemming
3. 将单词转变为词根形式,如 “drove ” “drive ” 等。这种操作称为: lemmatization
Stemming lemmatization 的异同:
相同之处: Stemming lemmatization 都要使词汇成为词根形式。
两者的方式不同:
Stemming 采用的是 缩减 的方式: “cars” “car” “driving” “drive”
Lemmatization 采用的是 转变 的方式: “drove” “drove” “driving” “drive”
两者的算法不同:
Stemming 主要是采取某种固定的算法来做这种缩减,如去除 “s” ,去除 “ing” “e” ,将 “ational”
“ate” ,将 “tional” 变为 “tion”
Lemmatization 主要是采用保存某种字典的方式做这种转变。比如字典中
“driving” “drive” “drove” “drive” “am, is, are” “be” 的映射,做转变时,只要查字典就可以了。
Stemming lemmatization 不是互斥关系,是有交集的,有的词利用这两种方式都能达到相同的转换。
语言处理组件 (linguistic processor) 的结果称为词 (Term)
在我们的例子中,经过语言处理,得到的词 (Term) 如下:
“student” “allow” “go” “their” “friend” “allow” “drink” “beer” “my” “friend” “jerry” “go” “schoo
l” “see” “his” “student” “fifind” “them” “drink” “allow”
也正是因为有语言处理的步骤,才能使搜索 drove ,而 drive 也能被搜索出来。
将得到的词 (Term) 传给索引组件 (Indexer)

分词器
WhitespaceAnalyzer 仅仅是去掉了空格,没有其他任何操作,不支持中文。
SimpleAnalyzer 讲除了字母以外的符号全部去除,并且讲所有字符变为小写,需要注意的是这个分词器同样把数据也去除了,同样 不支持中文。
StopAnalyzer 这个和SimpleAnalyzer 类似,不过比他增加了一个的是,在其基础上还去除了所的
stop words ,比如 the, a, this 这些。这个也是不支持中文的。
StandardAnalyzer 英文方面的处理和StopAnalyzer 一样的,对中文支持,使用的是单字切割。
CJKAnalyzer 这个支持中日韩,前三个字母也就是这三个国家的缩写。这个对于中文基本上不怎么用吧,对中文的支持很烂,它 是用每两个字作为分割,分割方式个人感觉比较奇葩,我会在下面比较举例。
SmartChineseAnalyzer 中文的分词。比较标准的中文分词,对一些搜索处理的并不是很好

相关文章:

  • 基于SSM的视频管理系统【完整项目源码】
  • 做到年收入一百万需要怎样做?
  • 人工神经网络连接方式,全连接神经网络作用
  • RabbitMq消息队列
  • 神经网络计算机的用途是,神经网络计算机的应用
  • visual studio快捷键
  • gif制作动图教你一键搞定,图片转gif和视频转gif怎么制作
  • 国产数据库百家争鸣,百花齐放有感
  • 电脑重装系统Win11edge浏览器看视频绿屏如何处理?
  • 【python】计算mel频率可能比你想象的要复杂一点
  • springboot+vue+elementui校园博客管理系统
  • 从QQ秀到VR穿搭,为什么服装搭配对虚拟化身如此重要
  • LeetCode每日一题——946. 验证栈序列
  • Mybatis的注解实现复杂映射开发
  • 【Games101 作业6 + 附加题】渲染兔子 BVH SAH 代码
  • #Java异常处理
  • ES6简单总结(搭配简单的讲解和小案例)
  • Spark学习笔记之相关记录
  • Sublime text 3 3103 注册码
  • vue--为什么data属性必须是一个函数
  • 类orAPI - 收藏集 - 掘金
  • 三栏布局总结
  • 网页视频流m3u8/ts视频下载
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 仓管云——企业云erp功能有哪些?
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • 移动端高清、多屏适配方案
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (BFS)hdoj2377-Bus Pass
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (编译到47%失败)to be deleted
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (机器学习的矩阵)(向量、矩阵与多元线性回归)
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (算法)前K大的和
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .net core 6 集成和使用 mongodb
  • .net mvc部分视图
  • .Net程序帮助文档制作
  • .net打印*三角形
  • .net连接oracle数据库
  • .NET文档生成工具ADB使用图文教程
  • [20161214]如何确定dbid.txt
  • [C#]winform制作圆形进度条好用的圆环圆形进度条控件和使用方法
  • [caffe(二)]Python加载训练caffe模型并进行测试1
  • [CF226E]Noble Knight's Path
  • [LeetCode]—Add Binary 两个字符串二进制相加
  • [LeetCode]剑指 Offer 40. 最小的k个数
  • [Linux]创建新用户并授予root权限
  • [LLM][FT]大模型Fine-Tuning相关技术0
  • [one_demo_12]递归打印*\n*.*.\n*..*..\n图形