分享一个作者维护多年的导航网站,叫做发现导航
,从2018年维护至今,是目前全球开源且持续维护的项目,目前拥有1000+使用者。 Github项目, 2.1KStar+
三不需:无需数据库
、无需服务器
、无需成本
发现导航
的理念就是做一款无需依赖后端服务既简单又方便,没有繁杂的配置和数据库等配置概念, 做到开箱即用。
800+
优质网站1、打开 https://github.com/xjh22222228/nav 点击右上角Fork到自己仓库
2、确认
3、创建 Token 后续访问后台需要, 打开 https://github.com/settings/tokens/new
输入 TOKEN,过期时间选为不过期, 权限全部勾选,TOKEN 只要不泄露是安全的。
复制并保存TOKEN,如果这一步不保存就查看不了
4、打开 https://github.com/你的用户名/nav/settings/secrets/actions/new 把链接的用户名改成你的Github
Name填写 TOKEN
大写, Secret 填写刚刚保存的 TOKEN
5、编辑根目录 nav.config.ts
文件,只需要修改仓库地址,把ID改成Github ID
6、这个时候Github Action 会自动启动
7、完成!坐等2分钟,打开链接 https://你的用户名.github.io/nav
项目包含后台,不需要额外部署,只需要将链接后面增加 system
=> https://nav3.cn/system
如果你的应用服务启用了history路由模式,服务需要重定向到 index.html
netlify 默认不启用,启用需要在 public
目录新建 _redirects
文件内容:
/* /index.html 200
netlify.toml
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
参考:https://docs.netlify.com/routing/redirects/rewrites-proxies/#history-pushstate-and-single-page-apps
找了下目前小程序没有跨端小程序生成二维码的库, 花了点时间整合了一下
小程序生成二维码支持微信、支付宝 等小程序使用。
具体使用移步到仓库:
Fluence 给大约1W个Github用户投放,领取条件是 Github 用户在去年给 WEB3
项目提交过贡献即可领取,如果不知道是否有领取资格打开链接 https://claim.fluence.network 输入你的Github 用户名检查是否有资格。
如果检查到你有资格你没有SSH密钥也是不行的,Fluence是通过你本地的SSH 和Github上SSH公钥匹配才能领取,如果不放心领取完把Github上的SSH公钥删掉, 我就因为换了电脑没密钥差点丢失了几万块钱,还好电脑给别人用了拿回密钥。
Fluence 是什么? Fluence 是一个新的项目,目前还没上交易所, FLT币。
5000FLT币价值多少?大约2.5万到3.6万RMB,有波动,上个月前还是1U,现在0.8572, 可以在这个网站查询 https://www.bybit.com/en/trade/spot/FLT/USDT
领取后锁仓2个月,2个月后我在更新文章提现成人民币。
领取过程比较复杂,我把领取的过程图片贴上去了:
Starknet 供应计划领取111.1个币,$250左右,可换人民币大概2000左右,不要白不要啊。
具体详情看这里 https://www.starknet.io/en/content/starknet-provisions-program
1、在 2023 年 11 月 15 日之前,您对属于全球前 5,000 个存储库(按星数排名)之一的存储库至少进行了 3 次提交。其中至少有一项提交发生在 2018 年或之后。
如果您是开源贡献者这完全不是问题。
1、打开 https://provisions.starknet.io/#widget
2、按照下图查看是否有资格
如果有资格,注册钱包并绑定即可领取。
如果领到币的还不知道怎么卖出的可以联系我。
可以对选中的 DOM 节点进行截图:
或者使用快捷键 shift+command+p
然后输入 screenshot
进行全屏截图。
这是浏览器控制台内置的方法,主要用来剪贴内容。 比如说我们要把 localStorage
里的数据都拷贝出来,如果一个一个key/value 考出来就很费劲。 copy 就可以很好的解决这个问题。
在控制台调试时需要对某个元素进行操作时一般会使用 document.querySelector(element)
,这种方式不仅长,还得知道元素选择器。
$0
则可以获取选中的目标元素。
切换隐藏选中元素,实际上 Chrome 添加了一个类, visibility: hidden !important
。
这2个方法实际上是 document.querySelector
/ document.querySelectorAll
简写。
在一些场景定位BUG时想要对某个请求重新发起,如果不知道这个技巧,一般都是刷新页面。
选中需要重新发起请求栈右键:
这个功能我用得也挺多的, 主要是移动端调试直接扫一扫即可,而不需复制链接。
获取最后一次执行结果,减少了赋值操作很方便。
在大部分时候初始化变量我们会这么做:
var n int
// 或者
n := 0
上面的代码实际上是等价的,没有问题, 但是在切片就不同了,看下面例子:
var sliceN1 []int // 结果为 nil
sliceN2 := []int{} // 结果为 []
这TM是一个坑啊,官方是这么解释的:
When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, “” for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.
官方文档指出切片在没有初始化的情况下切换的零值是 nil
, 第一种是未初始化,第二种是初始化为空,所以这2种声明都是不等价的,也就是 nil
!= slice
。 在开发过程中需要注意。
还有一种区别就是在 json
转换时 nil
会被当成 null
, 而另外一种是被当初 []
空数组。 在WEB开发中完全是坑。
var n []string
r, _ := json.Marshal(n)
fmt.Println(string(r)) // "null"
n := []string{}
r, _ := json.Marshal(n)
fmt.Println(string(r)) // "[]"
如果我们使用第二种初始化,就会被 Goland
认为是一种冗余的写法,会让你转换为第一种 var
, 这样就又出刚刚说的问题了。
有人说这是 Goland
的 Bug, Goland 团队认为 nil切片在功能上等同于零长度切片,即使它不指向任何内容
。
解决办法是使用 make
var n []int = make([]int, 0) // 全局
sliceN := make([]int, 0) // 函数内
我的建议就是在声明切片时都使用 make
来初始化,防止入坑。
go1.16 出了 go:embed
指令, 了解后发现很强!!!
go:embed
是一个指令,用来嵌入静态文件到二进制文件当中。
我们在部署时会打包成一个二进制文件,如果应用包含一些配置文件比如 config.ini
/ index.html
config.yaml
等一系列静态文件就比较麻烦了,在以往的做法就是将这些静态文件放到二进制同一个目录里头,通过文件系统进行读取, 或者是写在 .go
文件里, 就很不方便。
现在可以通过 go:embed
指令将这些静态文件打包在二进制文件里,也就是只有一个二进制文件。
在使用 go:embed
指令之前先了解一下有哪些限制。
go:embed
指令跟着单个文件,只支持 string
/ []byte
/ embed.FS
go:embed
指令跟着多个文件或文件夹,只支持 embed.FS
go:embed
指令下一行必须紧跟着嵌入变量string
/ []byte
/ embed.FS
, 其他类型包括别名都不支持.
/ ..
, 当前路径使用 *
2.txt 文件内容:
hello golang
package main
import (
_ "embed"
"fmt"
)
//go:embed 2.txt
var s string
func main() {
fmt.Println(s)
// hello golang
}
注意 //
注释后面不能有空格
字节切片类型:
package main
import (
_ "embed"
"fmt"
)
//go:embed 2.txt
var b []byte
func main() {
fmt.Println(b)
// [104 101 108 108 111 32 103 111 108 97 110 103]
}
package main
import (
"embed"
"fmt"
)
//go:embed 2.txt
var f embed.FS
func main() {
s, _ := f.ReadFile("2.txt")
fmt.Println(string(s))
// hello golang
}
当嵌入多个文件只能使用 embed.FS
类型,不支持其他类型。
package main
import (
"embed"
"fmt"
)
//go:embed 1.txt 2.txt
var f embed.FS
func main() {
s1, _ := f.ReadFile("1.txt")
s2, _ := f.ReadFile("2.txt")
fmt.Println(string(s1))
fmt.Println(string(s2))
}
或者指令分开多行写也可以
package main
import (
"embed"
"fmt"
)
//go:embed 1.txt
//go:embed 2.txt
var f embed.FS
func main() {
s1, _ := f.ReadFile("1.txt")
s2, _ := f.ReadFile("2.txt")
fmt.Println(string(s1))
fmt.Println(string(s2))
}
FS 目前提供了3个API:
这3个都是只读的,没有可写的接口, 也就是不存在竞态问题,可以并发读取。
embed.FS 实现了 1.16 io/fs.FS
新的接口:
package main
import (
"embed"
"fmt"
"io/fs"
)
//go:embed 1.txt
var f embed.FS
func main() {
content, _ := fs.ReadFile(f, "1.txt")
fmt.Println(string(content))
}
采用 /
斜杠来访问目录下的文件,windows
也一样。
当嵌入的是文件夹时除了 .
和 _
开头的文件和文件夹都会被嵌入即使在程序中没有使用。
package main
import (
"embed"
"fmt"
"io/fs"
)
//go:embed test
var f embed.FS
func main() {
content, _ := fs.ReadFile(f, "test/1.txt")
fmt.Println(string(content))
// hello golang
}
快速获取你的电脑WIFI密码,并在需要时生成WiFi的QR码,以便手机轻松连接。
Mac:
curl -fsSL https://raw.sevencdn.com/xjh22222228/wifi/main/install.sh | bash
Windows:
./wifi.exe
mac
# 获取WIFI密码
$ wifi
# 打印二维码
$ wifi -q
# 生成二维码图片
$ wifi -i
# 获取 SSID
$ wifi -s
windows 一样
./wifi.exe
./wifi.exe -q
如果你用的 go版本 >= 1.13 GOMODULE 默认会启用,低于此版本的话升级吧。
如果项目需要 go module 就必须初始化一次。
go mod init 模块名
如果是开源项目放在 github 上:
# 注意不要带协议
go mod init github.com/xjh22222228/gosh
生成后会在项目下生成 go.mod
文件, 大概长这样
module github.com/xjh22222228/gosh
go 1.15
如果开了 go module 下载依赖会跟以往不太一样
go get ./...
更新项目下所有依赖最新版本
go get -u
也可以指定要更新的包
go get -u github.com/xjh22222228/gosh
删除用不到的依赖包和下载用到的依赖包。
go mod tidy -v
在项目下生成 vendor 目录,也就是依赖包。
go mod vendor
$ go mod verify
all modules verified
用Go写的一个检查Python依赖包最新版本命令行工具
全称 python-check-updates
简称 pcu
。
主要原因是 Go 非常适合写工具类,因为不需要安装环境即可运行,加上更快,体积也小。
项目开源戳 https://github.com/xjh22222228/python-check-updates
这个项目只是用来玩的,直接用 pip 更好。
Find the latest version of your requirements.txt current dependency package.
Deno ships as a single executable with no dependencies. You can install it using the installers below, or download a release binary from the releases page.
Shell (Mac, Linux):
curl -fsSL https://raw.githubusercontent.com/xjh22222228/python-check-updates/main/install.sh | bash
Show any new dependencies for the project in the current directory:
$ pcu
Checking /opt/requirements_test.txt
13 / 13 [--------------------------] 100.00% 1 p/s
pytest-cov → 2.10.1
pytest-mock ==2.0.0 → 3.3.1
httpbin ==0.7.0 → 0.7.0
pytest-httpbin <1.0 → 1.0.0
MySQL-python ==1.2.5 → 1.2.5
aliyun-python-sdk-cdn ==2.3.1 → 3.5.8
XlsxWriter ==0.9.3 → 1.3.7
aliyun-python-sdk-core ==2.3.2 → 2.13.29
wheel → 0.35.1
service-identity ==16.0.0 → 18.1.0
pytest >=2.8.0,<=3.10.1 → 6.1.2
zope.interface ==4.3.2 → 5.2.0
Done in 1 s.
-f, --file Specify the file name of the check dependency
package, default 'requirements.txt'
-v, --version output the version number
-c, --check Check the latest version
-h, --help display help for command
$ pcu -f=requirements_test.txt
$ pcu -v
基于Flutter。
由于笔者不是专业的APP开发,在这方面较弱,在接入微信登录遇到不少坑,基本都是配置问题,在这里记录下过程。
IOS接入微信登录必须具备以下几点(缺一不可):
1、新建一个文件名叫 apple-app-site-association
没有任何后缀, 并写入以下配置
{
"applinks": {
"apps": [],
"details": [
{
"appID": "1UPF2ET6AP.com.example.app.scp",
"paths": [ "/help/*" ]
}
]
}
}
需要修改 appID
和 paths
。
2、将文件放在域名根目录下。
打开你的域名比如 https://www.example.com/apple-app-site-association
如果文件被下载说明这步没问题。
3、使用 safari 浏览器打开 https://www.example.com/help/123
前面修改为你的域名,help 是前面在 paths 指定的, 123是乱写的随机字符串,打开后如果出现类似下面说明正常:
4、打开微信开放平台,找到IOS配置,下面必须全部都要配置
5、打开APPLE开发者中心,找到 Associated Domains
这个选项将它开启。
XCODE
打开 Runner.xcodeproj
文件。如图配置 Domains, applinks 前缀是固定的,后面就前面配置的 Universal Links
这个干嘛用的呢,主要是添加微信的 scheme 到白名单上去,这一步少不了,如果不添加没有办法知道微信是否已安装(尽管已安装了)。
找到 info.plist
文件, 将下面这一块内容添加到 dict
节点的末位,如下:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>wechat</string>
<string>weixin</string>
<string>weixinULAPI</string>
</array>
如果不知道这个文件在哪里,直接在当前工程下搜索这个文件, 一定有。
接入这微信登录费了不少心思,少一步就要找各种问题进行排除,太蛋疼,还好终于搞定了,祝后面的小老弟们一切顺利哈。
很多情况需要在 宿主 运行容器内的命令, 比如需要在宿主运行容器 nginx / redis / 等命令
有2种方法可以执行容器命令:
1、用内联重定向 EOF
docker exec -it c6261194df8e /bin/bash << EOF
touch /home/inner.txt
EOF
2、docker 参数-c 可以执行容器shell
docker exec -it c6261194df8e /bin/bash -c "touch /home/inner.txt"
用上面2种方法会发现虽然执行了,但是并没有在容器内创建文件,不信可以试试。
目前我还没知道什么问题。
既然无法在宿主内执行容器内的Shell,那是不是可以执行容器Shell脚本?
写 Shell 脚本得有编辑器,容器默认不带 vim
,所以得先安装:
apt-get update && apt-get install vim
Shell 脚本, 这里是Demo,实际参照自己需要执行的Shell。
ci.sh 内容如下:
#!/bin/bash
echo "hello" >> /home/inner.txt
来试试
docker exec -it c6261194df8e /bin/bash -c "/bin/bash /opt/ci.sh"
然后进入容器会发现 /home 目录下确实有 inner.txt 文件,说明这思路是可以的。
对了,执行容器内的外建命令记得用绝对路径执行,例如:
# 必须
/bin/nginx -s reload
# 这样不行的,因为有些外建命令没有加入环境变量
nginx -s reload
在我们编写程序时是离不开日志的,在Go标准库中提供了一个 log
模块。
比较常用的方法有下面这几个:
ln
结尾的只是多了个换行符 \n
, f
结尾的是格式化文本。
很多新手不知道这些方法和 fmt
包中的方法有什么区别。
实际上上面那3个都只是 fmt
包中的语法糖
抛出异常信息并终止程序。
示例代码:
package main
import (
"log"
)
func main() {
log.Panic("error")
}
底层实现是这样子的:
可以看到这其实只是一个语法糖而已
func Panic(v ...interface{}) {
s := fmt.Sprint(v...)
std.Output(2, s)
panic(s)
}
如果说能不能用全局方法 panic
呢? 不行, 因为panic 只接受一个参数:
panic("error")
所以为什么会有 log.Panic
这个方法了吧。
Fatal
和 Panic
都是用来打印信息然后终止程序,不知道何时用哪个?
Panic
: 抛出异常终止程序,一般来说在调试时想获得更多错误信息那就使用 PanicFatal
: 这其实只是打印信息后正常退出程序不会提供更多额外信息给你所以在程序中使用 Fatal
是没什么意义的,所以不推荐使用。
创建日志文件的好处在于可以持久化,在出现问题的时候可以快速定位。
创建日志文件需要借助 os
模块和 log
模块。
示例代码,以注释说明:
package main
import (
"log"
"os"
)
func main() {
// 打开一个文件
// 第一个参数是写入日志文件的位置
// 第二个参数是需要以什么权限进行操作,多个mode用 | 符号分隔代表多个
// 最后参数是权限的八进制表示法
file, err := os.OpenFile("./log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0)
if err != nil {
log.Panicln("文件打开错误:", err)
}
defer file.Close()
// 用 New 方法实例化
// 第一个参数是 io.Writer 接口,只要实现了该接口都支持,文件就是
// 第二个参数是 前缀信息 一般用于区别日志是属哪个分类
// 最后一个参数是 标志, 也就是系统额外附加信息 LstdFlags 包含了日志和时间
ilog := log.New(file, "Error:", log.LstdFlags)
ilog.Println("输出错误信息")
}
标志有以下(都是以 L 开头):
参数 | 描述 |
---|---|
Ldate | 本地时区日期 2020/11/11 |
Ltime | 本地时区时间 01:23:23 |
Lmicroseconds | 微妙 01:23:23.123123 |
Llongfile | 打印完整文件路径和行号 |
Lshortfile | 打印文件名和行号 |
LUTC | 如果设置了日期或时间,请使用UTC而不是本地时区 |
LstdFlags | Ldate和Ltime的结合,日期和时间一起打印 |
Lmsgprefix | 将Flag信息移至消息前面 |
golang打印日志行号有时非常有用,因为默认 fmt
是不打印行号的,给调试带来不便。
有了上面的Flag信息就好实现了, 借助 Llongfile
即可。
package main
import (
"log"
"os"
)
func main() {
ilog := log.New(os.Stdout, "Info:", log.Llongfile)
ilog.Println("Hello")
// 输出:Info:/Users/main.go:10: Hello
}
目前比较流行的是这两个日志系统,可以去了解下
每次新建项目都会遇到这个提示,明明安装依赖包了, 能用,就是爆红。
解决办法是在 Goland 设置里将 Go Modules 开启即可。
等Vue3很长时间了,一把梭。
Vue3 提供了2种开发模式,Composition API 和 Vue2的导出模式, 以供Vue2.0快速切入。
VueRouter 和 Vuex 没有太大的变化,详细参考文档。
在学习Redis时整理的一些命令,方便日后快速查找。
Redis 命令不区分大小写, 即 SET name xjh
等价于 set name xjh
。
命令: SET key value
SET name xiejiahe
命令: MSET key value key value ...
# 返回的是一个数组
MSET name xiejiahe age 18
命令:GET key
GET name
命令:GET key key...
GET name age
命令:STRLEN key
# 返回 3
STRLEN name
命令:APPEND key value
,如果key不存在则创建,等价于 SET key value
# xjh + _hi = xjh_hi
APPEND name _hi
列表类似于编程当中的数组。
向左(头部)添加一个或多个元素,返回列表长度, 命令: LPUSH key value value...
LPUSH names xjh 1 2 3
向右(尾部)添加一个或多个元素,返回列表长度, 命令: RPUSH key value value...
RPUSH names xjh 1 2 3
提取列表指定范围元素,支持负数,-1最后一个,-2倒数第二个, 命令: LRANGE key index index
# 提取所有元素
LRANGE names 0 -1
向左弹出一个元素, 返回弹出的元素,如果没有元素返回 null, 命令: LPOP key
LPOP names
向右弹出一个元素,返回弹出的元素,如果没有元素返回 null, 命令: RPOP key
RPOP names
上限列表,只保留前面指定N个元素,其他元素移除掉, 有点类似于JS splice, 命令: LTRIM key start end
LTRIM names 0 2
与 LPOP
有点类似,等待 Key 列表元素,如果超时了仍然没有元素可弹出返回nil, 命令: BLPOP key seconds
接收2个参数,第一个是 Key, 第二个是超时单位秒,如果为0表示不超时,永久阻塞直到有新元素。
# 左边
BLPOP names 5
BLPOP name age 5 # 支持多个key
# 右边
BRPOP names 5
获取列表元素个数, 如果不存在返回 0, 命令: LLEN key
LLEN names
删除数组指定元素, 如果是正数从左边开始删N个元素的值,如果是负数则从右边删N个元素的值,如果是0删除所有指定元素, 命令: LREM key index value
LREM names 2 zhangsan
获取指定索引元素值, 索引从0开始, 命令: LINDEX key index
# xiejiahe
LINDEX names 0
设置指定索引元素值, 索引从0开始,如果索引不存在则报错, 命令: LSET key index value
LSET names 1 xiejiahe
Redis 哈希/散列相当于编程语言当中的 Map
设置一个字段, 如果字段存在则覆盖,命令:HSET key 键 值
HSET user:001 username xiejiahe
设置多个字段, 如果字段存在则覆盖,命令:HMSET key 键 值 键 值...
HMSET user:001 username xiejiahe age 18
读取字段的值, 命令:HGET key 键
HGET user:001 username
读取多个字段的值,命令:HMGET key 键 键...
HMGET user:001 username age
获取所有字段键和值, 返回的是列表 [键, 值, 键, 值], 单数是键,偶数是值。命令:HGETALL key
# name
# 谢家和
# age
# 18
HGETALL user:001
获取字段数量, 命令:HLEN key
# 10
HLEN user:001
删除字段一个或多个键, 命令:HDEL key 键...
# 删除 name 和 age
HDEL user:001 name age
获取所有字段键名, 返回列表, 命令:HKEYS key
# names
# age
HKEYS user:001
获取所有字段值, 返回列表, 命令:HVALS key
# xiejiahe
# 18
HVALS user:001
自增是原子的,如果字段不存在则创建为0然后增加指定数值, 命令:HINCRBY key 键 number
# 首次创建 like 字段不存在则为 0 + 2 = 2
HINCRBY user:001 like 2
检查字段是否存在, 存在返回 1
, 否则返回 0
, 命令:HEXISTS key 键
# 返回 1
HEXISTS user:001 name
字段不存在创建, 如果字段存在则跳过, 命令:HSETNX key 键 值
# 跟 HSET 命令一致,区别于如果字段存在了则不创建
HSETNX user:001 name 谢家和
Redis 集合是无序的字符串集合且唯一, 每次调用时随意顺序返回元素。
添加一个或多个元素,命令: SADD key value value...
SADD users 1 2 3
SADD users 1 2 3 5 # 因为是唯一的,所以只有5会被添加进去
删除一个或多个元素,命令: SREM key value value...
SREM users 1 2 3...
获取集合所有元素, 命令: SMEMBERS key
# 1
# 2
# 3
# 5
SMEMBERS users
检查指定元素是否存在, 返回 1
成功,0
失败, 命令: SISMEMBER key value
# 返回 1
SISMEMBER users xiejiahe
获取集合元素个数, 命令: SCARD key
# 返回 10
SCARD names
随机获得集合中的元素, 命令: SRANDMEMBER key [number]
# 不指定数量,随机获取一个元素
SRANDMEMBER names
# 指定随机获取 10 个元素
SRANDMEMBER names 10
从集合中弹出一个元素, 因为集合是无序的,所以会随机弹出一个元素, 命令: SPOP key
# 随机弹出一个元素
SPOP names
有序集合类型为集合中的每个元素都关联了一个分数(score)。
添加1个或多个元素, value
在集合不能重复,否则被覆盖,score
可以正负数也可以浮点数, 命令: ZADD key score value...
# 添加多个元素
ZADD score 99 xjh 88 zhangsan
# 添加一个元素, score 为浮点数
ZADD score 100.1 xjh
根据 value
修改 score
, 命令: ZADD key score value
# 将 xjh 分数修改为 100
ZADD score 100 xjh
根据 value
获得元素的 score
值, 命令: ZSCORE key value
# 返回 100
ZSCORE score xjh
通过索引获取元素范围, 类似 LRANGE
命令,支持负数, 命令: ZRANGE key start end <WITHSCORES>
# xjh
# zhangsan
ZRANGE score 0 -1
# xjh
# 100
# zhangsan
# 99
ZRANGE score 0 -1 WITHSCORES # 可选参数 WITHSCORES 返回包含 value 和 score
根据分数 score
范围获取元素, 可选标识符 (
, 命令: ZRANGEBYSCORE key index index <WITHSCORES>
# xjh
# zhangsan
# lisi
ZRANGEBYSCORE score 60 100 # 返回 60 - 100 元素
# 可以在 数值 前面加 "(" 表示不包含此分数
ZRANGEBYSCORE score 60 (100 # 取60-100,但不包含100
ZRANGEBYSCORE score (60 100 # 取60-100,但不包含60
# 支持正负无穷数, +inf / -inf
ZRANGEBYSCORE score 60 +inf # 取60含60以上的分数
# xjh
# 100
# zhangsan
# 99
ZRANGEBYSCORE score 60 +inf WITHSCORES # 额外参数 WITHSCORES, 同时返回 value 和 score
ZREVRANGEBYSCORE
有点类似 ZRANGE
命令,但是支持 LIMIT
用于限制条数。命令:ZREVRANGEBYSCORE key max min LIMIT index index
。
注意:结果按照从大到小进行排序。
# xiejiahe
# zhangsan
# wangwu
ZREVRANGEBYSCORE score 100 0 LIMIT 0 3
如果value存在则在现有基础上增加,否则添加一个新元素并设置相应的数值,命令:ZINCRBY key number value
number 支持负数,所以可以用来减少元素的分数。
ZINCRBY score 98 xiejiahe
# 支持负数
ZINCRBY score -98 xiejiahe
命令:ZCARD key
# 5
ZCARD score
命令:ZCOUNT key min max
# 在 0 - 60 分数中元素数量
ZCOUNT score 0 60
删除一个或多个元素,命令:ZREM key value value...
# 删除一个
ZREM score xiejiahe
# 删除多个
ZREM score xiejiahe wangwu
ZREMRANGEBYRANK
按照元素分数从小到大的顺序进行删除。 命:ZREMRANGEBYRANK key start stop
# 3 (返回删除元素数量)
ZREMRANGEBYRANK score 0 3
ZREMRANGEBYSCORE
按照分数范围删除所有元素,返回删除的元素数量。命令:ZREMRANGEBYSCORE key min max
# 删除0-60的所有元素
ZREMRANGEBYSCORE score 0 60
ZRANK
命令按照元素分数从小到大进行排序获得指定元素的排名,从0开始,命令:ZRANK key value
# 返回1, xiejiahe 排在第二
ZRANK score xiejiahe
Redis 字符串可以当做计数器使用, 内部会自动做类型转换, 常用于点赞、访问量等场景。
自增,如果 key
不存在则默认为 0
, 命令:INCR key
INCR number
自增指定数值, 命令:INCRBY key number
number 支持负数,所以可以替代 DECRBY
命令。
INCRBY number 10
递减,如果 key
不存在则默认为 -1
, 命令:DECR key
DECR number
递减指定数值, 命令:DECRBY key number
DECRBY number 10
自增或递减指定浮点数, 命令:INCRBYFLOAT key number
INCRBYFLOAT number 2.2
INCRBYFLOAT number -2.2
2个关键字: MULTI
开始事务, EXEC
执行事务。
注意:Redis不像关系型数据库那样可以回滚 rollback
,一旦失败只能交给开发者擦屁股。
事务中的命令是在 EXEC 之后才执行的。
一个简单例子:
# 开始事务
> MULTI
# 中间是执行命令
> INCR num
> GET num
# 执行事务并返回所有结果集合
> EXEC
监控一个或多个键,一旦其中有一个键被修改或删除,之后的事务就不会执行,一直持续到 EXEC
命令。
因为事务是在 EXEC 命令之后才执行的,假如在事务内执行原子自增,当其他客户也在执行原子自增就会出现竞态。
可以把 WATCH
理解为锁,锁住键防止其他客户端修改或删除。
# OK, 先设置一个值
> SET name xiejiahe
# OK, 监控 name
> WATCH name
# OK, 这里是能执行的,因为监控的是事务内的执行
> SET name facai
# 开启事务
> MULTI
# 修改 name, 如果其他客户端修改 name 那么这里执行失败,否则成功
SET name wangwu
# 执行事务
> EXEC
WATCH 命令还有一个对应的 UNWATCH
用于取消监控:
# 监控一个键
> WATCH num
# ...其他操作
# 取消监控,为了不影响下一个事务
UNWATCH num
# ...
SORT
命令可以对 列表类型、集合类型、有序集合类型进行排序。
注意:只能对数字元素进行排序,如果是非数字会出错。
# 创建一个列表
LPUSH score 99 5 3 -1 -55 9 100 88
# 排序
SORT score
# 1) "-55"
# 2) "-1"
# 3) "3"
# 4) "5"
# 5) "9"
# 6) "88"
# 7) "99"
# 8) "100"
默认情况下 SORT
是升序排序的从小到大。大多数情况下用倒序比较多,通过 DESC
参数可以实现倒序:
SORT score DESC
有时候并非所有元素都是数字,比如是字母或其他情况。通过 ALPHA
参数实现按照字典顺序排列。
# 设置一个列表为字母元素
LPUSH letter b d z a o p
# 排序
SORT letter ALPHA
# 1) "a"
# 2) "b"
# 3) "d"
# 4) "o"
# 5) "p"
# 6) "z"
SORT 命令还支持 LIMIT
参数,和 SQL 语句一样,可以用于分页。命令: SORT key LIMIT offset count
SORT score DESC LIMIT 1 5
发布的命令是 PUBLISH
, 完整命令使用: PUBLISH channel message
channel
是信道或频道或者理解为是房间,往某个房间发送数据。
执行后返回返回收到这条消息的订阅者数量,如果没有客户端订阅就返回 0;
发布不是持久化的,当发布后接着客户端订阅当前信道接收不到之前的消息。
# 往 room:0 发送 Hello
PUBLISH room:01 Hello
有发布肯定有订阅, 订阅命令是 SUBSCRIBE
, 完整命令使用:SUBSCRIBE channel ...
当执行 SUBSCRIBE
命令后将进入订阅状态。
# 订阅 room:01
SUBSCRIBE room:01
# 进入订阅状态
Switch to Pub/Sub mode. Close console tab to stop listen for messages.
1) "subscribe" # 订阅成功反馈信息
2) "room:01"
3) "1"
1) "message" # 收到的消息
2) "room:01"
3) "hello"
UNSUBSCRIBE
命令可以取消订阅信道, 如果不指定信号将取消所有订阅信道。
注意:如果是在 redis-cli ,无法执行 UNSUBSCRIBE
因为执行订阅后当前实例会被阻塞, 无法执行其他命令。
# 取消指定订阅信道
UNSUBSCRIBE room:01
# 取消全部订阅
UNSUBSCRIBE
检查 key
是否存在,存在返回 1
,否则 0
, 命令:EXISTS key
EXISTS names
获取key类型, 命令:TYPE key
# "string"
TYPE names
可以让一个键在某个时间段删除。 通常会用在获取验证码频繁,登录次数过多等场景。
目前Redis过期时间误差值在 0 ~ 1 毫秒之间。
给指定 key
设置过期时间,单位秒(整数), 命令: EXPIRE key seconds
# 方法一:在已存在的key设置过期时间
EXPIRE names 10
# 或者在设置值时指定过期时间
SET name xiejiahe EX 10
检查Key的生存剩余时间(秒), 命令: TTL key
TTL name
删除过期时间,使 Key
永久存在, 命令: PERSIST key
PERSIST name
除了 PERSIST
, 还可以使用 SET
命令赋值也会删除过期时间
# 设置过期时间为10秒
EXPIRE key 10
# 重新赋值会删除过期时间
SET key 1
删除一个或多个任何类型的 key
, 命令: DEL key...
DEL name
DEL name age # 支持删除多个
清空所有数据库中的数据, 相当于还原Redis初始状态, 命令: FLUSHALL
# 0-16 数据库数据全部清空
FLUSHALL
# 当前数据库中的所有数据将被清除
FLUSHDB
配置文件位于 redis 的安装目录下的 redis.conf
文件。 除了通过配置文件设置以外还支持命令进行配置查看。
设置命令 CONFIG SET key value
。
需要注意不是所有配置都可以用命令设置,有一些配置就必须在 redis.conf
文件中设置,比如 rename-command
。
# 这里增加一个配置name 为 xjh
CONFIG SET name xjh
查看配置 CONFIG GET key
CONFIG GET name
redis 数据库只有密码可以设置,没有用户名一说,redis的设计初衷就是简洁为美。
设计密码很简单,就是通过设置配置文件字段 requirepass
, 命令: CONFIG SET requirepass 密码
密码一定要设置得复杂一点,因为很容易被穷举。
CONFIG SET requirepass ddj1ja@9)..
当数据库设置密码后每次连接 redis 都要进行授权:
# AUTH 后面跟着密码
AUTH ddj1ja@9)..
有些命令比较长很难记,这时候可以给命令进行重命名。需要在 redis.conf 文件中进行设置。
# 将 SET 命令重命名为 s
rename-command SET s
有一些大厂就比较规范,会将一些比较耗性能或危险的命令给禁用掉。 需要在 redis.conf 文件中进行设置。
# 禁掉 FLUSHALL 命令
rename-command FLUSHALL ""
获取所有key名字, 支持正则, 此命令不建议生产环境使用,因为会遍历所有键,当键较多时会影响性能, 命令: KEYS key
# 正则通配符、正则
KEYS *
KEYS n*
获取当前数据库Key数量, 命令: DBSIZE
DBSIZE
RENAME
命令可以将存在的键名修改为新的键名。
SET inc 0
# 修改键名
RENAME inc increment