常见限流算法
常见限流算法限流算法主要是为了防止系统过载或者应对突发流量,是一种用于控制数据流速度的技术。主要包括下面四种方法:
固定窗口限流算法
滑动窗口限流算法
漏桶限流算法
令牌桶限流算法
滑动窗口限流算法有时候用户的一些无意义或者非法操作,例如频繁的发送短信、频繁的修改个人信息等操作就是无意义或者非法的,因此我们要针对这些操作进行限流。
限流的主要核心思路就是使用redis的zset结合滑动窗口限流算法,
针对这些行为,我设计了一个通用的接口,思路上是使用时间窗口限流算法,具体实现我使用zset进行的。
比如用户五分钟内只能发送三条验证码,于是就将用户发送短信的行为设计为redis的key,格式为:
场景:行为:用户唯一标识,zset的score分数值为时间戳,value值也是时间戳。
具体的流程为:
当用户每次发生这样的限流行为,我就会在Redis中进行记录,
在业务处理中,使用Redis的api进行查询,本质上就是调用了Redis的zcount命令去统计,传入开始score值和结束score值,我们以当前时间戳作为结束分值,然后使用当前时间去减去限流时间,例如五分钟,求出五分钟 ...
HashMap底层源码分析
HashMap底层源码分析HashMap主要是用来存放键值对的,它基于哈希表的Map接口实现,是常用的Java集合之一,是非线程安全的。
HashMap可以存放null的Key和value,但是null作为键只能有一个,作为value可以有多个
方法名称
说明
V put(K key, V value)
添加元素
V remove(Object key)
根据键删除键值对元素
void clear()
移除所有的键值对元素
boolean containsKey(Object key)
判断集合是否包含指定的键
boolean containsValue(Object value)
判断集合是否包含指定的值
boolean isEmpty()
判断集合是否为空
int size()
集合的长度,也就是集合中键值对的个数
HashMap结构HashMap内部方法图标为m表示method
HashMap内部类图标为c表示class
HashMap内部属性图标为f表示field
1static class Node<K,V> imp ...
MySQL索引
MySQL索引什么是索引?索引是一种特殊的数据结构,由数据表中的一列或者多列组成,可以用来快速查询数据库中的某一特定值的记录。
索引的类型有哪些?按照数据结构的维度进行划分:
BTree索引
哈希索引
RTree索引
全文索引
对文本的内容进行分词,进行索引。目前只有CHAR、VARCHAR、TEXT列上可以创建全文索引。一般不会使用,效率较低,通常使用搜索引擎如ElasticSearch代替。
按照应用的维度划分:
单列索引
联合索引
多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
主键索引
加速查询+列值唯一(不可以有NULL)
普通索引
仅加速查询
唯一索引
加速查询+列值唯一(可以有NULL)
覆盖索引
一个索引包含或者说覆盖所有需要查询的字段值
全文索引
联合索引:
使用表中的多个字段创建索引,就是联合索引,也叫组合索引或者复合索引。
以score和name两个字段建立联合索引。
1ALTER TABLE `cus_order` ADD INDEX id_score_name(score, name);
最左匹配原则 ...
JWT技术详解
常见的跨域认证方式:
Session-Cookies
Toekn验证(包括jwt、SSO)
OAuth2.0(开放授权)
Session-Cookies实现方式:
用户通过POST请求向服务器发送账号密码
服务器验证通过之后,在当前对话session中保存相应的数据,如用户角色、登录时间等
服务器向用户返回一个session_id,写入用户的cookies
用户随后的每一次请求,都会通过cookies,将session_id,传给服务器
服务器收到session_id,找到前期保存的数据,由此知道用户的身份。
这个流程在单机的时候没有问题,但是当服务器变为集群的时候,或者跨域的服务器的时候,Session就必须进行数据共享。
Cookies存在问题,容易被csrf攻击,解决攻击的方法:
提交Form表单时,添加本域才能获取验证消息
CSRF-Token
防止不明外部域名的访问
同源检测
Samesite Cookies
考虑每台服务器如何实现对Session的共享?
方式一:实现Session数据的持久化。当各种服务收到请求后,都向数据持久层请求数据,来验证 ...
Redis学习笔记
Redis基础NoSQL
认识redisredis
Remote Dictionary Server 远程词典服务器
特征:
键值型,value支持多种不同数据结构
单线程,每个命令具有原子性
低延迟,速度快(基于内存、IO多路复用、良好的编码)
支持数据的持久化(定期将数据持久化到硬盘中)
支持主从集群、分片集群
支持多语言客户端
redis6.0开始的时候是网络请求部分多线程,其余的依旧是单线程
Redis命令redis-cli -a 这个命令会调出控制台
redis-cli 进入到控制台的话,可以进入控制台,但是需要注意的是,如果设置了密码此时会提示你没有进行任何授权,需要进行授权
auth 123321
如果没有用户名直接auth + 密码即可
redis 常见命令
Redis常见数据结构
基本数据类型
String类型
Hash类型
List类型
Set类型
SortedSet类型(ZSet)
特殊类型
Redis通用命令通用命令是不分数据类型的,都可以使用的命令,常见的有:
KEYS:查看符合模板的所有key,不建议在生产环境中使用, ...
Java学习笔记
Java基础(黑马)提示忽略大小写
运算符字符串和字符的加操作字符+数字或者字符+数字时,会把字符通过ASCII码表查询到对应的数字再进行计算。
逻辑运算符短路运算符例如在登录程序中,判断用户名**&&**密码
1、用户名正确,需要判断密码
2、用户名错误,无需判断密码
数组
数组的定义
数组的静态初始化完整格式
数据类型[] 数组名 = new 数据类型[] {元素1, 元素2 …}
简化格式
数据类型[] 数组名 = {1,2,3,4,5}
12int[] arr = new int[] {1,2,3,4,5};int[] arr = {1,2,3,4,5};
idea中打印地址值
[I@1b6d3586
[ 表示当前是一个数组
I 表示当前数组中的元素都是int类型的
@ 表示一个间隔符号(固定格式)
1b6d3586 这部分才是数组真正的地址值(十六进制)
arr.fori 可以在idea中快速的遍历数组
数组的动态初始化数据类型[] 数组名 = new 数据类型[数组长度]
1int[] a ...
LRU算法
LRU算法
原理LRU(Leasted Recently Used)缓存机制可以通过哈希表和辅助双向链表实现
双向链表按照被使用的顺序存储了键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
哈希表存储数据的键映射到其在双向链表中的位置。
逻辑:
针对get操作:
先判断哈希表cache中是否存在该key
如果不存在则直接返回-1
如果存在,则将通过哈希表找到对应的结点并将该结点删除(removeNode)并在开头添加一个结点((addToHead),其中删除和添加操作只需要修改指针,因此时间复杂度为O(1)满足题目要求
针对put操作:
判断哈希表cache中是否存在该key
如果不存在该key则
先以该key、value为键值对构建一个双向链表node
将该结点node添加到链表的开头(addToHead)
然后在哈希表中添加键key和值node
将self.size+1,并判断当前size大小是否超过capacity
如果没有超过则不进行任何操作
如果超过了,则需要进行淘汰,将双线链表的末尾的结点删除并将其对应的key从哈希表中删除
如果存 ...
哈希表
哈希表用途:用于快速判断一个元素是否出现在集合里
可以做到O(1)的时间复杂度
哈希函数哈希碰撞将两个元素都映射到索引相同的位置,这一现象叫做哈希碰撞。
解决办法
拉链法
线性探测法
练习题242、有效的字母异位词
Python便捷解法:
1234class Solution: def isAnagram(self, s: str, t: str) -> bool: from collections import Counter return Counter(s)==Counter(t)
两个数组的交集12345class Solution: def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: set1 = set(nums1) set2 = set(nums2) return list(set1&set2)
202、快乐数1234567891011121314151617c ...
链表相关知识
链表链表的定义链表是一种通过指针串联在一起的数据结构,每个节点由两个部分组成,一个是数据域,一个是指针域(存放指向下一个地址的指针),最后一个节点的指针域为空NULL。
链表的入口节点被称为链表的头结点,即head
首先定义单个结点的结构
12345class ListNode: def __init__(self, val): self.val = val self.next = None
单个结点中,包含了一个结点值和下一个结点的指针域
整体代码如下:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051class ListNode: def __init__(self, val): self.val = val self.next = Noneclass MyLinkedList: def __init__(self): self.size = 0 ...
LinkedList底层源码分析
LinkedList底层源码分析LinkedList集合底层数据结构是双链表,查询慢,增删快,但如果操作的是首尾元素,速度也是极快的。
特有的API
底层原理LinkedList底层是双向链表结构
节点对象Node的定义如下
核心步骤如下:
刚开始创建的时候,底层创建了两个变量:一个记录头结点first,一个记录尾结点last,默认都为null
添加第一个元素时候,底层创建一个结点对象,first和last都记录这个结点的地址值
添加第二个元素时候,底层创建一个结点对象,第一个结点会记录第二个结点的地址值,last会记录新结点的地址值。
源代码流程: