Jinqq's Home

证明自己

🖼️橱窗

欢迎来到我的橱窗!这里是我精心整理的各种有趣的内容,每一项都代表了我的兴趣与热爱。你可以在这里找到游戏推荐、书籍推荐等内容。

阅读全文 »

一 一人一单的并发安全问题

原来使用synchronized悲观锁用户id来保证一人一单,如下图:

image-20250216160638777

但在集群部署情况下,仍然会出现并发安全问题,不同jvm下的线程无法实现锁互斥,如下:

image-20250216160705182

二 分布式锁

分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。

image-20250216161631886

三 Redis分布式锁

实现

image-20250216175750707

image-20250216162347593

image-20250216162523678

安全问题

使用Redis锁可能存在这样的安全问题,当线程1因某种原因线程阻塞时,可能会释放其他线程获取的锁。:

image-20250216172309261

为了解决该问题,在释放锁时应该判断是否是自己的锁,如下:

image-20250216172442370

这时仍然有可能出现问题,如下图,当线程1获取锁标识并判断一致后,还未释放锁时遇到阻塞(如jvm垃圾回收),导致后面又释放了其他线程的锁。

image-20250216173426178

因此必须确保判断锁标识动作和释放锁动作的原子性。

Redis的Lua脚本

Redis提供了Lua脚本功能,在一个脚本中编写多条Redis命令,确保多条命令执行时的原子性。

1
2
3
4
5
6
7
8
-- 获取锁中的线程标识 get key
local id = redis.call('get', KEYS[1])
-- 比较线程标识与锁中的标识是否一致
if (id == ARGV[1]) then
-- 一致则释放锁 del key
return redis.call('del', KEYS[1])
end
return 0

四 Redisson

Redisson分布式锁

基于Redis实现的分布式锁有以下问题:

image-20250216180139034

因此使用Redisson来优化。Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。

image-20250216180429234

Redisson分布式锁原理

可重入

可重入锁:同一个线程多次请求锁,可能会造成死锁。因此需要允许可重入锁。

利用redis中的hash数据结构,为锁额外记录一个值,代表该锁的重入次数。

image-20250216193242076

同一个线程每重入一次锁,将值+1,释放锁改为将值-1,值为0时才真正释放锁。

image-20250216193339921

为保证原子性,同样要使用Lua脚本。

重试机制

redisson可以设置等待时间waitTime,在获取锁失败后,会订阅并等待释放锁的信号,然后进行重试,直到获取锁成功或waitTime消耗完。

超时续约

redisson中的watchdog在获取锁时设置了一个定时任务,每隔一段时间刷新锁的有效期(releaseTime/3),直到释放锁时取消定时任务。注意,如果设置了leaseTime就没有watchdog了。

image-20250216231353719

主从一致性问题

问题产生原因:

image-20250216231742145

redisson解决方案:MultiLock

将每一个redis节点都视为主节点,只有向每个节点获取锁成功,才算成功。此时如果一个redis节点宕机,并不影响锁的正常获取和释放。此外也可以给每个节点建立主从关系,此时如果一个主节点宕机且刚好没有完成同步,由于从节点没有锁的标识,其他线程也无法成功获取锁,满足要求。

image-20250216231841738 image-20250216232050725

image-20250216233314954

一 超卖问题

image-20250216144807538

二 悲观锁与乐观锁

悲观锁

  • 认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行。
  • 例如Synchronized、Lock都属于悲观锁。

乐观锁

  • 认为线程安全问题不一定会发生,因此不加锁,只是在更新数据时去判断有没有其他线程对数据做了修改。
  • 如果没有修改则认为是安全的,自己才更新数据。
  • 如果已经被其他线程修改说明发生了安全问题,此时可以重试或异常。

三 乐观锁

乐观锁的关键是判断之前查询得到的数据是否有被修改过。

但是存在成功率低的问题。

版本号法

image-20250216145309648 image-20250216145323789

CAS法(Compare and Set)

image-20250216145514582

缓存穿透

缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。例如,一个不怀好意的黑客同时间内大量请求不存在的数据,这些请求都会打到数据库上,导致数据库 崩溃。

常见的解决方案有两种:缓存空对象和布隆过滤。

其他解决方案:增强id的复杂度,避免被猜测id规律;做好数据的基础格式校验;加强用户权限校验;做好热点参数的限流。

阅读全文 »

一 缓存更新策略

内存淘汰 超时剔除 主动更新
说明 不用自己维护,利用Redis的内存淘汰机制,当内存不足时自动淘汰部分数据,当内存不足时自动淘汰部分数据。下次查询时更新缓存。 给缓存数据添加TTL时间,到期后自动删除缓存。下次查询时更新缓存。 编写业务逻辑,在修改数据库的同时,更新缓存。
一致性 一般
维护成本

业务场景:

  • 低一致性需求:使用内存淘汰机制。例如店铺类型的缓存。
  • 高一致性需求:主动更新, 并以超时剔除作为兜底方案。例如店铺详情的缓存。
阅读全文 »

考试形式

一共十道简答题,书本——《软件安全技术(陈波)(Z-Library)》上的十章的课后题中每章选一题。

2024期末试卷回忆

  1. 什么是零日(0 day)漏洞?什么是零日(0 day)攻击?
  2. 试述软件漏洞的概念,谈谈软件漏洞与软件错误、软件缺陷、软件Bug的区别与联系。
  3. 分析攻击事件中XSS漏洞的原理。
  4. 什么是敏捷SDL?敏捷SDL和经典SDL的主要区别是什么?
  5. 软件安全需求的获取方法中的策略分解是指什么?
  6. 什么是最小授权原则?试举例说明软件设计时哪些措施是采用了最小授权原则。
  7. 安全编译是指在代码编译阶段采取的哪些安全措施?
  8. 什么是Rootkit?它与木马和后门有什么区别与联系?
  9. 所开发的软件中使用了带GPL许可证的开源软件,那么这个软件是不是就要开源?
  10. 试述软件版权的概念。针对软件的版权,有哪些侵权行为?有哪些保护措施?
阅读全文 »

关于考试

开卷考试,允许携带一张A4纸,可以展示大家的压缩字体能力和眼部放大镜能力了。

本章只是对本课所有课件的一个整理,同时还偷了一些来自参考的整理。

没有银弹NoSilverBullet

Author:Fredrick P. Brooks1986

主要思想

  • 没有任何一种单纯的技术或管理上的进展,能够独立地承诺十年内使生产率、可靠性或简洁性获得数量级上的进步。
  • 所有大家看到的技术、管理方法都不会给软件开发带来意想不到的效果。
  • 软件开发在根本上就是困难的。
阅读全文 »

没有银弹

  1. 在《没有银弹》中,Brooks主张:

    • 存在能够显著提高软件生产力的技术

    • 软件开发的主要困难是复杂度、一致性、变化性和不可见性

    • 通过采用新的编程语言,软件危机可以完全得到解决。

    • 大多数的软件开发问题都是由于使用了错误的工具。

  2. 《没有银弹》文章中提到的“银弹”是指:

    • 一种新的编程语言。
    • 一种新的软件开发方法。
    • 一种新的软件工具。
    • 一个具有魔法般效果的解决方案。
阅读全文 »
0%