Java技术栈 —— Redis的雪崩、穿透与击穿
Java技术栈 —— Redis的雪崩、穿透与击穿
- 〇、实验的先导条件(Nginx+Jmeter)
- 一、Redis缓存雪崩、缓存穿透、缓存击穿
- 1.1 雪崩
- 1.2 穿透
- 1.3 击穿
- 二、Redis应用场景——高并发
- 2.1 单机部署的高并发问题与解决(JVM级别锁)
- 2.2 集群部署的高并发问题与解决(分布式锁)
- 2.2.1 代码1(存在非原子操作与释放问题)
- 2.2.2 代码2(finally块中,存在释放其它线程锁的可能性)
- 2.2.3 代码3(redisson)
- 2.2.3.1 Java中嵌入Lua脚本
- 2.2.4 对代码3的性能优化、redis主从架构锁失效问题的解决方案
- 2.2.4.1 性能优化的解决(分段锁,重要)
- 2.2.4.2 主从架构锁失效问题的解决
- 2.2.4.2.1 zookeeper
- 2.2.4.2.2 redis的RedLock
- 三、Redis与数据库的数据一致性
〇、实验的先导条件(Nginx+Jmeter)
首先你需要掌握Nginx负载均衡与Jmeter压测工具,搭建过程与使用方式,见参考文章。
参考文章或视频链接 |
---|
[1] 《Java技术栈 —— Nginx的使用》 |
[2] 2 ways to install Apache JMeter on Ubuntu 22.04 LTS Linux |
一、Redis缓存雪崩、缓存穿透、缓存击穿
关于雪崩、穿透与击穿的原理,可以先看本节的参考文章[1],代码以后再写到文章中。
1.1 雪崩
1.2 穿透
1.3 击穿
一、参考文章或视频链接 |
---|
[1] 【什么是Redis缓存雪崩、穿透、击穿,十分钟给你讲的明明白白】- bilibili |
二、Redis应用场景——高并发
高并发导致的问题,本质就是资源争抢。 在操作系统中,这类问题的雏形有哲学家用餐问题、进程争夺计算资源,相关解决机制有信号量机制,所以道理都是相通的,高并发在计算机领域并不是什么新鲜事,只是落地到应用场景,会有一些其它考量。就像古代兵符印信,或是倚天屠龙记中说的“武林至尊,宝刀屠龙,号令天下,莫敢不从!倚天不出,谁与争锋?”,听谁的问题的解决方法啊,就是象征物在谁手上就听谁的,包括抢职位争权力,也可以理解为一种并发,谁坐到了那个位置,才有号令的权力,但是权力是致命毒药,要小心哦!
首先导入jedis
依赖,从而可以用java
程序包操纵redis
,以下是完整依赖。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version></dependency><!--实现分布式锁redisson--><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.6.5</version></dependency>
<!-- 也可以手动引入Jedis,不用SpringBoot提供的spring-boot-starter-data-redis--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>5.1.0</version></dependency><!--如果你导入了下面的SpringBoot父依赖,会自带Jedis,不过版本不一定最新而已,并且有些-->
<!-- <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.1</version><relativePath/> </parent>用SpringBoot提供的Jedis版本<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency> -->
然后,我们开始复现高并发问题。首先是假设你已经搭建了一个简单的SpringBoot项目架构,并且相关的Nginx配置也已配置好,可以看 参考文章[5] 《Java技术栈 —— Nginx的使用》第3.1节,那正是我为本文而写,项目demo搭好了,port
号也初步定为9998
。
二、参考文章或视频链接 |
---|
[1] Java guide(Jedis) - Redis Offical Website |
[2] Intro to Jedis – the Java Redis Client Library |
[3] Redis可视化工具 RedisInsight | The best Redis GUI |
[4] 示例代码来源,图灵诸葛老师,讲的确实很好: 【这可能是目前讲的最好的Redis高并发架构教程,堪称Redis架构实战的天花板!】 |
[5] 《Java技术栈 —— Nginx的使用》 |
2.1 单机部署的高并发问题与解决(JVM级别锁)
(1)先在redis中设置缓存好一个键值对,键的名字为store
,这是我们要高并发的对象。
$ redis-cli
127.0.0.1:6379> SETNX store 2000
127.0.0.1:6379> get store
"2000"
(2)写一段操作redis
获取store
值的代码,完整项目代码最后会附上开源地址。
@RestController
public class demoController {public static int count = 0;@RequestMapping("deduct_stock_then_get_stock")public Integer deductStock(){Jedis jedis = new Jedis("127.0.0.1", 6379);int currentStock = Integer.parseInt(jedis.get("stock"));if (currentStock > 0){currentStock--;jedis.set("stock", String.valueOf(currentStock));System.out.println("扣减成功,剩余库存"+currentStock);}else{System.out.println("扣减失败,库存不足");}return currentStock;}
}
启动项目并访问http://127.0.0.1:9998/deduct_stock_then_get_stock
,让我们先看看效果,慢慢迭代,好的,现在浏览器上已经返回了当前库存数量,显示是199不要在意,这个数字随时可以在redis中修改。

然后我们用Jmeter,模拟多个用户同时访问 http://127.0.0.1:9998/deduct_stock_then_get_stock
,上面这段Java代码会出什么问题呢?简单来说,就是会出现超卖问题。按下面的过程配置,并点击绿色的启动箭头,就开启了压测。
这是控制台输出的结果,果然,出现了超卖问题,这说明会有多个用户都看到了相同的1999库存,很明显是有问题的,这是因为多个用户同时进入了相同段代码的执行过程,并且都拿到了一个currentStock
变量作为副本,而这个变量在获取的时候出现了值相同的情况。

@RestController
public class demoController { //方法(2)以函数为单位上锁,写成 public synchronized Integer deductStock(){@RequestMapping("deduct_stock_then_get_stock")public Integer deductStock(){Jedis jedis = new Jedis("127.0.0.1", 6379);synchronized (this){ //方法(1)以对象为单位上锁int currentStock = Integer.parseInt(jedis.get("stock")); //上一段未加synchronized的代码,问题出在这里,都获取到了一样的值,那么再进行currentStock--,就是1999了if (currentStock > 0){currentStock--;jedis.set("stock", String.valueOf(currentStock));System.out.println("扣减成功,剩余库存"+currentStock);}else{System.out.println("扣减失败,库存不足");}return currentStock;}}}
只加了一个锁,问题解决,那么到目前为止,单机部署的高并发问题,可以算解决了,如果集群部署的话,上面这段代码还有用吗?

2.2 集群部署的高并发问题与解决(分布式锁)
根据参考视频[4]所说,上面的代码也只是解决了单机部署下的高并发问题,如果是集群部署,启动了多个服务分别部署在不同机器上呢?这个时候Nginx会分发请求到不同服务实例上,还会出现上面的超卖现象吗?答案是会的,这相当于线程A在服务A上执行扣库存,线程B在服务B上执行扣库存,这两个线程压根不归同一个JVM虚拟机进程管,是没办法用上面的加synchronized
关键字去限制的,具体可以看视频讲解。但是,只要思想不滑坡,办法总比困难多,请看。PS:你能想象,其实12306是全世界最能抗高并发的软件吗?总有些东西在微不足道的角落里熠熠生辉,独自发热。
还是刚刚那段,在单机部署上解决了高并发问题的代码,我们来多启动一个服务,只是端口不同。

由于在参考文章[5]中,我已经配置了Nginx,所以我们的Jmeter测试地址,应该改为http://127.0.0.1:8011/deduct_stock_then_get_stock
,看下面的两张截图,和视频[5]里说的一样,确实在集群部署时会出现超卖问题。


下面是加上分布式锁的解决方法, 但是仍然存在问题。
2.2.1 代码1(存在非原子操作与释放问题)
@RestController
public class demoController {@RequestMapping("deduct_stock_then_get_stock")public Integer deductStock(){String lockKey = "product_100";Jedis jedis = new Jedis("127.0.0.1", 6379);long result = jedis.setnx(lockKey,"xxx"); // 获取分布式锁if(result == 0){System.out.println("争抢分布式锁失败"); /*注意,这里实际使用会有问题,不应该return,只是作为示例争抢分布式锁失败的话也应该程门立雪,三顾茅庐,不可半途而返,半途而返会导致许多业务请求被扼杀*/ return 500; }//*****重要思维*****//业务逻辑,可能出异常,导致分布式锁无法释放,永远要考虑系统的业务逻辑被某种不可抗力因素停止,不管是运维还是什么,程序要具备健壮性。int currentStock = Integer.parseInt(jedis.get("stock"));if (currentStock > 0) {currentStock--;jedis.set("stock", String.valueOf(currentStock));System.out.println("扣减成功,剩余库存" + currentStock);} else {System.out.println("扣减失败,库存不足");}jedis.del(lockKey); //释放分布式锁return currentStock;}}
2.2.2 代码2(finally块中,存在释放其它线程锁的可能性)
下面的代码对上面的代码做了两处改进:
(1)将获取与设置超时时间这两步,组合成原子操作,不可分离。
(2)增加clientID
,保证释放的是自己加的锁,但在释放仍旧可能存在问题,视频中提到用redisson
进行解决,见 redisson - github wiki,redisson与jedis区别在于,jedis只是提供一些原生命令的实现,redisson可以提供分布式锁的实现能力。
@RequestMapping("deduct_stock_then_get_stock")
public Integer deductStock(){ //集群版//(1)获得分布式锁String lockKey = "product_100";Jedis jedis = new Jedis("127.0.0.1", 6379);String clientID = UUID.randomUUID().toString(); //唯一ID,加锁人的身份// String result = jedis.setex(lockKey, 10, clientID); //该命令是原子命令,将获取与设置超时时间这两步,组合成原子操作,不可分离,但还是存在问题,如业务逻辑执行较慢,锁已经超时释放了业务逻辑还没执行完,又导致了并发Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientID, 10, TimeUnit.SECONDS); //该命令是原子命令,将获取与设置超时时间这两步,组合成原子操作,不可分离,但还是存在问题,如业务逻辑执行较慢,锁已经超时释放了业务逻辑还没执行完,又导致了并发stringRedisTemplate.opsForValue().get(lockKey);if(result == Boolean.FALSE){System.out.println("争抢分布式锁失败"); // 分布式锁争抢失败应该等待,而不应该直接returnreturn 500;}try{//*****重要思维*****//(2)执行业务逻辑,可能出异常,导致分布式锁无法释放,永远要考虑系统的业务逻辑被某种不可抗力因素停止,不管是运维还是什么,要具备健壮性。//此处可能存在的异常有:// (2.1)业务逻辑执行失败,但finally可以正常释放分布式锁// (2.2)应用被重启,连finally都无法执行,那么就需要令分布式锁自动过期int currentStock = Integer.parseInt(jedis.get("stock"));if (currentStock > 0) {currentStock--;jedis.set("stock", String.valueOf(currentStock));System.out.println("扣减成功,剩余库存" + currentStock);} else {System.out.println("扣减失败,库存不足");}return currentStock;}finally{//(3)出异常时释放分布式锁,这里释放分布式锁可能存在问题if (clientID.equals(jedis.get(lockKey))){//自己加的锁才能释放,中间还可能存在执行时间的间隔,开一个分线程,将分布式锁加时,检测这把分布式锁还是否加载在该主线程中,加时到直到业务逻辑执行完成为止jedis.del(lockKey);}}}
2.2.3 代码3(redisson)
@RequestMapping("deduct_stock_then_get_stock_cluster_redisson")
public Integer deductStock3(){ //集群+redisson版//(1)获得分布式锁String lockKey = "product_100";Jedis jedis = new Jedis("127.0.0.1", 6379);RLock redissonLock = redisson.getLock(lockKey); //获取RLock对象try{redissonLock.lock(); //(2)上锁,底层调用redis命令时用到了lua脚本//(3)业务逻辑int currentStock = Integer.parseInt(jedis.get("stock"));if (currentStock > 0) {currentStock--;jedis.set("stock", String.valueOf(currentStock));System.out.println("扣减成功,剩余库存" + currentStock);} else {System.out.println("扣减失败,库存不足");}return currentStock;}finally{//(4)释放锁redissonLock.unlock();}
}
redisson是一种Redis Java client,上述redisson的使用方法,也是大厂在生产环境会用到的,但上面的代码还有两个问题:
(1)性能问题,虽然没有超卖,但会导致系统性能问题,需要开始性能优化。
(2)redis主从架构下,锁失效问题。比如Master同步给Slave分布式锁时,Master正好挂掉,然后重新选举的Master正好没有同步到这把锁,就失效了。
2.2.3 参考文章或视频链接 |
---|
[1] 1. Overview of Redisson - GitHub |
2.2.3.1 Java中嵌入Lua脚本
什么是Lua脚本?我第一次听说Lua,是在敖丙解说B站出事那次,最后定位到一段Lua写的gcd()
代码,久闻大名却未上手实操过。请看本节参考文章[1]。
2.2.3.1 参考文章或视频链接 |
---|
[1] Lua:about - Offical Website |
2.2.4 对代码3的性能优化、redis主从架构锁失效问题的解决方案
2.2.4.1 性能优化的解决(分段锁,重要)
先了解下并发编程集合类ConcurrentHashMap
,这是一个高并发的Java集合类且线程安全,其保证线程安全的原理是,使用分段锁。受此启发,性能优化也可以用分段加锁,每个线程去不同的段位请求锁即可。

2.2.4.1 参考文章或视频链接 |
---|
[1] 《详解ConcurrentHashMap》- CSDN |
2.2.4.2 主从架构锁失效问题的解决
- Zookeeper集群是
CP
架构。- Redis单机是
CP
架构,Redis集群是AP
架构。[5]
CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

CAP理论
2.2.4.2 参考文章或视频链接 |
---|
[1] Redis persistence and CAP theorem-From Zero to Hero -part II |
[2] 《架构设计之「 CAP 定理 」》- CSDN |
[3] 《CAP定理一文带你速解(通俗易懂,图文并茂)》- CSDN |
推荐优先阅读:[4] 《NoSQL 简介》- 菜鸟教程 |
[5] 《redis是CA还是CP呢》- 腾讯云 |
2.2.4.2.1 zookeeper
使用zookeeper,zookeeper解决主从架构锁失效问题更合适,但会牺牲一点性能。
2.2.4.2.1 参考文章或视频链接 |
---|
[1] What is Apache ZooKeeper? |
[2] Welcome to Apache ZooKeeper |
[3] 《2.0 Zookeeper 安装配置》- 菜鸟 |
[4] 《zookeeper快速入门一:zookeeper安装与启动》 |
2.2.4.2.2 redis的RedLock
要超过半数redis节点加锁成功才算成功,这样的原理又回到了zookeeper,还是会损失加锁的性能,所以RedLock实现的是否完善依旧存在争议。
三、Redis与数据库的数据一致性
(1)要保证的是数据的最终一致性,而不是强一致性,若要保证数据强一致性会损失性能,这违背了使用Redis的初衷。
(2)删除Redis缓存,而不是更新Redis缓存。
(3)先更新数据库数据。
三、 参考文章或视频链接 |
---|
[1] 《字节二面:redis如何保证缓存和数据库的一致性》 |
相关文章:

Java技术栈 —— Redis的雪崩、穿透与击穿
Java技术栈 —— Redis的雪崩、穿透与击穿 〇、实验的先导条件(NginxJmeter)一、Redis缓存雪崩、缓存穿透、缓存击穿1.1 雪崩1.2 穿透1.3 击穿 二、Redis应用场景——高并发2.1 单机部署的高并发问题与解决(JVM级别锁)2.2 集群部署…...

Scala知识点——App类
我们在代码中一般程序都是是通过main方法进入。但是在scala中提供了一个App类,通过继承可以实现不用显式的调用main方法就能运行。 App类中实现了main方法:...

(vue)增加行,对应行删除
(vue)增加行,对应行删除 效果: 代码: <div v-for"(ele,i) of algorithmList" :key"i"><el-form-item label"模型类型"><el-selectv-model"ele.algorithmId"placeholder"选择模…...

案例088:基于微信小程序的校车购票平台设计与实现
文末获取源码 开发语言:Java 框架:SSM JDK版本:JDK1.8 数据库:mysql 5.7 开发软件:eclipse/myeclipse/idea Maven包:Maven3.5.4 小程序框架:uniapp 小程序开发软件:HBuilder X 小程序…...

pytorch集智-1安装与简单使用
1 安装 1.1 简介 pytorch可用gpu加速,也可以不加速。gpu加速是通过cuda来实现,cuda是nvidia推出的一款运算平台,它可以利用gpu提升运算性能。 所以如果要装带加速的pytorch,需要先装cuda,再装pytorch,如…...

『番外篇六』SwiftUI 取得任意视图全局位置的三种方法
概览 在 SwiftUI 开发中,利用描述性代码我们可以很轻松的构建各种丰富多彩的视图。我们可以设置它们的大小、位置、颜色并应用不计其数的修改器。 但是,小伙伴们是否想过在 SwiftUI 中如何获取一个视图的全局位置坐标呢? 在本篇博文中,您将学到如下内容: 概览1. SwiftU…...

Ribbon相关面试及答案(2024)
1、Ribbon是什么,它在微服务架构中扮演什么角色? Ribbon是一个客户端负载均衡器,它在微服务架构中扮演着关键性的角色。Ribbon的设计理念是在客户端进行服务发现和负载均衡,这种方式不同于传统的通过中心化的负载均衡器ÿ…...

【Mybatis】深入学习MyBatis:CRUD操作与动态SQL实战指南
🍎个人博客:个人主页 🏆个人专栏: Mybatis ⛳️ 功不唐捐,玉汝于成 目录 前言 正文 一基本用法 1 CRUD操作 1. 增加(Create) 2. 查询(Read) 3. 更新&#x…...

前端uniapp的tab选项卡for循环切换、开通VIP实战案例【带源码/最新】
目录 效果图图1图2 源码最后 这个案例是uniapp,同样也适用Vue项目,语法一样for循环,点击切换 效果图 图1 图2 源码 直接代码复制查看效果 <template><view class"my-helper-service-pass"><view class"tab…...
【我的RUST库】get_local_info
get_local_info是一个获取linux本地信息的Rust三方库,其目标是降低获取本地linux系统信息的难度。支持银河麒麟10、UOS、鸿蒙等国产系统 项目维护:长期 当前版本0.1.4,已有功能: 1.获取活动网卡信息:网卡࿰…...

【JUC】Synchronized及JVM底层原理
Synchronized使用方式 Synchronized有三种应用方式 作用于实例方法,当前示实例加锁进入同步代码前要获得当前实例的锁,即synchronized普通同步方法,调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置。 如果设置了,执行…...

用户管理第2节课--idea 2023.2 后端--实现基本数据库操作(操作user表) -- 自动生成 --【本人】
一、插件安装 1.1 搜索插件 mybatis 安装 1.2 接受安装 1.3 再次进入,说明安装好了 1.4 与鱼皮不同点 1)mybatis 版本不一致 鱼皮: 本人: 2)鱼皮需重启安装 本人不需要 1.5 【需完成 三、步骤,再来看】 …...

深入了解隧道代理HTTP的协议与技术细节
隧道代理HTTP,作为一种网络通信的桥梁技术,其背后的协议与技术细节承载着网络世界的无尽奥秘。对于技术人员而言,深入了解这些细节,不仅有助于优化网络性能,还能为网络安全提供坚实的保障。 一、隧道代理HTTP的协议基…...
系统运维-Apache服务的基础安装与使用
Apache:WEB服务器的软件 Apache HTTP是一个模块化的服务器,源于NCSAhttpd服务器,经过多次修改,成为世界使用排名第一的WEB服务器软件。 目录 HTTP HTTPS HTTP 首先安装apache yum install httpd* -y #install http 要有网站首…...

Android 相机库CameraView源码解析 (四) : 带滤镜预览
1. 前言 这段时间,在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位,在项目前期,的确为我们节省了不少时间。 但随着项目持续深入,对于CameraView的使用进入深水区,逐…...

蜥蜴目标检测数据集VOC格式1400张
蜥蜴,一种爬行动物,以其独特的形态和习性,成为了人们关注的焦点。 蜥蜴的外观多样,体型大小不一。它们通常拥有长条的身体、四肢和尾巴,鳞片覆盖全身,这使得它们能够在各种环境中轻松移动。大多数蜥蜴拥有…...

2020年认证杯SPSSPRO杯数学建模C题(第一阶段)抗击疫情,我们能做什么全过程文档及程序
2020年认证杯SPSSPRO杯数学建模 C题 抗击疫情,我们能做什么 原题再现: 2020 年 3 月 12 日,世界卫生组织(WHO)宣布,席卷全球的冠状病毒引发的病毒性肺炎(COVID-19)是一种大流行病。…...

Java技术栈 —— Hadoop入门(一)
Java技术栈 —— Hadoop入门(一) 一、Hadoop第一印象二、安装Hadoop三、Hadoop解析3.1 Hadoop生态介绍3.1.1 MapReduce - 核心组件3.1.2 HDFS - 核心组件3.1.3 YARN - 核心组件3.1.4 其它组件3.1.4.1 HBase3.1.4.2 Hive3.1.4.3 Spark 一、Hadoop第一印象…...

Shell脚本小游戏:石头剪刀布
脚本代码: #!/bin/bash echo "接下来的是石头剪刀布的游戏" echo "定义1:包子;2:剪刀;3:布" echo "------------------------------" NUMecho $[RANDOM%31] #1包子 #2剪刀…...

Windows10系统的音频不可用,使用疑难解答后提示【 一个或多个音频服务未运行】
一、问题描述 打开电脑,发现电脑右下角的音频图标显示为X(即不可用,无法播放声音),使用音频自带的【声音问题疑难解答】(选中音频图标,点击鼠标右键,然后选择“声音问题疑难解答(T)”…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...