分布式锁
分布式锁
1.什么是分布式锁分布式锁:满足分布式系统或集群模式下多线程可见并且互斥的锁。
2.分布式锁的实现分布式锁的核心是实现多线程之间的互斥,而满足这一点的方式有很多,常见的有三种:
Mysql
Redis
Zookeeper
互斥
利用mysql本身互斥锁机制
利用setnx这样的互斥命令
利用节点的唯一性和有序性实现互斥
高可用
好
好
好
高性能
一般
好
一般
安全性
断开连接,自动释放锁
利用锁超时的时间,到期释放
临时节点,断开连接自动释放
实现 分布式锁时需要实现的两个基本方法:
获取锁:
互斥:确保只能有一个线程获取锁# 添加锁,利用setnx的互斥特性
SETNX lock thread1
# 添加锁过期时间,避免服务宕机引起的死锁现象
EXPIRE lock 10
# 改善应该放在一起写,分开写的话在 SETNX lock thread1执行后, EXPIRE lock 10执行前,如果服务器发生宕机,lock将无法释放
# 添加锁,NX是互斥,EX是设置过期时间
SET lock thread1 NX EX 10
...
优惠券秒杀
优惠券秒杀1.全局唯一ID1.1 全局ID生成器每个店铺都可以发布优惠券当用户抢购时,就会生成订单并保存到tb_voucher_order这张表中,而订单表如果使用数据库自增ID就存在一些问题:
id的规律太明显
受单表数据量的限制
全局ID生成器,是一种在分布式系统下用来生成全局唯一ID的工具,一般需要满足以下特性为了增加ID的安全性,我们可以不直接使用Redis自增的数值,而是拼接一些其他信息:ID的组成部分:
符号位:1bit,永远为0
时间戳:31bit,以秒为单位,可以使用69年
序列号:32bit,秒内的计数器,支持每秒产生2^32个不同的ID
1.2 全局ID生成器(代码实现)@Component
public class RedisIdWorker {
@Resource
private StringRedisTemplate stringRedisTemplate;
private final long BEGIN_TIMESTAMP = 1672531200L;
private final long COUNT_BITS = ...
缓存
缓存1.什么是缓存缓存就是数据交换的缓冲区(称作Cache),是存储数据的临时地方,一般读写性能较高。缓存的作用:
降低后端负载
提高读写效率,降低响应时间
缓存和成本
数据一致性成本
代码维护成本
运维成本
2.添加Redis缓存代码实现:
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
@Resource
StringRedisTemplate stringRedisTemplate;
@Override
public Result queryById(Long id) {
String key = CACHE_SHOP_KEY + id;
//1.从redis查询商铺缓存
String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
...
Redis基础
Redis基础学习1. Redis数据结构介绍Redis是一个key-value的数据库,key一般是String类型,不过value的类型多种多样:
2. Redis通用命令
KEYS: 查看符合模板的所有key
DEL: 删除一个指定的key
EXISTS: 判断key是否存在
EXPIRE: 给一个key设置有效期,有效期到期时该key会被自动删除
TTL: 查看一个key的剩余有效期 通过help[command]可以查看一个命令的具体用法,例如:
3. String类型String类型,也就是字符串类型,是Redis中最简单的存储类型。其value是字符串,不过根据字符串的格式不同,又可以分为3类:
string: 普通字符串
int: 整数类型,可以做自增、自减操作
float: 浮点类型,可以做自增、自减操作不管是哪种格式,底层都是字节数组形式存储,只不过是编码的方式不同。字符串类型的最大空间不能超过512m
4. String类型常见命令有:
SET(单个增): 添加或者修改一个已经存在的一个String类型的键值对
GET(单个查): 根据key获 ...
lambda的双冒号语法
lambda的双冒号语法在学习过程中最近经常能碰见java中的双冒号::语法形式举例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.forEach(System.out::println);
String name = null;
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
lqw.like(Book::getName,name);
bookDao.selectList(lqw);
查阅相关资料了解到双冒号 ::语法是lambda表达式的一种表示
我们先来复习一下Lambda表达式
Lambda表达式1.组成格式
组成Lambda表达式的三要素:形式参数,箭头,代码块
Lambda表达式的格式①、格式:(形式参数) -> {代码块}②、形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可③、->:由英文中画线和大于符号组成,固定写法。代表指向动作④、代码块:是我们 ...
RESTful入门案例
RESTful入门案例1. 环境准备
创建一个Web的Maven项目
pom.xml添加Spring依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhn</groupId>
<artifactId>springmvc</artifactId>
<version>1.0-SNAPSHOT</version& ...
REST风格
Rest风格REST简介REST(Representational State Transfer),表现形式状态转换,它是一种软件架构 风格当我们想表示一个网络资源的时候,可以使用两种方式:
传统风格资源描述形式http://localhost/user/getById?id=1 查询id为1的用户信息http://localhost/user/saveUser 保存用户信息
REST风格描述形式http://localhost/user/1http://localhost/user
传统方式一般是一个请求url对应一种操作,这样做不仅麻烦,也不安全,因为会程序的人读取了你的请求url地址,就大概知道该url实现的是一个什么样的操作。
查看REST风格的描述,你会发现请求地址变的简单了,并且光看请求URL并不是很能猜出来该URL的具体功能
所以REST的优点有:
隐藏资源的访问行为,无法通过地址得知对资源是何种操作
书写简化但是我们的问题也随之而来了,一个相同的url地址即可以是新增也可以是修改或者查询,那么到底我们该如何区分该请求到底是什么操作呢?
按照REST风格访问资源时使 ...
关于ArrayList和LinkedList
1. ArrayListArrayList内部是动态数组的结构,具备数组的特点优点:
1)查找速度快2)ArrayList可随着元素的增长而自动扩容,正常扩容的话,每次扩容到原来的1.5倍3)尾插速度快,时间复杂为O(N)
缺点:
1)头插、中间插入和删除操作时需要搬运数据,时间复杂度是O(N)2) 数组需要连续的内存空间,对空间要求高3) 数组是固定大小的,但是ArrayList 插入元素会触发扩容机制4) ArrayList的线程是不安全的。
1.1 常用方法1)add( element) 添加一个元素,2)add( index , element) 在index位置添加一个元素 index当前元素就会往后挪3)size() 顺序表长度4) set (index ,element) 将index位置元素进行修改5)get( index) 获取index位置的元素6)remove(index) 删除index 位置的元素7)contains (element) 是否包含该元素8)isEmpry() 判断顺序表是否为空
1.2 源码相关定义:// 默认的容量大小(常量)
pr ...
从上到下打印二叉树
题目:从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。例如:给定二叉树: [3,9,20,null,null,15,7],返回:
[3,9,20,15,7]提示:
节点总数 <= 1000
思路:二叉树的层次遍历可以看成是二叉树的广度优先搜索(BFS)BFS 通常借助 队列 的先入先出特性来实现。
算法流程:
特例处理: 当树的根节点为空,则直接返回空列表 [] ;
初始化: 打印结果列表 res = [] ,包含根节点的队列 queue = [root] ;
BFS 循环: 当队列 queue 为空时跳出;
出队: 队首元素出队,记为 node;
打印: 将 node.val 添加至列表 tmp 尾部;
添加子节点: 若 node 的左(右)子节点不为空,则将左(右)子节点加入队列 queue ; 返回值: 返回打印结果列表 res 即可。
题解:/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode ...
第一个只出现一次的字符
题目:在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。示例 1:
输入:s = “abaccdeff”输出:’b’
示例 2:
输入:s = “”输出:’ ‘
限制:
0 <= s 的长度 <= 50000
思路:对字符串进行两次遍历。
在第一次遍历时,我们使用哈希映射统计出字符串中每个字符出现的次数。在第二次遍历时,我们只要遍历到了一个只出现一次的字符,那么就返回该字符,否则在遍历结束后返回空格。
题解:class Solution {
public char firstUniqChar(String s) {
LinkedHashMap<Character,Integer> map = new LinkedHashMap();
for(char c:s.toCharArray())
{
map.put(c,map.getOrDefault(c,0)+1);
}
for(char c:map.ke ...
Java中二维数组的length方法
Java中二维数组的length方法今天遇到一道很简单的算法题
在一个 n * m 的二维数组中,每一行都按照从左到右 非递减 的顺序排序,每一列都按照从上到下 非递减 的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数
刚开始是这样写的
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
boolean flag = false;
for(int i = 0;i < matrix.length;i++)
{
for(int j = 0; j < matrix.length;j++)
{
if(target == matrix[i][j])
{
flag = true;
...