简单的kafkaredis学习之redis
简单的kafka&redis学习之redis
2. Redis
2.1 什么是Redis
Redis是一种面向 “Key-Value” 数据类型的内存数据库,可以满足我们对海量数据的快速读写需求,Redis是一个 NoSQL 数据库,NoSQL的全称是not only sql,不仅仅是SQL,泛指非关系型数据库,这种类型的数据库不支持SQL语法。
首先Redis是一种内存数据库,它的数据都是放在内存里面的,然后Redis中存储的数据都是key-value类型的,其中redis中的key只能是字符串,value支持多种数据类型
常见的有string、hash、list、set、sortedset
等
- 字符串 string
- 哈希 hash,类似于java中的hashmap
- 字符串列表 list
- 字符串集合 set 不重复,无序
- 有序集合sorted set ,不重复,有序
2.2 Redis的特点
接下来看一下Redis的一些特点
- 高性能:Redis读的速度是11W次/s,写的速度是8.1W次/s
- 原子性:保证数据的准确性
- 持久存储:支持两种方式的持久化,RDB和AOF,可以把内存中的数据持久化到磁盘中
- 支持主从:master-slave架构,可以实现负载均衡、高可用
- 支持集群:从3.0版本开始支持
注意:Redis是一个 单线程的服务,作者之所以这么设计,主要是为了保证redis的快速,高效,如果涉及了多线程,就需要使用锁机制来解决并发问题,这样执行效率反而会打折扣。
2.3 Redis安装部署
下面来看一下redis的安装部署,首先下载redis,使用此链接下载,可以显示Redis目前所有的版本 http://download.redis.io/releases/,我们选择目前比较稳定的5.0.9版本。将下载好的安装包上传到bigdata04机器的/data/soft目录下
将下载好的安装包上传到bigdata04机器的/data/soft目录下
1:解压
[root@bigdata04 soft]# tar -zxvf redis-5.0.9.tar.gz
2:编译+安装
[root@bigdata04 soft]# cd redis-5.0.9
[root@bigdata04 redis-5.0.9]# make
[root@bigdata04 redis-5.0.9]# make install
只要不报错就说明编译安装成功。由于redis需要依赖于C语言环境(yum install gcc),如果你安装的centos镜像是精简版,会缺失c语言的依赖,所以需要安装C语言环境才可以编译成功。我们在这使用的centos镜像是完整版,里面是包含C语言环境的,所以就不存在这个问题了。
如果yum install gcc后执行make报 [致命错误:jemalloc/jemalloc.h:没有那个文件或目录] 错误,需要再执行 make MALLOC=libc
3:修改redis.conf配置文件
[root@bigdata04 redis-5.0.9]# vi redis.conf
daemonize yes
logfile /data/soft/redis-5.0.9/log
bind 127.0.0.1 192.168.182.103
- daemonize参数的值默认是no,表示在前台启动Redis,但是Redis是一个数据库,我们希望把它放到后台运行,所以将参数的值改为yes
- logfile 参数的值默认为空,表示redis会将日志输出到/dev/null里面,也就是不保存了,建议在这设置一个日志路径记录redis的日志,便于后期排查问题。
- bind 参数可以绑定指定ip,这样就可以通过这里指定的ip来访问redis服务了,可以在后面指定当前机器的本地回环地址(127.0.0.1)和内网地址(192.168.182.103),指定本地回环地址是为了能够在本机自己连自己比较方便。指定内网地址是为了能够让公司局域网内的其它服务器也能连到这个redis
如果你这台机器有外网地址的话不建议在这配置,因为使用外网地址的话就不安全了,容易受到网络攻击。
4:启动redis
```shell
[root@bigdata04 redis-5.0.9]# redis-server redis.conf
```
5:验证
注意:redis不是java程序,所以使用jps命令查不到,需要使用ps命令查看redis的进程
```shell
[root@bigdata04 redis-5.0.9]# ps -ef|grep redis
root 5828 1 0 16:12 ? 00:00:00 redis-server 127.0.0.1:6379
```
6:连接redis数据库
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379>
注意:使用redis-cli默认可以连接本地的redis
其实redis-cli后面省略了-h 127.0.0.1
和 -p 6379
[root@bigdata04 redis-5.0.9]# redis-cli -h 127.0.0.1 -p 6379
此时使用内网ip也能连接,这样其实我们就可以在其它安装有redis-cli客户端的机器上连接这个redis服务了。
[root@bigdata04 redis-5.0.9]# redis-cli -h 192.168.182.103 -p 6379
192.168.182.103:6379>
7:停止redis数据库
暴力一点的方式是使用kill命令直接杀进程
不过redis提供的有停止命令
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379> shutdown
not connected>
或者这样停止也是可以的
[root@bigdata04 redis-5.0.9]# redis-cli shutdown
2.4 Redis基础命令
下面我们来看一下Redis中的基础命令
先启动redis服务,使用redis-cli客户端连到redis数据库里面
[root@bigdata04 redis-5.0.9]# redis-server redis.conf
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379>
-
获得符合规则的键:keys
keys 后面可以指定正则表达式
127.0.0.1:6379> keys * (empty list or set) 127.0.0.1:6379> set a 1 OK 127.0.0.1:6379> keys * 1)"a" 127.0.0.1:6379> keys a* 1) "a" 127.0.0.1:6379> keys a+ (empty list or set)
注意:在生产环境下建议禁用keys命令,因为这个命令会查询过滤redis中的所有数据,可能会造成服务阻塞,影响redis执行效率。
如果有类似的查询需求建议使用scan,scan命令用于迭代当前数据库中的key集合,它支持增量式迭代,每次执行只会返回少量元素,所以它可以用于生产环境,而不会出现像keys 命令那样可能会阻塞服务器的问题。
SCAN命令是一个基于游标的迭代器。这意味着命令每次被调用都需要使用上一次调用返回的游标作为该次调用的游标参数,以此来延续之前的迭代过程
当SCAN命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。向redis中初始化一批数据
127.0.0.1:6379> set a1 1 OK 127.0.0.1:6379> set a2 1 OK 127.0.0.1:6379> set a3 1 OK 127.0.0.1:6379> set a4 1 OK 127.0.0.1:6379> set a5 1 OK 127.0.0.1:6379> set a6 1 OK 127.0.0.1:6379> set a7 1 OK 127.0.0.1:6379> set a8 1 OK 127.0.0.1:6379> set a9 1 OK 127.0.0.1:6379> set a10 1 OK
使用scan迭代数据,后面游标参数指定为0,表示从头开始迭代key
127.0.0.1:6379> scan 0 1) "3" 2) 1) "a9"2) "a3"3) "a1"4) "a10"5) "a8"6) "a5"7) "a4"8) "a"9) "a7"10) "a6"
SCAN 命令的返回值是一个包含两个元素的数组,第一个元素是用于进行下一次迭代的新游标,而第二个元素则是一个数组, 这个数组中包含了所有被迭代出来的元素。默认情况下scan返回10条数据,所以这样执行效果也是一样的
127.0.0.1:6379> scan 0 count 10 1) "3" 2) 1) "a9"2) "a3"3) "a1"4) "a10"5) "a8"6) "a5"7) "a4"8) "a"9) "a7"10) "a6"
scan命令此时返回的游标为3,注意,游标的值并不等于返回的数据量。如果想要继续往下面迭代数据的话,下一次执行scan的时候需要指定之前返回的游标,redis会根据这个游标继续往下面迭代
127.0.0.1:6379> scan 3 count 10 1) "0" 2) 1) "a2"
这一次使用scan命令,返回的游标为0,表示迭代已经结束,整个redis中的key都被迭代完了。redis中一共有11个key,第一次使用scan 0获取到了10个key,第二次获取到了1个key,没有问题。
注意:大家在下面练习的时候可能会发现你那边第一次返回的游标和我这边显示的不一样,那也很正常,因为你会发现你返回的这个数据集的顺序也是不一样的。
所以,如果redis中有很多key,我们可以使用scan命令来迭代,一次迭代一部分,不至于造成阻塞,如果redis中的key比较少,那么使用keys * 也是可以的。如果想要在迭代key的时候对key进行过滤,可以在scan后面指定match参数,match后面可以指定正则表达式
127.0.0.1:6379> scan 0 match a[1-5] count 10 1) "3" 2) 1) "a3"2) "a1"3) "a5"4) "a4"
此时实际返回的key的数量是4个,但是游标还是3,相当于还是迭代了10条数据,只不过不满足条件的没有返回而已。
-
判断键是否存在:exists
127.0.0.1:6379> exists a (integer) 1 127.0.0.1:6379> exists b (integer) 0
-
删除键:del
127.0.0.1:6379> del a (integer) 1
注意:del也支持一次删除多个key
127.0.0.1:6379> del a1 a2 (integer) 2
-
获得键值的类型:type
返回值可能是这五种类型(string,hash,list,set,zset)
127.0.0.1:6379> set a 1 OK 127.0.0.1:6379> type a string
这个命令可以帮我们快速识别某一个key中存储的数据是什么类型的,因为针对存储了不同类型值的key,操作的命令是不一样的。
-
帮助命令:help
127.0.0.1:6379> help setSET key value [EX seconds] [PX milliseconds] [NX|XX]summary: Set the string value of a keysince: 1.0.0group: string
-
退出客户端:quit/exit
127.0.0.1:6379> quit
不过我还是习惯使用
ctrl+c
退出redis-cli客户端,最后有一点需要注意:Redis的命令不区分大小写,但是key的名称需要区分大小写!
2.5 Redis多数据库特性
Redis默认支持 16 个数据库,通过databases
参数控制的
这个参数在redis.conf配置文件中
[root@bigdata04 redis-5.0.9]# cat redis.conf | grep databases
# Set the number of databases. The default database is DB 0, you can select
# dbid is a number between 0 and 'databases'-1
databases 16
# Compress string objects using LZF when dump .rdb databases?
每个数据库对外都是以一个从0开始的递增数字命名,不支持自定义
Redis默认选择的是0号数据库,可以通过 select 命令切换
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> select 2
OK
127.0.0.1:6379[2]> select 15
OK
127.0.0.1:6379[15]> select 16
(error) ERR DB index is out of range
一般在工作中会使用2~3个数据库,可以根据业务类型来分库,不同业务的数据存到不同的库里面,还有一种用法是,一个库作为测试库,一个库作为正式库。
如果没有特殊需求,一般使用0号数据库就可以了,这个库使用起来比较方便,默认就是0号库,不需要使用select切换。具体在工作中怎么用都行,只要理解它的特性就可以了。但是有一点需要注意:多个数据库之间并不是完全隔离的,如果使用flushall命令,则会清空redis中所有数据库内的数据。
并且我们在redis中使用多个库,并不能提高redis的存储能力,因为默认这16个库共用redis的内存存储空间,如果想要提高redis的存储能力,需要给我们的服务器增加内存才可以。
127.0.0.1:6379[15]> set x 1
OK
127.0.0.1:6379[15]> flushall
OK
127.0.0.1:6379[15]> keys *
(empty list or set)
127.0.0.1:6379[15]> select 0
OK
127.0.0.1:6379> keys *
(empty list or set)
如果只想清空当前数据库中的数据,可以使用flushdb
127.0.0.1:6379> select 15
OK
127.0.0.1:6379[15]> set a 1
OK
127.0.0.1:6379[15]> keys *
1) "a"
127.0.0.1:6379[15]> flushdb
OK
2.6 Redis数据类型
下面来看一下redis中的常用数据类型,这些数据类型都是针对于redis中的value而言的,因为key都是字符串
- string
- hash
- list
- set
- sorted set
2.6.1 Redis数据类型之string
字符串类型是redis中最基本的数据类型,它能存储任何形式的内容,包含二进制数据,甚至是一张图片
一个字符串类型的值存储的最大容量是1GB,一般情况下我们存储的单条数据肯定是达不到的这个限值的,所以大家不用担心
string类型比较适合存储类型单一的数据
针对string类型主要有下面这些常见命令:
命令 | 格式 | 解释 |
---|---|---|
set | set key value | 给key设置一个Value(字符串类型的) |
get | get key | 获取key的值 |
incr | incr key | 对key的值递加+1(值必须是数字) |
decr | decr key | 对key的值递减-1(值必须是数字) |
strlen | strlen key | 获取key值的长度 |
添加数据 set
127.0.0.1:6379> set str a
OK
查询数据 get
127.0.0.1:6379> get str
"a"
一次添加多条数据
127.0.0.1:6379> mset str1 a1 str2 a2
OK
一次查询多条数据
127.0.0.1:6379> mget str1 str2
1) "a1"
2) "a2"
递增1
127.0.0.1:6379> set num 1
OK
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> get num
"2"
递减1
127.0.0.1:6379> decr num
(integer) 1
127.0.0.1:6379> get num
"1"
递增指定数值(整数类型)
127.0.0.1:6379> incrby num 2
(integer) 3
127.0.0.1:6379> get num
"3"
127.0.0.1:6379> incrby num 2.1
(error) ERR value is not an integer or out of range[注意:增量数值只支持integer类型]
递减指定数值(整数类型)
127.0.0.1:6379> decrby num 2
(integer) 1
127.0.0.1:6379> get num
"1"
递增指定数值(float类型)
127.0.0.1:6379> incrbyfloat num 2.1
"3.1"
127.0.0.1:6379> get num
"3.1"
获取指定key的value长度
127.0.0.1:6379> get str
"a"
127.0.0.1:6379> strlen str
(integer) 1
127.0.0.1:6379> set str abcd
OK
127.0.0.1:6379> strlen str
(integer) 4
2.6.2 Redis数据类型之hash
hash类型的值存储了字段和字段值的映射,字段和字段值只能是字符串,不支持其他数据类型。hash类型的值至多存储2的32次方-1
个字段,一般情况下我们也达不到这个极限,hash类型比较适合存储对象,因为对象里面是有一些属性和值的,我们就可以把这些属性和值存储到这个hash类型里面
针对hash类型主要有下面这些常见命令:
命令 | 格式 | 解释 |
---|---|---|
hset | hset key field value | 向hash中添加字段和值 |
hget | hget key field | 获取hash中指定字段的值 |
hgetall | hgetall key | 获取hash中所有的字段和值 |
hexists | hexists key field | 判断hash中是否包含指定字段 |
hincrby | hincrby key field num | 对hash中指定字段的值递增 |
hdel | hdel key field | 删除hash中指定的字段 |
hkeys/hvals | hkeys/hvals key | 获取hash中所有字段或字段值 |
hlen | hlen key | 获取hash中所有字段的数量 |
添加数据 hget
127.0.0.1:6379> hset user:1 name zs
(integer) 1
查询数据 hget
127.0.0.1:6379> hget user:1 name
"zs"
向一个hash中同时添加多个k-v hmset
127.0.0.1:6379> hmset user:2 name lisi age 18
OK
查询一个hash数据中多个k的值 hmget
127.0.0.1:6379> hmget user:2 name age
1) "lisi"
2) "18"
查询一个hash数据中的所有k-v hgetall
127.0.0.1:6379> hgetall user:2
1) "name"
2) "lisi"
3) "age"
4) "18"
判断一个hash数据中是否存在指定k hexists
127.0.0.1:6379> hexists user:2 name
(integer) 1
127.0.0.1:6379> hexists user:2 city
(integer) 0
对一个hash数据中指定k的v进行递增 hincrby
127.0.0.1:6379> hincrby user:2 age 1
(integer) 19
127.0.0.1:6379> hget user:2 age
"19"
删除一个hash数据中的指定k hdel
127.0.0.1:6379> hset user:2 city beijing
(integer) 1
127.0.0.1:6379> hdel user:2 city
(integer) 1
获取一个hash数据中的所有k hkeys
127.0.0.1:6379> hkeys user:2
1) "name"
2) "age"
获取一个hash数据中的所有v hvals
127.0.0.1:6379> hvals user:2
1) "lisi"
2) "19"
获取一个hash数据中有多少个k hlen
127.0.0.1:6379> hlen user:2
(integer) 2
2.6.3 Redis数据类型之list
list是一个有序的字符串列表,列表内部是使用双向链表(linked list)实现的,list列表类型的值最多可以存储2的32次方-1
个元素,一般我们也达不到这个限值。list类型比较适合作为队列使用,使用lpush+rpop可以实现先进先出的队列
针对list类型主要有下面这些常见命令:
命令 | 格式 | 解释 |
---|---|---|
lpush | lpush key value | 从列表左侧添加元素 |
rpush | rpush key value | 从列表右侧添加元素 |
lpop | lpop key | 从列表左侧弹出元素 |
rpop | rpop key | 从列表右侧弹出元素 |
llen | llen key | 获取列表的长度 |
lrange | lrange key start stop | 获取列表指定区间的元素 |
lindex | lindex key index | 获取列表指定角标的元素 |
lset | lset key index value | 修改列表中指定角标的元素 |
添加元素(左侧添加) lpush
127.0.0.1:6379> lpush list1 a
(integer) 1
127.0.0.1:6379> lpush list1 b
(integer) 2
取出元素(左侧取元素) lpop
127.0.0.1:6379> lpop list1
"b"
127.0.0.1:6379> lpop list1
"a"
127.0.0.1:6379> lpop list1
(nil)
添加元素(右侧添加) rpush
127.0.0.1:6379> rpush list2 x
(integer) 1
127.0.0.1:6379> rpush list2 y
(integer) 2
取出元素(右侧取元素) rpop
127.0.0.1:6379> rpop list2
"y"
127.0.0.1:6379> rpop list2
"x"
列表长度 llen
127.0.0.1:6379> lpush list3 a b c d
(integer) 4
127.0.0.1:6379> llen list3
(integer) 4
获取列表中的元素 lrange
127.0.0.1:6379> lrange list3 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
查询指定角标元素 lindex
127.0.0.1:6379> lindex list3 1
"c"
修改指定角标元素 lset
127.0.0.1:6379> lset list3 1 m
OK
127.0.0.1:6379> lrange list3 0 -1
1) "d"
2) "m"
3) "b"
4) "a"
2.6.4 Redis数据类型之set
set是一个集合,set集合中的元素都是不重复的,无序的。set集合类型的值最多可以存储2的32次方-1个
元素。set集合比较适合用在去重的场景下,因为它里面的元素是都不重复的
针对set类型主要有下面这些常见命令:
命令 | 格式 | 解释 |
---|---|---|
sadd | sadd key value | 向集合中添加元素 |
smembers | smembers key | 获取集合中所有元素 |
srem | srem key value | 从集合中删除指定元素 |
sismember | sismember key value | 判断集合中是否包含指定元素 |
sdiff | sdiff key1 key2 | 获取两个集合的差集 |
sinter | sinter key1 key2 | 获取两个集合的交集 |
sunion | sunion key1 key2 | 获取两个集合的并集 |
scard | scard key | 获取集合中元素的数量 |
向集合中添加元素 sadd
127.0.0.1:6379> sadd set1 a
(integer) 1
127.0.0.1:6379> sadd set1 b
(integer) 1
获取集合中所有元素 smembers
127.0.0.1:6379> smembers set1
1) "b"
2) "a"
删除集合中的元素 srem
127.0.0.1:6379> srem set1 a
(integer) 1
判断元素是否存在集合中 sismember
127.0.0.1:6379> sismember set1 b
(integer) 1
127.0.0.1:6379> sismember set1 a
(integer) 0
两个集合取差集 sdiff
127.0.0.1:6379> sadd set2 a b c
(integer) 3
127.0.0.1:6379> sadd set3 a b x
(integer) 3
127.0.0.1:6379> sdiff set2 set3
1) "c"
127.0.0.1:6379> sdiff set3 set2
1) "x"
两个集合取交集 sinter
127.0.0.1:6379> sinter set2 set3
1) "b"
2) "a"
两个集合取并集 sunion
127.0.0.1:6379> sunion set2 set3
1) "c"
2) "a"
3) "x"
4) "b"
获取集合长度(获取集合中元素的个数) scard
127.0.0.1:6379> scard set3
(integer) 3
2.6.5 Redis数据类型之sorted set
有序集合,在集合类型的基础上为集合中的每个元素都关联了一个分数,根据分数进行排序,这样就实现了有序。sorted set比较适合用在获取TopN的场景,因为它里面的数据是有序的
针对sorted set类型主要有下面这些常见命令:
命令 | 格式 | 解释 |
---|---|---|
zadd | zadd key value | 向集合中添加元素 |
zscore | zscore key value | 获取集合中指定元素的分值 |
zrange | zrange key value | 获取集合指定元素的排名(正序) |
zrevrange | 格式同上 | 获取集合指定元素的排名(倒序) |
zincrby | zincrby key num value | 给集合中指定元素增加分值 |
zcard | zcard key | 获取集合中元素的数量 |
zrem | zrem key value | 从集合中删除指定元素 |
向集合中添加元素 zadd
127.0.0.1:6379> zadd zset1 5 a
(integer) 1
127.0.0.1:6379> zadd zset1 3 b
(integer) 1
127.0.0.1:6379> zadd zset1 4 c
(integer) 1
查询集合中指定元素的分值 zscore
127.0.0.1:6379> zscore zset1 a
"5"
根据角标获取集合中的元素(按照正序) zrange
127.0.0.1:6379> zrange zset1 0 -1
1) "b"
2) "c"
3) "a"
根据角标获取集合中的元素(按照倒序) zrevrange
127.0.0.1:6379> zrevrange zset1 0 -1
1) "a"
2) "c"
3) "b"
对集合中元素的分值进行递增 zincrby
127.0.0.1:6379> zincrby zset1 3 a
"8"
127.0.0.1:6379> zscore zset1 a
"8"
获取集合中元素的个数 zcard
127.0.0.1:6379> zcard zset1
(integer) 3
删除集合中的元素 zrem
127.0.0.1:6379> zrem zset1 a
(integer) 1
127.0.0.1:6379> zrange zset1 0 -1
1) "b"
2) "c"
sorted set使用注意点:
- 1:+inf(正无穷) -inf(负无穷),在给集合中的元素设置分值的时候可以使用这两个特殊数值。
- 2:set命令:如果key持有其它类型值,set会覆盖旧值,无视类型
2.6.6 eg:存储高一班的学员信息
需求:将学员的姓名、年龄、性别、住址信息保存到Redis中,分析一下:
在这里我们可以把学生认为是一个对象,学生对象具备了多个属性信息,姓名,年龄,性别,住址信息
所以针对学生信息非常适合使用hash类型进行存储
我们可以给学生生成一个编号拼接到key里面,姓名、年龄、性别、住址信息存储到hash类型的value中
注意:这里面针对key的命名,stu是student的简写,尽量不要写太多字符,否则会额外占用内存空间的,后面的:1,表示这个学生的编号是1,后期如果我们想获取所有学员的key,就可以使用这个规则进行过滤了。
这个规则进行过滤了。
127.0.0.1:6379> hmset stu:1 name xiaoming age 18 sex 0 address beijing
OK
127.0.0.1:6379> hgetall stu:1
1) "name"
2) "xiaoming"
3) "age"
4) "18"
5) "sex"
6) "0"
7) "address"
8) "beijing"
127.0.0.1:6379> hget user:1 age
"18"
2.7 JAVA操作redis
在这我们以java代码为例,演示一下如何使用java代码操作redis,我们需要借助于第三方jar包jedis来操作,首先在idea中创建maven项目db_redis
。在pom.xml文件中添加jedis依赖
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.3.0</version>
</dependency>
2.7.1 单连接方式
接下来使用单连接的方式操作redis,代码如下:
package cn.git.redis;import redis.clients.jedis.Jedis;/*** 单连接方式操作redis* Created by lixuchun*/
public class RedisSingle {/*** 注意:此代码能够正常执行的前提是* 1:redis所在服务器的防火墙需要关闭* 2:redis.conf中的bind参数需要指定192.168.182.103* @param args*/public static void main(String[] args) {//获取jedis连接Jedis jedis = new Jedis("192.168.182.103",6379);//向redis中添加数据,key=git,value=hello bigdata!jedis.set("git","hello bigdata!");//从redis中查询key=git的value的值String value = jedis.get("git");System.out.println(value);//关闭jedis连接jedis.close();}
}
代码执行效果如下:hello bigdata!,此时到redis中确认一下:
127.0.0.1:6379> keys *
1) "git"
127.0.0.1:6379> get git
"hello bigdata!"
其实在这你会发现,我们前面讲的那些在redis-cli中使用的命令,和jedis中提供的函数名称是一一对应的。切换到代码中来使用也是可以直接上手的。
2.7.2 连接池方式
接下来使用连接池的方式操作redis,代码如下:
package cn.git.redis;import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;/*** 连接池的方式操作redis* Created by lixuchun*/
public class RedisPool {public static void main(String[] args) {//创建连接池配置对象JedisPoolConfig poolConfig = new JedisPoolConfig();//连接池中最大空闲连接数poolConfig.setMaxIdle(10);//连接池中创建的最大连接数poolConfig.setMaxTotal(100);//创建连接的超时时间poolConfig.setMaxWaitMillis(2000);//表示从连接池中获取连接的时候会先测试一下连接是否可用,这样可以保证取出的连接都是可用的poolConfig.setTestOnBorrow(true);//获取jedis连接池JedisPool jedisPool = new JedisPool(poolConfig, "192.168.182.103", 6379);//从jedis连接池中取出一个连接Jedis jedis = jedisPool.getResource();String value = jedis.get("git");System.out.println(value);//注意:此处的close方法有两层含义//1:如果jedis是直接创建的单连接,此时表示直接关闭这个连接//2:如果jedis是从连接池中获取的连接,此时会把这个连接返回给连接池jedis.close();//关闭jedis连接池jedisPool.close();}
}
执行结果:hello bigdata!
2.7.3 提取RedisUtils工具类
基于redis连接池的方式提取RedisUtils工具类
package cn.git.redis;import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;/*** 基于redis连接池提取redis工具类* Created by lixuchun*/
public class RedisUtils {//私有化构造函数,禁止newprivate RedisUtils(){}private static JedisPool jedisPool = null;//获取连接public static synchronized Jedis getJedis(){if(jedisPool==null){JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxIdle(10);poolConfig.setMaxTotal(100);poolConfig.setMaxWaitMillis(2000);poolConfig.setTestOnBorrow(true);jedisPool = new JedisPool(poolConfig, "192.168.182.103", 6379);}return jedisPool.getResource();}//向连接池返回连接public static void returnResource(Jedis jedis){jedis.close();}}
使用工具类代码
package cn.git.redis;import redis.clients.jedis.Jedis;/*** Created by lixuchun*/
public class TestRedisUtils {public static void main(String[] args) {//获取连接Jedis jedis = RedisUtils.getJedis();String value = jedis.get("git");System.out.println(value);//向连接池返回连接RedisUtils.returnResource(jedis);}
}
2.8 高级特性
2.8.1 expire 生存时间
Redis中可以使用expire命令设置一个键的生存时间,到时间后Redis会自动删除它
它的一个典型应用场景是:手机验证码
我们平时在登录或者注册的时候,手机会接收到一个验证码,上面会提示验证码的过期时间,过了这个时间之后这个验证码就不能用了。
expire支持以下操作
命令 | 格式 | 解释 |
---|---|---|
expire | expire key seconds | 设置key的过期时间(单位:秒) |
ttl | ttl key | 获取key的剩余有效时间 |
persist | persist key | 取消key的过期时间 |
expireat | expireat key timestamp | 设置UNIX时间戳的过期时间 |
设置key的过期时间
127.0.0.1:6379> set abc 123
OK
127.0.0.1:6379> expire abc 200
(integer) 1
获取key的剩余有效时间
127.0.0.1:6379> ttl abc
(integer) 192
取消key的过期时间
127.0.0.1:6379> persist abc
(integer) 1
此时再查看这个key的剩余有效时间,返回的值是-1,-1表示这个key是一个永久存在的key
127.0.0.1:6379> ttl abc
(integer) -1
还可以通过expireat指定key在指定时间点过期
先获取当前时间戳
[root@bigdata04 ~]# date +%s
1768618628127.0.0.1:6379> expireat abc 1768618638
(integer) 1
过一会再查看这个key的剩余有效时间,返回的是-2,表示这个key被删除了,不存在了
127.0.0.1:6379> ttl abc
(integer) -2
127.0.0.1:6379> exists abc
(integer) 0
总结一下:
当key永久存在的时候,执行ttl返回的是-1,
当key被设置了过期时间之后,执行ttl返回的就是这个key剩余的有效时间
当key已经被删除了,不存在的时候,执行ttl返回的是-2
2.8.2 pipeline 管道
针对批量操作数据或者批量初始化数据的时候使用,效率高,Redis的pipeline功能在命令行中没有实现,在Java客户端(jedis)中是可以使用的
它的原理是这样的
不使用管道的时候,我们每执行一条命令都需要和redis服务器交互一次
使用管道之后,可以实现一次提交一批命令,这一批命令只需要和redis服务器交互一次,所以就提高了性能。
这个功能就类似于mysql中的batch批处理。
接下来看一个案例,案例:初始化10万条数据,需求:使用普通方式一条一条添加和使用管道批量初始化进行对比分析
代码如下:
package cn.git.redis;import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;/*** pipeline(管道)的使用* Created by lixuchun*/
public class PipelineOp {public static void main(String[] args) {// 1:不使用管道Jedis jedis = RedisUtils.getJedis();long start_time = System.currentTimeMillis();for(int i=0;i<100000;i++){jedis.set("a"+i,"a"+i);}long end_time = System.currentTimeMillis();System.out.println("不使用管道,耗时:"+(end_time-start_time));// 2:使用管道Pipeline pipelined = jedis.pipelined();start_time = System.currentTimeMillis();for(int i=0;i<100000;i++){pipelined.set("b"+i,"b"+i);}pipelined.sync();end_time = System.currentTimeMillis();System.out.println("使用管道,耗时:"+(end_time-start_time));RedisUtils.returnResource(jedis);}}
结果如下:
不使用管道,耗时:40887
使用管道,耗时:180
在代码执行的过程中,我们可以使用info命令观察数据库中的数据条数
127.0.0.1:6379> info
# Keyspace
db0:keys=200000,expires=1,avg_ttl=389945
从这可以看出来,针对海量数据的初始化,管道可以显著提高初始化性能。
2.8.3 info命令
这里面参数比较多,在这我们主要关注几个重点的参数
# Redis 服务器版本
redis_version:5.0.9
# Redis服务的可执行文件路径
executable:/data/soft/redis-5.0.9/redis-server
# 启动Redis时使用的配置文件路径
config_file:/data/soft/redis-5.0.9/redis.conf
# 已连接客户端的数量
connected_clients:1
# Redis目前存储数据使用的内容
used_memory_human:15.01M
# Redis可以使用的内存总量,和服务器的内存有关
total_system_memory_human:1.78G
# db0表示0号数据库,keys:表示0号数据库的key总量,expires:表示0号数据库失效被删除的key总量
db0:keys=200001,expires=1,avg_ttl=389945
2.8.4 Redis的持久化
Redis持久化简单理解就是把内存中的数据持久化到磁盘中 可以保证Reids重启之后还能恢复之前的数据,Redis支持两种持久化,可以 单独使用 或者 组合使用
RDB 和 AOF
2.8.4.1 Redis持久化之RDB
RDB是Redis默认的持久化机制,RDB持久化是通过快照完成的,当符合一定条件时Redis会自动将内存中的所有数据执行快照操作并存储到硬盘上,默认存储在dump.rdb
文件中
[root@bigdata04 redis-5.0.9]# ll
....
-rw-r--r--. 1 root root 2955661 Jan 17 12:12 dump.rdb
......
Redis什么时候会执行快照?Redis执行快照的时机是由以下参数控制的,这些参数是在redis.conf文件中的
save 900 1
save 300 10
save 60 10000
save 900 1 表示900秒内至少一个key被更改则进行快照,这里面的三个时机哪个先满足都会执行快照操作。
RDB持久化的优缺点:
- RDB的优点:由于存储的有数据快照文件,恢复数据很方便
- RDB的缺点:会丢失最后一次快照以后更改的所有数据,因为两次快照之间是由一个时间差的,这一段时间之内修改的数据可能会丢。
2.8.4.2 Redis持久化之AOF
AOF持久化是通过日志文件的方式,默认情况下没有开启,可以通过appendonly
参数开启
[root@bigdata04 redis-5.0.9]# vi redis.conf
....
appendonly yes
....
AOF日志文件的保存位置和RDB文件相同,都是dir参数设置的,默认的文件名是appendonly.aof
注意:dir参数的值为. 表示当前目录,也就是说我们在哪个目录下启动redis,rdb快照文件和aof日志文件就产生在哪个目录下。
可以试验一下,换一个目录启动redis,发下redis中的数据是空的。关闭之后,重新在之前的目录启动redis,数据又回来了。
AOF方式只会记录用户的写命令,添加、修改、删除之类的命令,查询命令不会记录,因为查询命令不会影响数据的内容。
那redis什么时候会把用户的写命令同步到aof文件中呢?
# appendfsync always
# appendfsync everysec
# appendfsync no
三种配置解释如下:
- appendfsync everysec,默认是每秒钟执行一次同步操作。
- appendfsync always,实现每执行一次写操作就执行一次同步操作,但是这样效率会有点低。
- appendfsync no,表示不主动进行同步,由操作系统来做,30秒执行一次。
如果大家对数据的丢失确实是0容忍的话,可以使用always。不过一般情况下,redis中存储的都是一些缓存数据,就算丢了也没关系,程序还会继续往里面写新数据,不会造成多大影响。
2.9 Redis 的安全策略
2.9.1 设置数据库密码
默认情况下访问redis只要网络能通就可以直接访问,这样其实是有一些不安全的,不过我们一般会限制只能在内网访问,这样其实问题也不大。
redis针对这个问题,也支持给数据库设置密码,在redis.conf中配置
[root@bigdata04 redis-5.0.9]# vi redis.conf
....
requirepass admin
....
重启redis服务
[root@bigdata04 redis-5.0.9]# redis-cli shutdown
[root@bigdata04 redis-5.0.9]# redis-server redis.conf
重新连接redis
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379> get git
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth admin
OK
127.0.0.1:6379>get git
hello bigdata!
在代码层面,以后在使用的时候时候就需要使用auth方法先校验权限了。
package com.git.redis;import redis.clients.jedis.Jedis;/*** 单连接方式操作redis* Created by xuwei*/
public class RedisSingle {/*** 注意:此代码能够正常执行的前提是* 1:redis所在服务器的防火墙需要关闭* 2:redis.conf中的bind参数需要指定192.168.182.103* @param args*/public static void main(String[] args) {// 获取jedis连接Jedis jedis = new Jedis("192.168.182.103",6379);// 使用密码jedis.auth("admin");// 向redis中添加数据,key=git,value=hello bigdata!jedis.set("git","hello bigdata!");// 从redis中查询key=git的value的值String value = jedis.get("git");System.out.println(value);//关闭jedis连接jedis.close();}
}
注意:在实际工作中一般不会设置密码,因为我们在这设置的密码是明文的,其实意义也不大,针对别有用心的人,你这样设置是没有意义的。
所以在实际工作中我们一般只需要控制好redis服务器的访问权限就可以了,redis服务器的访问权限其实就是使用bind参数来设置的。所以再把刚才设置的密码取消掉,直接把对应的配置注释掉即可。
所以再把刚才设置的密码取消掉,直接把对应的配置注释掉即可
[root@bigdata04 redis-5.0.9]# vi redis.conf
#requirepass admin
2.9.2 bind参数的应用
在实际工作中,我们的服务器至少会有3个ip地址
- 127.0.0.1 这个是本机回环地址
- 192.168.10.14 这个是本机的内网地址
- 还有一个是外网地址
我们一般会使用bind绑定内网ip,这样其实就限制了redis服务器的访问范围,不会暴露在外网,只需要运维同学做好网络的访问限制就可以了,此时我们就可以认为redis是安全的了。
2.9.3 命令重命名
咱们前面讲过一个命令是flushall
,这个命令是很危险的,它可以把redis中的所有数据全部清空
所以在实际工作中一般需要将这个命令给禁用掉,防止误操作。
在redis.conf配置文件中进行设置
[root@bigdata04 redis-5.0.9]# vi redis.conf
....
rename-command flushall ""
....
这样修改之后,就把flushall命令给禁用掉了,重启redis服务
[root@bigdata04 redis-5.0.9]# redis-cli shutdown
[root@bigdata04 redis-5.0.9]# redis-server redis.conf
重新连接redis
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379> flushall
(error) ERR unknown command `flushall`, with args beginning with:
127.0.0.1:6379>
此时会提示未知命令。其实我们还可以选择,在重命名的时候给这个命令起一个别名,这样后期如果想使用的时候也是可以使用的。我们现在在后面直接指定的是空字符串 就是直接禁用了,如果指定一个其它字符串,就相当于起别名了。
2.9.4 Redis实例最多存多少key
一个Redis实例最多能存放多少key?有没有限制?Redis本身是不会限制存储多少key的,但是Redis是基于内存的,它的存储极限是系统中的可用内存值,如果内存存满了,那就无法再存储key了。
2.9.5 Redis监控命令-monitor
这个monitor
命令是一把双刃剑。在实际工作中要慎用。先演示一下:
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379> monitor
OK
执行代码RedisSingle.java
中的代码,然后会发现monitor监控到我们对redis的所有操作
[root@bigdata04 redis-5.0.9]# redis-cli
127.0.0.1:6379> monitor
OK
1768628815.007443 [0 192.168.182.1:60633] "SET" "git" "hello bigdata!"
1768628815.007797 [0 192.168.182.1:60633] "GET" "git"
monitor可以监控我们对redis的所有操作,如果在线上的服务器上打开了这个功能,这里面就会频繁打印出来我们对redis数据库的所有操作,这样会影响redis的性能,所以说要慎用。
但是在某些特殊的场景下面它是很有用的,之前在工作中我遇到过一个很奇怪的问题,redis中的一个key总是会莫名其妙的消失,我的一个程序会定时向redis中写入一个key,但是我发现这个key刚写进去,然后一会就没了,很奇怪,当时我仔细排查了我的代码,里面既没有设置失效时间,也没有使用删除功能。
所以这个key不是我的代码删的,肯定是有其它的代码会删除这个key,但是到底是哪的代码?这个时候就不好排查了,我们的业务机有几十台,根本无从下手。
这个时候我突然想到了monitor这个命令,虽然开启monitor会影响redis的性能,但是这个时候需要排查问题,使用一会也是可以接受的。
所以就打开了monitor,打开之后屏幕上就打印出来很多命令,这样根本就看不清,没办法追踪,数据太多了。所以又想到了这个办法,结合grep命令来操作,这样就可以过滤出来对指定key的所有操作了。
[root@bigdata04 redis-5.0.9]# redis-cli monitor | grep key
1768629282.484868 [0 192.168.182.1:52364] "del" "key"
通过这条数据我们可以分析出来到底是哪台机器上的程序删除了这个key。然后再排查这台机器上都有哪些程序,对应的去排查代码,这样就快多了,最终发现是有一个代码里面会定时删除这个key。这个就是monitor的典型应用。
相关文章:

简单的kafkaredis学习之redis
简单的kafka&redis学习之redis 2. Redis 2.1 什么是Redis Redis是一种面向 “Key-Value” 数据类型的内存数据库,可以满足我们对海量数据的快速读写需求,Redis是一个 NoSQL 数据库,NoSQL的全称是not only sql,不仅仅是SQL&…...
前端性能优化全攻略:提升用户体验,加速页面加载
在当今互联网时代,用户对于网页的加载速度和性能要求越来越高。快速响应的网页不仅能提升用户体验,还能提高网站的搜索引擎排名和转化率。因此,前端性能优化成为了前端开发中至关重要的一环。本文将深入探讨前端性能优化的原则、方法以及如何…...

手机玩亚托莉:我挚爱的时光!手机推gal、躺床玩漫改gal教程
亚托莉:我挚爱的时光是一款视觉与情感交织的好游戏 。游戏背景设定在因为不明原因导致全球海平面上升之后的未来,在全球大多数地方都被海洋淹没城市才是相对环境的情况下,在一场事故失去了一条腿的男主斑鸠夏生却选择了放弃城市,转…...
metasploit/modules/evasion 有哪些模块,以及具体使用案例
Metasploit框架的evasion模块用于生成绕过安全检测的有效载荷。以下是一些常见的evasion模块及其使用案例: 1. 通用Evasion模块 windows/meterpreter/reverse_tcp_rc4:使用RC4加密的反向TCP Meterpreter会话。 set PAYLOAD windows/meterpreter/reverse…...

网络安全入门文档-虚拟机配置篇
前言 虚拟机作为网络安全渗透测试中常见的工具。通常被用来安装kali系统 简单解释一下,目前操作系统分为三类 windows、linux、mac linux又有两个小类,分别是RedHat、Debian 而我们要安装的kali就是基于Debian的操作系统。 简单来说。虚拟机和系统是两个…...

class 041 最大公约数、同余原理
1. 辗转相除法 对下面的证明过程有什么问题和怀疑的直接随便找两个数字自己写一遍就行了. 1.1 利用辗转相除法计算最大公约数 直接记忆这段代码公式就行了(具体的证明过程直接去看左程云老师写的就行了). public static long gcd(long a, long b) { // Greatest Common Di…...
token的创建与解析,并配合拦截器使用
场景: 进行web应用开发时,往往需要对当前用户进行身份判断,此时可以使用token技术 1.导入依赖 <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><scope>runtime<…...
Oracle 数据库历史备份数据恢复验证
Oracle ASM 管理的数据库历史备份数据恢复至单实例数据库 简介: 验证 ASM 管理的数据库的历史备份恢复至单实例数据库(主要目的在于验证历史备份是否可用的一次恢复演练) 一、恢复演练系统选择 根据数据库情况选择恢复测试的环境。 此次恢…...

【网络面积篇】TCP断开连接(笔记)
目录 一. 四次挥手 (1)过程描述 (2)为什么是四次挥手? 二、相关问题 1. 第一次挥手丢失了,会发生什么? 2. 第二次挥手丢失了,会发生什么? 补充:close …...

下跌多少才能涨回来?
文章目录 上涨下跌函数关系函数图形数学分析 上涨下跌函数关系 最近炒股很热,对于股票来说,有个很重要的参数涨跌幅,那么下跌多少才能涨回来?这个不需要太深的知识就可以计算出来,下跌和上涨不是等价的,下跌…...

【AAOS】【源码分析】CarSystemUI -- CarSystemBar
CarSystemBar不像Android手机那样固定的顶部“状态栏”和底部“导航栏”,而是将StatusBar和NavigationBar都统称为SystemBar,可以通过如下配置为每侧最多配置一个“系统栏”。 packages/apps/Car/SystemUI/res/values/config.xml<!-- Configure which system bars should …...
[供应链] 邀请招标
1.邀请招标定义 邀请招标(Invitation to Bid by Request) 也称为有限竞争性招标(limited Competitive Bidding)或选择性招标(Selected Bidding) 邀请招标的采购方式下,采购人(如政府机构、企业或其他组织)不是公开发布招标信息,而是根据供应商或承包商…...

VS2017+Qt5.12.9+CMake3.30.2编译VTK 9.2.0
一.准备工作 vs2017,QT,Cmake自行下载准备, VTK下载地址 1.官网下载 2.github下载 二.编译VTK源码 1.个人习惯创建以下目录,一个源码目录,Build为vs解决方案输出目录和编译输出以及中间生成文件目录 2.cmake基础…...
Java线程CPU占用过高如何排查?
使用ps命令查看java进程详细信息: ps aux | grep java使用top命令查看系统进程占用情况 top使用jstack命令导出Java进程的堆栈信息 jstack pid | grep tid -A 10 "java.lang.Thread.State" > gc.log找出占用cpu最高的线程id: top -Hp -d 1 …...

uniapp推送配置流程
Dcloud Dcloud注册账号 个推 了解即可 注册个推账号 ios配置流程 需配置含有推送的描述文件以及p8证书 配置推送证书 ios证书配置报技术错误(参数错误) TeamID-苹果开发者账号唯一的ID 安卓需配置多厂商 小米手机需要配置小米厂商 华为手机则需…...

qt QPicture详解
1、概述 QPicture类是Qt框架中的一个重要图形类,它主要用于记录和回放QPainter的绘图指令。这个类能够跨平台、无分辨率依赖地绘制图形,非常适合用于实现打印预览和图像操作等场景。QPicture可以将绘图操作序列化为一种独立于平台的格式,保存…...
ScheduledFuture Source Code Analysis
ScheduledFuture Overview is a delayed result-bearing action, 可以被cancel.通常是在ScheduledExecutorService里面schedule一个task, 然后ScheduledFuture是其task执行接受后的返回结果。 Code Analysis 继承于两个接口: extends Delayed, Future一些继承ch…...

【CSS】CSS 样式重置 (normalize.css 和 reset.css) 和通用样式配置
一般来说,每一个项目初始化阶段都需要样式重置和样式定制化。样式重置最常用的就是 normalize.css 和 reset.css 这两个文件。 他们的区别: Normalize.css更加注重保留有用的浏览器默认样式,仅修复浏览器之间的不一致性,适用于需…...
自动化机器学习(AutoML)详解
自动化机器学习(AutoML)详解 引言 在数据驱动的时代,将庞大的数据集转化为有价值的洞察和预测模型是众多组织的首要任务。然而,传统的机器学习流程复杂且耗时,包括数据预处理、特征选择、模型选择、调参以及模型评估…...
Linux: network:erspan0
文章目录 问题介绍生成时间:代码Linux引入后面NONE是怎么生成的问题 最近看到一个网卡是erspan0,不知道是做什么用的: # ip -d link show erspan0 7: erspan0@NONE: <BROADCAST,MULTICAST> mtu 1450 qdisc noop state DOWN mode DEFAULT group default qlen 10000...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...