Oct1a

性能测试

简单测试:

# 测试:100个并发连接 100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

基础知识

redis默认有16个数据库

默认使用的第0个;

16个数据库为:DB 0~DB 15
默认使用DB 0

命令 描述
**keys *** 查看当前数据库中所有的key
flushdb 清空当前数据库中的键值对
flushall 清空所有数据库的键值对
select db_number 切换数据库
DBSIZE 当前数据库大小, size和key个数相关

Redis是单线程的,Redis是基于内存操作的。

所以Redis的性能瓶颈不是CPU,而是机器内存和网络带宽。

那么为什么Redis的速度如此快呢,性能这么高呢?QPS达到10W+

Redis为什么单线程还这么快?

  • 误区1:高性能的服务器一定是多线程的?
  • 误区2:多线程(CPU上下文会切换!)一定比单线程效率高!

核心:Redis是将所有的数据放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存存储数据情况下,单线程就是最佳的方案。

二、五大数据类型

Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。

Redis-key

在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。

命令 描述
exists key 判断键是否存在
del key 删除键值对
move key db 将键值对移动到指定数据库
expire key second 设置键值对的过期时间
ttl key 查看key的过期剩余时间*
type key 查看value的数据类型

关于TTL命令

Redis的key,通过TTL命令返回key的过期时间,一般来说有3种:

  1. 当前key没有设置过期时间,所以会返回-1.
  2. 当前key有设置过期时间,而且key已经过期,所以会返回-2.
  3. 当前key有设置过期时间,且key还没有过期,故会返回key的正常剩余时间.

关于重命名RENAMERENAMENX

  • RENAME key newkey修改 key 的名称
  • RENAMENX key newkey仅当 newkey 不存在时,将 key 改名为 newkey 。

String(字符串)

普通的set、get直接略过。

命令 描述
APPEND key value 向指定的key的value后追加字符串
DECR/INCR key 将指定key的value数值进行**+1/-1**(仅对于数字)
INCRBY/DECRBY key n 按指定的步长对数值进行加减
INCRBYFLOAT key n 为数值加上浮点型数值
STRLEN key 获取key保存值的字符串长度
GETRANGE key start end 按起止位置获取字符串(闭区间,起止位置都取)
SETRANGE key offset value 用指定的value 替换key中 offset开始的值
GETSET key value 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。如果不存在值,则返回nil
SETNX key value 仅当key不存在时进行set
SETEX key seconds value set 键值对并设置过期时间
MSET key1 value1 [key2 value2..] 批量set键值对
MSETNX key1 value1 [key2 value2..] 批量设置键值对,仅当参数中所有的key都不存在时执行
MGET key1 [key2..] 批量获取多个key保存的值
PSETEX key milliseconds value 和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,

String类似的使用场景:value除了是字符串还可以是数字,用途举例:

  • 计数器
  • 统计多单位的数量:uid:123666:follow 0
  • 粉丝数
  • 对象存储缓存

List(列表)

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

首先我们列表,可以经过规则定义将其变为队列、栈、双端队列等

命令 描述
LPUSH/RPUSH key value1[value2..] 从左边/右边向列表中PUSH值(一个或者多个)。
LRANGE key start end 获取list 起止元素==(索引从左往右 递增)==
LPUSHX/RPUSHX key value 向已存在的列名中push值(一个或者多个)
LINSERT key BEFORE|AFTER pivot value 在指定列表元素的前/后 插入value
LLEN key 查看列表长度
LINDEX key index 通过索引获取列表元素
LSET key index value 通过索引为元素设值
LPOP/RPOP key 从最左边/最右边移除值 并返回
RPOPLPUSH source destination 将列表的尾部(右)最后一个值弹出,并返回,然后加到另一个列表的头部
LTRIM key start end 通过下标截取指定范围内的列表
LREM key count value List中是允许value重复的 count > 0:从头部开始搜索 然后删除指定的value 至多删除count个 count < 0:从尾部开始搜索… count = 0:删除列表中所有的指定value。
BLPOP/BRPOP key1[key2] timout 移出并获取列表的第一个/最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
BRPOPLPUSH source destination timeout RPOPLPUSH功能相同,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
  • list实际上是一个链表,before Node after , left, right 都可以插入值
  • 如果key不存在,则创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有值,空链表,也代表不存在
  • 在两边插入或者改动值,效率最高!修改中间元素,效率相对较低

应用:

消息排队!消息队列(Lpush Rpop),栈(Lpush Lpop)

Set(集合)

Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。

Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

命令 描述
SADD key member1[member2..] 向集合中无序增加一个/多个成员
SCARD key 获取集合的成员数
SMEMBERS key 返回集合中所有的成员
SISMEMBER key member 查询member元素是否是集合的成员,结果是无序的
SRANDMEMBER key [count] 随机返回集合中count个成员,count缺省值为1
SPOP key [count] 随机移除并返回集合中count个成员,count缺省值为1
SMOVE source destination member 将source集合的成员member移动到destination集合
SREM key member1[member2..] 移除集合中一个/多个成员
SDIFF key1[key2..] 返回所有集合的差集 key1- key2 - …
SDIFFSTORE destination key1[key2..] 在SDIFF的基础上,将结果保存到集合中==(覆盖)==。不能保存到其他类型key噢!
SINTER key1 [key2..] 返回所有集合的交集
SINTERSTORE destination key1[key2..] 在SINTER的基础上,存储结果到集合中。覆盖
SUNION key1 [key2..] 返回所有集合的并集
SUNIONSTORE destination key1 [key2..] 在SUNION的基础上,存储结果到及和张。覆盖
SSCAN KEY [MATCH pattern] [COUNT count] 在大量数据环境下,使用此命令遍历集合中元素,每次遍历部分

Hash(哈希)

Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。

Set就是一种简化的Hash,只变动key,而value使用默认值填充。可以将一个Hash表作为一个对象进行存储,表中存放对象的信息。

命令 描述
HSET key field value 将哈希表 key 中的字段 field 的值设为 value 。重复设置同一个field会覆盖,返回0
HMSET key field1 value1 [field2 value2..] 同时将多个 field-value (域-值)对设置到哈希表 key 中。
HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值。
HEXISTS key field 查看哈希表 key 中,指定的字段是否存在。
HGET key field value 获取存储在哈希表中指定字段的值
HMGET key field1 [field2..] 获取所有给定字段的值
HGETALL key 获取在哈希表key 的所有字段和值
HKEYS key 获取哈希表key中所有的字段
HLEN key 获取哈希表中字段的数量
HVALS key 获取哈希表中所有值
HDEL key field1 [field2..] 删除哈希表key中一个/多个field字段
HINCRBY key field n 为哈希表 key 中的指定字段的整数值加上增量n,并返回增量后结果 一样只适用于整数型字段
HINCRBYFLOAT key field n 为哈希表 key 中的指定字段的浮点数值加上增量 n
HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对。

Hash变更的数据user name age,尤其是用户信息之类的,经常变动的信息!Hash更适合于对象的存储,Sring更加适合字符串存储!

Zset(有序集合)

不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。

score相同:按字典顺序排序

有序集合的成员是唯一的,但分数(score)却可以重复。

有效经纬度

  • 有效的经度从-180度到180度。
  • 有效的纬度从-85.05112878度到85.05112878度。
Vue网站数据传输加密

为了数据安全,防止被黑客模拟字段恶意发请求攻击,决定在前端,服务端,脚本上都对数据传输进行加解密

为了都能加解密,所需要的库也是要一样的,否则算法不一致就无法解密,目前 使用的是非对称密钥加密,库使用crypto

网站前端:

iv 偏移量使用当前时间戳进行截取前六位数,为了严谨是可以多截取几位,但不能太多,否则在59分到00分时,会出现偏移量不一致无法解密,然后再补齐至16位数

const crypto = require('crypto')
const iv = new Buffer(String(new Date().getTime()).slice(0, 6) + '0000000000') // 要16位才转buffer
const key = window.location.hostname

export function encryp(data) {
  if (!data || data.length <= 0) {
    return data
  }
  if (typeof data === 'object') {
    // 如果传入的data是json对象,先转义为json字符串
    try {
      data = JSON.stringify(data)
    } catch (error) {
      console.log('error:', error)
    }
  }
  const decodeKey = crypto.createHash('sha256').update(key, 'utf-8').digest()
  const cipher = crypto.createCipheriv('aes-256-cbc', decodeKey, iv)
  return cipher.update(data, 'utf8', 'base64') + cipher.final('base64')
}

export function decrypt(data) {
  const encodeKey = crypto.createHash('sha256').update(key, 'utf-8').digest()
  const cipher = crypto.createDecipheriv('aes-256-cbc', encodeKey, iv)
  return cipher.update(data, 'base64', 'utf8') + cipher.final('utf8')
}

工具库写完后,需要在全局发送请求与接收请求上对数据进行加解密

import { encryp, decrypt } from '@/utils/crypto'
service.interceptors.request.use(
  (config) => {
    if (store.getters.token) {
      // 头部增加token验证
      config.headers['X-Token'] = getToken()
    }
    if (config.data !== undefined || config.data) {
      // 设置请求头为 表单提交头
      config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
      // 设置提交数据为form表单格式
      const formData = new FormData()
      const encrypts = encryp(config.data)
      formData.append('p', encrypts) // 对请求参数数据进行加密
      config.data = formData
    }
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

service.interceptors.response.use(
  (response) => {
    let res = response.data
    // 由于站内有些请求是第三方网站的,所以这边加了字段判断,
    // 也可以根据response.config的网站进行判断是否属于自身网站请求
    if (res.b) {
      // 对返回数据进行解密
      res = JSON.parse(decrypt(response.data.b))
    }
  },
  (error) => {
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

网站后端:

Unsupported state or unable to authenticate data

node:internal/crypto/cipher:193
  const ret = this[kHandle].final();
                            ^

Error: Unsupported state or unable to authenticate data

image-20220210170829291

因解密的密钥错误,重新申请一个即可

服务器反向代理

Nginx的配置还是比较简单的,如:

location ~ /* ``{``proxy_pass http://127.0.0.1:8008;``}

或者可以

location /``{``proxy_pass http://127.0.0.1:8008;``}

Apache2的反向代理的配置是:

ProxyPass /ysz/ http://localhost:8080/

然而,如果要配置一个相对复杂的反向代理
Nginx相对Apache2就要麻烦一些了
比如,将url中以/wap/开头的请求转发到后台对应的某台server上
可以再Nginx里设置一个变量,来临时保存/wap/后面的路径信息

node 创建触发器出现check the manual that corressponds to yours Mysql server version for the right syntax to use near ‘DELIMITER //‘


code: 'ER_PARSE_ERROR',
  errno: 1064,
  sqlMessage: "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER //\r\n" +
    "CREATE TRIGGER `device_group_before_insert` BEFORE INSERT ON `devi' at line 1",
  sqlState: '42000',
  index: 73,

原本想通过node来动态创建数据库,因为数据库中有触发器,导致出现触发器无法创建的错误

image-20211223174603459

解决方法:

DELIMITER // 替换为空即可

按自己理解的话,可能是因为DELITMITER是关键字,mysql库在解析的时候出现错误了,直接删除该句是不影响创建的

U盘无法传输单个大文件,无需格式化解决

U盘无法传输单个大文件,无需格式化解决

因需要U盘传输一个大文件,但文件太大,出现无法传输的情况,因为U盘默认FAT32格式最大只能传输4G以内大小的单文件,超过系统就会弹出窗口提示“对于目标文件系统,文件XXX过大”,但为何电脑硬盘可以传输呢, 因为电脑硬盘默认格式是用NTFS,支持大文件,所以我们只需更改u盘文件格式转为NTFS即可。

这篇只介绍不格式化U盘达到转换格式方法。

1. 检查盘符并修复

直接运行更改格式指令,出现卷的文件错误,所以建议先运行检查并修复命令,再进行第二步操作

打开cmd,运行:CHKDSK F: /R

F为自身盘符名称,自行替换

2.格式转换

输入covert f: /fs:ntfs /x

covert f 的f为盘符,自行替换,注意中间有空格,运行后我们可以看到整个处理过程,等待结束就可以了

Linux利用iptables屏蔽某些域名

一般 iptables 自带的都有 string 模块,这个模块的作用就是匹配字符串,匹配到泛域名的URL,然后就把数据包丢弃,就实现了屏蔽泛域名的功能。比如可以限制SS访问youtube.com