互联网应用主流框架整合之Redis基础

news2024/12/27 12:16:28

Redis简介

在传统的Java Web项目中存储数据,主要是用关系型数据库,如MySQL、SqlServer、Oracle等等,这些数据库的数据持久化在磁盘上,而磁盘的读写速度比较慢,而一般的管理系统上又不存在瞬间的高并发场景,没有瞬间的读写大量数据的要求,这个情况使用关系型数据库,把数据持久化在磁盘上没什么太大问题,但在互联网应用中瞬间的高并发和瞬间读写大量数据的场景比较多,就不是关系型数据库所能够承受的了,因此Java Web引入了NoSQL技术,它也是一种数据库,但基于内存而非磁盘,也提供了持久化的功能,因此读写速度远大于关系型数据,然而基于内存的数据库成本相对关系型数据库而言非常高,因此什么数据使用NoSQL,什么数据使用关系型数据库要进行清晰的规划才能得到更好地投入产出比

Redis就是当前比较广泛的NoSQL,其性能十分优越,可以支持每秒十几万次的读写操作,性能远超关系型数据库,并且支持集群、分布式、主从同步等配置,若不考虑成本,理论上可以无限扩展,让更多的数据存储在内存中,并且Redis还支持事务,这对高并发场景下的数据一致性特别重要

Redis的性能优越主要源于如下几个因素:

  • 它是基于ANSI C语言编写的,ANSI C语言是更接近于汇编语言的机器语言,运行速度非常快
  • 内存存储:Redis将所有数据存储在内存中,避免了磁盘I/O的延迟,使得数据访问速度极快。内存读写速度远远高于硬盘,这是Redis能够实现高速响应的基础
  • 单线程模型:Redis使用单线程来处理客户端的所有请求,避免了多线程切换上下文的开销。虽然单线程限制了它不能并行处理多个请求,但在没有锁竞争的情况下,它的处理效率非常高,尤其适合于执行大量简单命令的场景
  • 数据结构优化:Redis不仅仅是一个键值存储系统,它还支持多种数据结构如字符串、哈希、列表、集合、有序集合等,这些数据结构都经过精心设计和优化,能够在执行特定操作时达到很高的性能
  • I/O多路复用:Redis使用I/O多路复用技术(如epoll on Linux),可以同时监听多个套接字,当任何一个套接字准备好进行读写操作时,Redis就可以进行处理,这大大提高了处理并发连接的效率,无需为每个连接创建一个单独的线程
  • 持久化策略:Redis提供了RDB(快照)和AOF(追加文件)两种持久化方式,用户可以根据实际需求选择合适的策略,在保证数据安全的同时,尽量减少对性能的影响
  • 缓存淘汰策略:当内存不足时,Redis通过配置的缓存淘汰策略(如LRU、LFU等)自动移除不常访问的数据,确保热点数据始终被保留,提升访问效率

综上所述,Redis结合了内存存储、高效的单线程模型、优化的数据结构、I/O多路复用技术以及灵活的持久化和缓存淘汰策略,共同造就了其高速的数据处理能力,是关系型数据库的几倍到几十倍

然而缺点也比较明显,例如数据存储在内存中部分持久化在磁盘,持久化能力有限,因此是不完全安全的,遇上机器故障数据容易丢失,此外NoSQL的数据完整性、事务能力、可靠性及可扩展性都远不及关系型数据库,且也不支持复杂的计算远不如关系型数据库的SQL语句强大

Redis数据结构简介

Redis是一种基于内存的数据库,并且提供一定的持久化功能,它是一种键值数据库,使用key作为索引找到当前缓存的数据,并且返回给调用者,当前的Redis支持多种数据结构,例如字符串、列表、集合、哈希、有序集合、基数、数据流和地理空间索引等

使用Redis编程要了解其支持的数据结构及相关命令,此外这些数据结构,Redis不但提供了存储功能,还能对存储的数据进行计算,例如字符串支持浮点数的自增、自减、字符求子串;集合可以求并集、交集;有序集合可以排序等等

如下表所示是常用的Redis数据结构
在这里插入图片描述

Redis和关系型数据库的差异

使用Redis的常见场景

  • 缓存:在对数据的读写操作中,实际上读操作的次数远超过写操作,通常会超过70%都是读操作,当发送SQL读取数据时,数据库会去磁盘把对应的数据索引回来,而索引磁盘是一个相对缓慢的过程;如果使用Redis则不需要读写磁盘,运行在内存中速度显然会快很多

    而磁盘的容量可以是TB级别十分廉价,而内存的容量一般达到几百GB就相当不错了,因此在使用Redis时,需要有条件的存储,通常要考虑数据是否常用、数据是否读操作较多、数据大小如何,通常情况下是用Redis做一级缓存,读操作直接读Redis,如果不存在则读数据库,拿到数据后写入Redis,并返回给前端,第二次再读的时候还是先读Redis,这样访问速度就大大提高了,且减轻了关系型数据库的负担,而写入操作则同步更新关系型数据和Redis即可

  • 高速读写:这种场景往往是异步写入关系型数据库,首先将热点数据事先存放到Redis,当高速读写完成后再将满足条件的数据向关系型数据库同步,这种场景往往是秒杀或者抢红包之类的场景,当商品数量为0或者红包金额为0或者请求超时时,则将Redis的数据向关系型数据库批量同步更新,从而完成持久化的工作,一般缓存不做持久化

在实际开发中更复杂一些,还需要考虑数据安全、数据一致性、限制流量、有效请求、无效请求、事务一致性等等维度

Redis的Java API

在Java中可以简易的使用Redis,也可以通过Spring的RedisTemplate使用Redis,还可以SpringBoot的注解方式使用Redis

Java中简单使用Redis

首先需要引入如下依赖

    <!-- 引入Jedis依赖,用于Java操作Redis -->
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>3.2.0</version>
    </dependency>

然后编写简单的测试代码如下所示

package com.sr.test;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class TestJedis {

    public static void main(String[] args) {
        testJedis();
        testPool();
    }

    public static void testJedis() {
        // 连接Redis
        Jedis jedis = new Jedis("192.168.3.115", 6379);
        // jedis.auth("Ms123!@#");
        int i = 0; // 记录操作次数
        try {
            // 开始毫秒数
            long start = System.currentTimeMillis();
            while (true) {
                long end = System.currentTimeMillis();
                // 当大于等于1000毫秒(相当于1秒)时,结束操作
                if (end - start >= 1000) {
                    break;
                }
                i++;
                jedis.set("test" + i, i + "");
            }
        } finally {// 关闭连接
            jedis.close();
        }
        // 打印1秒内对Redis的操作次数
        System.out.println("redis每秒操作:" + i + "次");
    }

    public static void testPool() {
        JedisPoolConfig poolCfg = new JedisPoolConfig();
        // 最大空闲数
        poolCfg.setMaxIdle(50);
        // 最大连接数
        poolCfg.setMaxTotal(100);
        // 最大等待毫秒数
        poolCfg.setMaxWaitMillis(20000);
        // 使用配置创建连接池
        JedisPool pool = new JedisPool(poolCfg, "192.168.3.115");
        Jedis jedis = null;
        try {
            // 从连接池中获取单个连接
            jedis = pool.getResource();
            // 设置密码
            // jedis.auth("Ms123!@#");
            jedis.set("pool_key1", "pool_value1");
        } finally {// 关闭连接
            jedis.close();
        }
        // 关闭连接池
        pool.close();
    }
}

执行代码,控制台打印如下内容

....
redis每秒操作:33011次

Process finished with exit code 0

然后检查一下Redis,写入了(pool_key1", "pool_value1),代码中使用了两种链接redis的方式,第一种是用了一个独立的链接,第二种使用了连接池来管理链接,Redis的连接池提供了类redis.clients.jedis.JedisPool来创建Redis的连接池对象,然后使用redis.clients.jedis.JedisPoolConfig类对连接池进行配置

在Spring使用Redis

由于Redis只能提供基于字符串的操作,而Java以使用类对象为主,所以需要Redis存储的字符串和Java对象相互转换,例如一个角色对象,没办法直接存到Redis里需要进一步转换,Spring提供了序列器,通过这个序列器Spring可以将对象转换成字符串,Redis可以将其存起来,并且在读取的时候,通过反序列化再将字符串转换为Java对象,在Spring中使用Redis,需要引入如下依赖

		<!-- Spring Data Redis -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>2.2.4.RELEASE</version>
		</dependency>
		<!-- Jedis -->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>3.2.0</version>
		</dependency>

这样就可以使用spring-data-redis操作Redis了,spring-data-redis内部提供了RedisTemplate,它通过连接工厂RedisConnectionFactory获取Redis连接。

首先要配置Redis的连接池,如下所示

package com.sr.config;

/****import****/

@Configuration
public class RedisConfig {

	@Bean("redisPoolConfig")
	public JedisPoolConfig poolConfig() {
		JedisPoolConfig poolCfg = new JedisPoolConfig();
		// 最大空闲数
		poolCfg.setMaxIdle(50);
		// 最大连接数
		poolCfg.setMaxTotal(100);
		// 最大等待毫秒数
		poolCfg.setMaxWaitMillis(20000);
		return poolCfg;
	}

有了连接池之后,就需要配置Redis的连接工厂,接口是RedisConnectionFactory,他有两个比较重要的实现类
在这里插入图片描述
然后在RedisConfig类下装配一个JedisConnectionFactory对象,代码如下所示

    /**
     * 创建Jedis连接工厂
     * @param jedisPoolConfig
     * @return 连接工厂
     */
    @Bean("redisConnectionFactory")
    public RedisConnectionFactory redisConnectionFactory(@Autowired JedisPoolConfig jedisPoolConfig) {
        // 独立Jedis配置
        RedisStandaloneConfiguration rsc = new RedisStandaloneConfiguration();
        // 设置Redis服务器
        rsc.setHostName("192.168.80.128");
        // 如需要密码,设置密码
        rsc.setPassword("abcdefg");
        // 端口
        rsc.setPort(6379);
        // 获得默认的连接池构造器
        JedisClientConfigurationBuilder jpcb = JedisClientConfiguration.builder();
        // 设置Redis连接池
        jpcb.usePooling().poolConfig(jedisPoolConfig);
        // 获取构建器
        JedisClientConfiguration jedisClientConfiguration = jpcb.build();
        // 创建连接工厂
        return new JedisConnectionFactory(rsc, jedisClientConfiguration);
    }

这样就完成了一个RedisConnectionFactory的配置,这里配置的是JedisConnectionFactory,如果需要LettuceConnectionFactory,可以将代码中JedisConnectionFactory改为LettuceConnectionFactory,并且引入相关依赖即可

有了RedisConnectionFactory,就可以开始配置RedisTemplate<K,V>了,普通的连接没办法把Java对象直接存入Redis,需要开发来提供方案,通常是将对象序列化,然后使用Redis序列化存储对象,取回时,再通过反序列化将之前存储的内容转换为Java对象接口;RedisTemplate<K,V>中提供了封装方案,从RedisTemplate的定义中不难发现,它存在K和V两个泛型,因为Redis是一个Key-value数据库,需要将key和value序列化后才能存储,而为了便于序列化,Spring内部提供了RedisSerializer<T>接口和一些实现类来完成key和value与java对象之间序列化和反序列化的工作;
在这里插入图片描述
以下是RedisSerializer<T>接口实现类说明:

  • StringRedisSerializer 将Java对象序列化为字符串。这通常用于序列化字符串类型的键或值,是最基本的序列化方式之一。
  • JdkSerializationRedisSerializer 使用Java的序列化机制,即将对象转换为字节数组。这适用于任何实现了Serializable接口的Java对象,但是序列化后的结果可能较大,且不跨语言。
  • OxmSerializer 基于Spring的OXM(Object/XML Mappers)模块,可以将对象序列化为XML或其他格式。这在需要XML格式数据时很有用。
  • Jackson2JsonRedisSerializer 使用Jackson库将对象序列化为JSON格式。这提供了良好的可读性和跨语言兼容性,同时支持复杂的数据结构。
  • GenericJackson2JsonRedisSerializer 类似于Jackson2JsonRedisSerializer,但是更通用,可以序列化更广泛的Java类型到JSON。
  • GenericTostringSerializer 将对象转换为其toString()方法的结果,然后使用StringRedisSerializer序列化。这通常用于调试目的,而不是生产环境。
  • ByteArrayRedisSerializer 直接将对象视为字节数组进行序列化和反序列化,适用于原始二进制数据。

在Spring Data Redis中,你可以通过配置RedisTemplateStringRedisTemplatesetKeySerializer, setValueSerializer, setHashKeySerializer, 和 setHashValueSerializer方法来指定不同的序列化器,从而控制如何序列化和反序列化键和值。

这些序列化器的选择通常基于性能需求、数据类型、跨语言兼容性以及是否需要人类可读等因素。例如,如果性能是关键因素,你可能会选择ByteArrayRedisSerializerGenericJackson2JsonRedisSerializer,而如果数据主要是字符串,那么StringRedisSerializer可能是最合适的选择

Spring提供的RedisTemplate有几个关于设置序列化器的关键属性如下

    @Nullable
    private RedisSerializer<?> defaultSerializer;
    @Nullable
    private RedisSerializer keySerializer = null;
    @Nullable
    private RedisSerializer valueSerializer = null;
    @Nullable
    private RedisSerializer hashKeySerializer = null;
    @Nullable
    private RedisSerializer hashValueSerializer = null;
	/**
	 * 创建RedisTemplate 
	 * @param connectionFactory Redis连接工厂
	 * @return RedisTemplate对象
	 */
	@Bean("redisTemplate")
	public RedisTemplate<String, Object> redisTemplate(@Autowired RedisConnectionFactory connectionFactory) {
		// 创建RedisTemplate
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		// 字符串和JDK序列化器
		StringRedisSerializer strSerializer = new StringRedisSerializer();
		JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();
		// 设置键值序列化器
		redisTemplate.setKeySerializer(strSerializer);
		redisTemplate.setValueSerializer(jdkSerializer);
		// 设置哈希字段和值序列化器
		redisTemplate.setHashKeySerializer(strSerializer);
		redisTemplate.setHashValueSerializer(jdkSerializer);
		// 给RedisTemplate设置连接工厂
		redisTemplate.setConnectionFactory(connectionFactory);
		return redisTemplate;
	}

代码中使用StringRedisSerializer作为Redis的key的序列化器,使用JdkSerializationRedisSerializer 作为value的序列化器,这样就配置了一个RedisTemplate对象,然后spring-data-redis就知道用对应的序列化器转换Redis不同类型数据的键值了;更多的时候会使用字符串,字符串不但可读性高且Redis最基本的数据类型也是字符串,Spring提供了StringRedisTemplate对象,它继承了RedisTemplate,可以认为StringRedisTemplate对象是一个键值为String泛型的RedisTemplate专门针对字符串进行操作

	/**
	 * 创建StringRedisTemplate
	 * @param connectionFactory 连接工厂
	 * @return StringRedisTemplate对象
	 */
	@Bean("stringRedisTemplate")
	public StringRedisTemplate  stringRedisTemplate(@Autowired RedisConnectionFactory connectionFactory) {
		// 创建StringRedisTemplate对象
		StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
		// 设置连接工厂
		stringRedisTemplate.setConnectionFactory(connectionFactory);
		return stringRedisTemplate;
	}

为了测试上述代码是否生效,先建一个可序列化的角色对象,代码如下

package com.sr.pojo;

import java.io.Serializable;

/**
 * 角色类,表示系统中的一个角色
 * 实现Serializable接口,使得角色对象可以被序列化
 */
public class Role implements Serializable {

    // 序列化版本ID,用于保证序列化兼容性
    private static final long serialVersionUID = 6977402643848374753L;

    // 角色ID,唯一标识一个角色
    private Long id;
    // 角色名称,表示角色的名称
    private String roleName;
    // 角色描述,对角色的详细描述
    private String note;

    /**
     * 默认构造函数,用于创建一个新的角色实例
     */
    public Role() {
    }

    /**
     * 构造函数,用于创建一个带有指定属性的角色实例
     * @param id    角色的ID
     * @param roleName 角色的名称
     * @param note  角色的描述
     */
    public Role(Long id, String roleName, String note) {
        this.id = id;
        this.roleName = roleName;
        this.note = note;
    }
    
    /**
     * 获取角色ID
     * @return 角色的ID
     */
    public Long getId() {
        return id;
    }

    /**
     * 设置角色ID
     * @param id 角色的ID
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * 获取角色名称
     * @return 角色的名称
     */
    public String getRoleName() {
        return roleName;
    }

    /**
     * 设置角色名称
     * @param roleName 角色的名称
     */
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    /**
     * 获取角色描述
     * @return 角色的描述
     */
    public String getNote() {
        return note;
    }

    /**
     * 设置角色描述
     * @param note 角色的描述
     */
    public void setNote(String note) {
        this.note = note;
    }

}

要序列化对象,它必须实现Serializable接口,这样这个对象才能被序列化,serialVersionUID代表序列化的版本编号

接下来就可以测试对象的序列化和反序列化,代码如下

    public static void testRedisTemplate() {
        // 创建IoC容器
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(RedisConfig.class);
        // 获取RedisTemplate,注意StringRedisTemplate是其子类,因此只能通过名称获取
        RedisTemplate<String, Object> redisTemplate = ctx.getBean("redisTemplate", RedisTemplate.class);
        // 创建Role对象
        Role role = new Role(1L, "role_name_1", "note_1");
        // 让Redis服务器存放对象
        redisTemplate.opsForValue().set("role-1", role);
        // 获取对象
        Role role1 = (Role) redisTemplate.opsForValue().get("role-1");
        System.out.println(role1.getRoleName());
        // 获取StringRedisTemplate
        StringRedisTemplate stringRedisTemplate = ctx.getBean(StringRedisTemplate.class);
        // 对Redis服务器的String的键值操作
        stringRedisTemplate.opsForValue().set("template-1", "value-1");
        String value = stringRedisTemplate.opsForValue().get("template-1");
        System.out.println(value);
    }

然后检查Redis里的数据,如下图所示
在这里插入图片描述
然而这里边有个问题,就是我们使用连接池来管理链接,并不能保证每次使用RedisTemplate都是同一个操作同一个Redis链接,比如如下代码

        // 让Redis服务器存放对象
        redisTemplate.opsForValue().set("role-1", role);
        // 获取对象
        Role role1 = (Role) redisTemplate.opsForValue().get("role-1");

代码很简单,但是在内部他们是同一个连接池的不同链接,为了使所有操作都来自同一个链接,可以使用SessionCallback或者RedisCallback接口,其中RedisCallback是底层的封装,SessionCallback是相对高级的封装接口使用起来更为友好,通过这两个接口之一就可以把多个命令放入同一个Redis连接中执行,这样对于资源的损耗更小,如下代码所示


    public static void testRedisTemplate2() {
        // 创建IoC容器
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(RedisConfig.class);
        // 获取RedisTemplate,注意StringRedisTemplate是其子类,因此只能通过名称获取
        RedisTemplate<String, Object> redisTemplate = ctx.getBean("redisTemplate", RedisTemplate.class);
        // Lambda表达式创建SessionCallback
        SessionCallback callBack1 = new SessionCallback() {
            @Override
            public Object execute(RedisOperations ops) throws DataAccessException {
                // 创建Role对象
                Role role = new Role(1L, "role_name_1", "note_1");
                ops.boundValueOps("role-1").set(role);
                Role role1 = (Role) ops.boundValueOps("role-1").get();
                return role1;
            }
        };
        redisTemplate.execute(callBack1);
        // 创建Role对象
        Role role = new Role(1L, "role_name_1", "note_1");
        // 让Redis服务器存放对象
        redisTemplate.opsForValue().set("role-1", role);
        // 获取对象
        Role role1 = (Role) redisTemplate.opsForValue().get("role-1");
        System.out.println(role1.getRoleName());
        // 获取StringRedisTemplate
        StringRedisTemplate stringRedisTemplate
                = ctx.getBean(StringRedisTemplate.class);
        // Lambda表达式创建SessionCallback
        SessionCallback callBack2 = new SessionCallback() {
            @Override
            public Object execute(RedisOperations ops) throws DataAccessException {
                ops.boundValueOps("template-1").set("value-1");
                String value1 = (String) ops.boundValueOps("template-1").get();
                return value1;
            }
        };
        String value = (String) stringRedisTemplate.execute(callBack2);
        System.out.println(value);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1988027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

英语疑惑之在树上

在树上&#xff0c;on the tree&#xff0c;我想这个这个介词到底该用in&#xff0c;on or other prep。本来我以为跟on the roof差不多&#xff0c;就是在物体表面&#xff0c;可是百度了一下&#xff0c;可以有on the tree, in the tree, by the tree, at the tree, under th…

vs+qt项目转qt creator

1、转换方法 打开vs工程&#xff0c;右键项目&#xff0c;Qt->Create Base .pro File 后面默认OK 如果工程有include和lib路径需要配置&#xff0c;则转换后的工程&#xff0c;需要修改pro文件 2.修改pro文件 例如转换后的工程如下&#xff1a; 修改后 # ------------…

掌握 R 软件在 Windows 及 Mac 上的下载安装流程

临床数据科学是一门综合利用统计学、数据挖掘、机器学习和信息技术等方法&#xff0c;对临床数据进行分析和解释的学科。它的目标是从海量的临床数据中挖掘出有价值的信息&#xff0c;以支持医疗决策&#xff0c;提高医疗质量&#xff0c;降低医疗成本&#xff0c;并促进医学研…

springboot高校无人车配送系统-计算机毕业设计源码90207

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1系统开发流程 2.2.2 用户登录流程 2.2.3 系统操作流程 2.2.4 添加信息流程 2.2.5 修改信息流程 2.2.6 删除信息流程 2.3 系统功能…

云计算专业创新人才培养体系的探索与实践

一、引言 近年来&#xff0c;云计算技术凭借其高效、灵活、可扩展等优势&#xff0c;在各行各业得到广泛应用。为满足社会对云计算人才的需求&#xff0c;职业院校纷纷开设云计算相关专业&#xff0c;并积极探索创新人才培养体系。本文基于职业院校的特点&#xff0c;构建了“…

【wsl】wsl + vscode 中使用 typora 打开 markdown 文件

vscode 连接好wsl 使用Open in External App 一个五星好评的插件Open in External App则可以在vscode中用typora打开md文件&#xff0c;不仅如此&#xff0c;还有设定其他应用打开相应的文件&#xff0c;比如chrome打开html。插件食用方法也比较简单&#xff0c;安装后&#…

Stable Diffusion绘画 | 图生图-涂鸦

涂鸦的参数与「图生图」Tab 是完全一致的&#xff0c;只是没有蒙版的设置。 它不再局限在某一块小区域的蒙版修改&#xff0c;而是对整个画面进行修改。 可以将它理解成一个能够用画笔随意绘画的图生图功能。 实操 在图片中&#xff0c;用画笔画一个眼镜形状&#xff0c;正…

土壤中微生物碳氮磷含量测定

微生物量碳(MBC) 指土壤中体积小于5000μm的活的和死的微生物体内碳的总和。它是土壤碳库中的一个重要组成部分&#xff0c;虽然占比较小&#xff0c;但对土壤有效养分和生物化学转化过程具有重要影响。 微生物量氮(MBN) 指土壤中体积小于5000μm的活的和死的生物体(不含活体…

完美主义 —— 软件工程生产力最大的阻力

此文大部分内容来自翻译&#xff0c;原地址在文末 简介 在毫无意识的情况下&#xff0c;完美主义常常让我们遭受损失。我们可能认为花这么多精力来确保结果是 “完美的” 感觉很棒的&#xff0c;但最终&#xff0c;我们永远不会达成完美&#xff0c;而我们的工作会变成无休止的…

架构DBA

写一篇很费心力,大家觉得好,就点下广告支持下吧! 如何选择国产数据库?再论国产数据库的选择DBA搞钱之路 做为一名中年DBA来说,从2002年毕业来,享受国家经济高速发展的时期,路过2008年经济危机,错过了房地产致富之路! 焦虑与不安伴随多年,不是几年,而是十几年! 小仙我,不是名…

制造知识普及(九)--企业内部物料编码(IPN)与制造商物料编码(MPN)

在日常的物料管理业务逻辑中&#xff0c;一物一码是物料管理的基本的业务规则&#xff0c;不管物料从产品开发还是仓库管理&#xff0c;甚至成本核算&#xff0c;都要遵循这个原则&#xff0c;才能保证产品数据的准确性&#xff0c;才具备唯一追溯的可行性。大部分企业都是这种…

Android进阶之路 - 字体加粗,定制化字体粗度

在客户端中不论是PC端&#xff0c;还是移动端主要价值之一就体现在用户交互方面&#xff0c;也就是用户体验了&#xff0c;接下来讲的是很常见的字体加粗问题 UI大找茬 深入浅出字体、字体库TextView文本渐变字体阴影、文字阴影字体加粗 - 定制化字体粗度 在开发中经常会遇到…

【SQL Server】端口安全配置:SQL Server的安全最佳实践与防火墙规则配置

目录 SQL Server 安全最佳实践 防火墙规则配置 示例&#xff1a;配置 Windows 防火墙规则 加密连接 示例代码&#xff1a;使用加密连接 安全注意事项 SQL Server 安全最佳实践 在进行任何网络或数据库系统的部署时&#xff0c;确保安全是至关重要的。SQL Server&#xf…

语法检测在线英语怎么做?5个检测技巧教会你

相信大家平时在撰写文章、报告或是任何形式的书面材料时&#xff0c;都会发现语法错误会很大程度地影响信息的传达和读者的理解。尤其是对于非母语的写作者来说&#xff0c;语法问题更是棘手。 但得益于语法检测在线免费工具的出现&#xff0c;现在可就再也不需要为语法错误而…

10款超好用的图纸加密软件推荐,2024企业常用图纸加密软件分享

在现代企业中&#xff0c;设计图纸和敏感数据的安全性至关重要。一旦图纸泄露&#xff0c;可能会对企业造成不可估量的损失。因此&#xff0c;选择一款高效、可靠的图纸加密软件显得尤为重要。 1. 安秉图纸加密软件 安秉图纸加密软件是一款专为保护工程图纸和设计文件安全的软…

【youcans动手学模型】YOLO5 (上)环境配置与测试

欢迎关注『youcans动手学模型』系列 本专栏内容和资源同步到 GitHub/youcans 【youcans动手学模型】YOLO5 &#xff08;上&#xff09;环境配置与测试 1. YOLO 简介2. YOLOv5 简介3. YOLOv5 下载4. 配置 PyCharm 环境5. 运行 YOLOv5 推理 1. YOLO 简介 YOLO (You Only Look On…

【溯源反制】反制攻击者-蚁剑低版本

目录 安装 攻击者获取防守方的权限 防守方反制攻击者 防守方获取攻击者的shell权限 安装 安装蚁剑2.0.7版本 链接&#xff1a;https://pan.baidu.com/s/1t40UxkZ2XuSWG6VCdGzvDw?pwd8888 提取码&#xff1a;8888 下载好后先打开Loader文件夹下的.exe文件&#xff0c;打…

2.如何定制 Dcat-admin list 中显示的信息

操作在xxxController.php文件中的 protected function grid() 中进行 定制显示的内容 protected function grid(){return Grid::make(new Robot(), function (Grid $grid) {// ->sortable() 新增一个排序的按钮在列标题中$grid->column(id)->sortable();// 正常字符…

STM32-OTA升级

一、OTA&#xff08;Over-The-Air&#xff09; OTA&#xff08;Over-The-Air&#xff09;是一种通过无线通信方式&#xff0c;为设备分发新软件、配置甚至更新加密密钥的技术。它允许中心位置向所有用户发送更新&#xff0c;确保每个接收者都无法拒绝、破坏或改变这些更新&…

C# Unity 面向对象补全计划 设计者模式 之 单例模式

本文仅作学习笔记与交流&#xff0c;不作任何商业用途&#xff0c;作者能力有限&#xff0c;如有不足还请斧正 本系列作为七大原则和设计模式的进阶知识&#xff0c;看不懂没关系 了解我的专栏C#面向对象与进阶:http://t.csdnimg.cn/mIitr&#xff0c;尤其是关于类的那篇文章即…