当前位置: 首页 > news >正文

旅游订票网站开发wordpress super 缓存

旅游订票网站开发,wordpress super 缓存,淘宝运营是做什么的工作,哈尔滨行业网站这篇文章的问题来源于一个读者之前分享的 OPPO 后端凉经#xff0c;我对比较典型的一些问题进行了分类并给出了详细的参考答案。希望能对正在参加面试的朋友们能够有点帮助#xff01; Java String 为什么是不可变的? public final class String implements java.io.Seri…这篇文章的问题来源于一个读者之前分享的 OPPO 后端凉经我对比较典型的一些问题进行了分类并给出了详细的参考答案。希望能对正在参加面试的朋友们能够有点帮助 Java String 为什么是不可变的? public final class String implements java.io.Serializable, ComparableString, CharSequence {private final char value[];//... }String 真正不可变有下面几点原因 保存字符串的数组被 final 修饰且为私有的并且String 类没有提供/暴露修改这个字符串的方法。String 类被 final 修饰导致其不能被继承进而避免了子类破坏 String 不可变。 在 Java 9 之后String、StringBuilder 与 StringBuffer 的实现改用 byte 数组存储字符串。 public final class String implements java.io.Serializable,ComparableString, CharSequence {// Stable 注解表示变量最多被修改一次称为“稳定的”。Stableprivate final byte[] value; }abstract class AbstractStringBuilder implements Appendable, CharSequence {byte[] value;}新版的 String 其实支持两个编码方案Latin-1 和 UTF-16。如果字符串中包含的汉字没有超过 Latin-1 可表示范围内的字符那就会使用 Latin-1 作为编码方案。Latin-1 编码方案下byte 占一个字节(8 位)char 占用 2 个字节16byte 相较 char 节省一半的内存空间。 JDK 官方就说了绝大部分字符串对象只包含 Latin-1 可表示的字符。 如果字符串中包含的汉字超过 Latin-1 可表示范围内的字符byte 和 char 所占用的空间是一样的。 这是官方的介绍https://openjdk.java.net/jeps/254 。 如何创建线程 一般来说创建线程有很多种方式例如继承Thread类、实现Runnable接口、实现Callable接口、使用线程池、使用CompletableFuture类等等。 不过这些方式其实并没有真正创建出线程。准确点来说这些都属于是在 Java 代码中使用多线程的方法。 严格来说Java 就只有一种方式可以创建线程那就是通过new Thread().start()创建。不管是哪种方式最终还是依赖于new Thread().start()。 关于这个问题的详细分析可以查看这篇文章大家都说 Java 有三种创建线程的方式并发编程中的惊天骗局。 Java 线程的状态有哪几种? Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态 NEW: 初始状态线程被创建出来但没有被调用 start() 。RUNNABLE: 运行状态线程被调用了 start()等待运行的状态。BLOCKED阻塞状态需要等待锁释放。WAITING等待状态表示该线程需要等待其他线程做出一些特定动作通知或中断。TIME_WAITING超时等待状态可以在指定的时间后自行返回而不是像 WAITING 那样一直等待。TERMINATED终止状态表示该线程已经运行完毕。 线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。 Java 线程状态变迁图(图源挑错 |《Java 并发编程的艺术》中关于线程状态的三处错误) 由上图可以看出线程创建之后它将处于 NEW新建 状态调用 start() 方法后开始运行线程这时候处于 READY可运行 状态。可运行状态的线程获得了 CPU 时间片timeslice后就处于 RUNNING运行 状态。 在操作系统层面线程有 READY 和 RUNNING 状态而在 JVM 层面只能看到 RUNNABLE 状态图源HowToDoInJavaJava Thread Life Cycle and Thread States所以 Java 系统一般将这两个状态统称为 RUNNABLE运行中 状态 。 为什么 JVM 没有区分这两种状态呢 摘自Java 线程运行怎么有第六种状态 - Dawell 的回答 现在的时分time-sharing多任务multi-task操作系统架构通常都是用所谓的“时间分片time quantum or time slice”方式进行抢占式preemptive轮转调度round-robin 式。这个时间分片通常是很小的一个线程一次最多只能在 CPU 上运行比如 10-20ms 的时间此时处于 running 状态也即大概只有 0.01 秒这一量级时间片用后就要被切换下来放入调度队列的末尾等待再次调度。也即回到 ready 状态。线程切换的如此之快区分这两种状态就没什么意义了。 当线程执行 wait()方法之后线程进入 WAITING等待 状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态。TIMED_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制比如通过 sleeplong millis方法或 waitlong millis方法可以将线程置于 TIMED_WAITING 状态。当超时时间结束后线程将会返回到 RUNNABLE 状态。当线程进入 synchronized 方法/块或者调用 wait 后被 notify重新进入 synchronized 方法/块但是锁被其它线程占有这个时候线程就会进入 BLOCKED阻塞 状态。线程在执行完了 run()方法之后将会进入到 TERMINATED终止 状态。 相关阅读线程的几种状态你真的了解么 。 为什么要用线程池项目中使用的线程池是使用内置的还是自己创建的 线程池提供了一种限制和管理资源包括执行一个任务的方式。 每个线程池还维护一些基本统计信息例如已完成任务的数量。 这里借用《Java 并发编程的艺术》提到的来说一下使用线程池的好处 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。提高响应速度。当任务到达时任务可以不需要等到线程创建就能立即执行。提高线程的可管理性。线程是稀缺资源如果无限制的创建不仅会消耗系统资源还会降低系统的稳定性使用线程池可以进行统一的分配调优和监控。 《阿里巴巴 Java 开发手册》中强制线程池不允许使用 Executors 去创建而是通过 ThreadPoolExecutor 构造函数的方式这样的处理方式让写的同学更加明确线程池的运行规则规避资源耗尽的风险 Executors 返回线程池对象的弊端如下(后文会详细介绍到) FixedThreadPool 和 SingleThreadExecutor 使用的是无界的 LinkedBlockingQueue任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求从而导致 OOM。CachedThreadPool 使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE 如果任务数量过多且执行速度较慢可能会创建大量的线程从而导致 OOM。ScheduledThreadPool 和 SingleThreadScheduledExecutor : 使用的无界的延迟阻塞队列DelayedWorkQueue任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求从而导致 OOM。 相关阅读 8 个线程池最佳实践和坑使用不当直接生产事故手写一个轻量级动态线程池很香 数据库 如何找到慢 SQL MySQL 慢查询日志是用来记录 MySQL 在执行命令中响应时间超过预设阈值的 SQL 语句。因此通过分析慢查询日志我们就可以找出执行速度比较慢的 SQL 语句。 出于性能层面的考虑慢查询日志功能默认是关闭的你可以通过以下命令开启 # 开启慢查询日志功能 SET GLOBAL slow_query_log ON; # 慢查询日志存放位置 SET GLOBAL slow_query_log_file /var/lib/mysql/ranking-list-slow.log; # 无论是否超时未被索引的记录也会记录下来。 SET GLOBAL log_queries_not_using_indexes ON; # 慢查询阈值秒SQL 执行超过这个阈值将被记录在日志中。 SET SESSION long_query_time 1; # 慢查询仅记录扫描行数大于此参数的 SQL SET SESSION min_examined_row_limit 100;设置成功之后使用 show variables like slow%; 命令进行查看。 | Variable_name | Value | ----------------------------------------------------------- | slow_launch_time | 2 | | slow_query_log | ON | | slow_query_log_file | /var/lib/mysql/ranking-list-slow.log | ----------------------------------------------------------- 3 rows in set (0.01 sec)我们故意在百万数据量的表(未使用索引)中执行一条排序的语句 SELECT score,name FROM cus_order ORDER BY score DESC;确保自己有对应目录的访问权限 chmod 755 /var/lib/mysql/查看对应的慢查询日志 cat /var/lib/mysql/ranking-list-slow.log我们刚刚故意执行的 SQL 语句已经被慢查询日志记录了下来 # Time: 2022-10-09T08:55:37.486797Z # UserHost: root[root] [172.17.0.1] Id: 14 # Query_time: 0.978054 Lock_time: 0.000164 Rows_sent: 999999 Rows_examined: 1999998 SET timestamp1665305736; SELECT score,name FROM cus_order ORDER BY score DESC;这里对日志中的一些信息进行说明 Time 被日志记录的代码在服务器上的运行时间。UserHost谁执行的这段代码。Query_time这段代码运行时长。Lock_time执行这段代码时锁定了多久。Rows_sent慢查询返回的记录。Rows_examined慢查询扫描过的行数。 实际项目中慢查询日志通常会比较复杂我们需要借助一些工具对其进行分析。像 MySQL 内置的 mysqldumpslow 工具就可以把相同的 SQL 归为一类并统计出归类项的执行次数和每次执行的耗时等一系列对应的情况。 如何分析 SQL 性能 我们可以使用 EXPLAIN 命令来分析 SQL 的 执行计划 。执行计划是指一条 SQL 语句在经过 MySQL 查询优化器的优化会后具体的执行方式。 EXPLAIN 并不会真的去执行相关的语句而是通过 查询优化器 对语句进行分析找出最优的查询方案并显示对应的信息。 EXPLAIN 适用于 SELECT, DELETE, INSERT, REPLACE, 和 UPDATE语句我们一般分析 SELECT 查询较多。 我们这里简单来演示一下 EXPLAIN 的使用。 EXPLAIN 的输出格式如下 mysql EXPLAIN SELECT score,name FROM cus_order ORDER BY score DESC; -------------------------------------------------------------------------------------------------------------------- | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | -------------------------------------------------------------------------------------------------------------------- | 1 | SIMPLE | cus_order | NULL | ALL | NULL | NULL | NULL | NULL | 997572 | 100.00 | Using filesort | -------------------------------------------------------------------------------------------------------------------- 1 row in set, 1 warning (0.00 sec)各个字段的含义如下 列名含义idSELECT 查询的序列标识符select_typeSELECT 关键字对应的查询类型table用到的表名partitions匹配的分区对于未分区的表值为 NULLtype表的访问方法possible_keys可能用到的索引key实际用到的索引key_len所选索引的长度ref当使用索引等值查询时与索引作比较的列或常量rows预计要读取的行数filtered按表条件过滤后留存的记录数的百分比Extra附加信息 篇幅问题我这里只是简单介绍了一下 MySQL 执行计划详细介绍请看MySQL 执行计划分析这篇文章。 项目中是怎么使用索引的联合索引了解吗 索引是一种用于快速查询和检索数据的数据结构其本质可以看成是一种排序好的数据结构。 索引的作用就相当于书的目录。打个比方: 我们在查字典的时候如果没有目录那我们就只能一页一页的去找我们需要查的那个字速度很慢。如果有目录了我们只需要先去目录里查找字的位置然后直接翻到那一页就行了。 虽然索引能带来查询上的效率但是维护索引的成本也是不小的。 如果一个字段不被经常查询反而被经常修改那么就更不应该在这种字段上建立索引了。 要选择选择合适的字段创建索引 不为 NULL 的字段索引字段的数据应该尽量不为 NULL因为对于数据为 NULL 的字段数据库较难优化。如果字段频繁被查询但又避免不了为 NULL建议使用 0,1,true,false 这样语义较为清晰的短值或短字符作为替代。被频繁查询的字段我们创建索引的字段应该是查询操作非常频繁的字段。被作为条件查询的字段被作为 WHERE 条件查询的字段应该被考虑建立索引。频繁需要排序的字段索引已经排序这样查询可以利用索引的排序加快排序查询时间。被经常频繁用于连接的字段经常用于连接的字段可能是一些外键列对于外键列并不一定要建立外键只是说该列涉及到表与表的关系。对于频繁被连接查询的字段可以考虑建立索引提高多表连接查询的效率。 使用表中的多个字段创建索引就是 联合索引也叫 组合索引 或 复合索引。 以 score 和 name 两个字段建立联合索引 ALTER TABLE cus_order ADD INDEX id_score_name(score, name);我们应该尽可能的考虑建立联合索引而不是单列索引。因为索引是需要占用磁盘空间的可以简单理解为每个索引都对应着一颗 B树。如果一个表的字段过多索引过多那么当这个表的数据达到一个体量后索引占用的空间也是很多的且修改索引时耗费的时间也是较多的。如果是联合索引多个字段在一个索引上那么将会节约很大磁盘空间且修改数据的操作效率也会提升。 缓存 Redis 提供的数据类型有哪些 Redis 中比较常见的数据类型有下面这些 5 种基础数据类型String字符串、List列表、Set集合、Hash散列、Zset有序集合。3 种特殊数据类型HyperLogLog基数统计、Bitmap 位图、Geospatial (地理位置)。 除了上面提到的之外还有一些其他的比如 Bloom filter布隆过滤器、Bitfield位域。 String 的应用场景有哪些底层实现是什么 String 是 Redis 中最简单同时也是最常用的一个数据类型。它是一种二进制安全的数据类型可以用来存储任何类型的数据比如字符串、整数、浮点数、图片图片的 base64 编码或者解码或者图片的路径、序列化后的对象。 String 的常见应用场景如下 常规数据比如 Session、Token、序列化后的对象、图片的路径的缓存计数比如用户单位时间的请求数简单限流可以用到、页面单位时间的访问数分布式锁(利用 SETNX key value 命令可以实现一个最简易的分布式锁)…… Redis 是基于 C 语言编写的但 Redis 的 String 类型的底层实现并不是 C 语言中的字符串即以空字符 \0 结尾的字符数组而是自己编写了 SDSSimple Dynamic String简单动态字符串 来作为底层实现。 SDS 最早是 Redis 作者为日常 C 语言开发而设计的 C 字符串后来被应用到了 Redis 上并经过了大量的修改完善以适合高性能操作。 Redis7.0 的 SDS 的部分源码如下https://github.com/redis/redis/blob/7.0/src/sds.h: /* Note: sdshdr5 is never used, we just access the flags byte directly.* However is here to document the layout of type 5 SDS strings. */ struct __attribute__ ((__packed__)) sdshdr5 {unsigned char flags; /* 3 lsb of type, and 5 msb of string length */char buf[]; }; struct __attribute__ ((__packed__)) sdshdr8 {uint8_t len; /* used */uint8_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[]; }; struct __attribute__ ((__packed__)) sdshdr16 {uint16_t len; /* used */uint16_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[]; }; struct __attribute__ ((__packed__)) sdshdr32 {uint32_t len; /* used */uint32_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[]; }; struct __attribute__ ((__packed__)) sdshdr64 {uint64_t len; /* used */uint64_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[]; };通过源码可以看出SDS 共有五种实现方式 SDS_TYPE_5并未用到、SDS_TYPE_8、SDS_TYPE_16、SDS_TYPE_32、SDS_TYPE_64其中只有后四种实际用到。Redis 会根据初始化的长度决定使用哪种类型从而减少内存的使用。 类型字节位sdshdr5 18sdshdr818sdshdr16216sdshdr32432sdshdr64864 对于后四种实现都包含了下面这 4 个属性 len字符串的长度也就是已经使用的字节数alloc总共可用的字符空间大小alloc-len 就是 SDS 剩余的空间大小buf[]实际存储字符串的数组flags低三位保存类型标志 SDS 相比于 C 语言中的字符串有如下提升 可以避免缓冲区溢出C 语言中的字符串被修改比如拼接时一旦没有分配足够长度的内存空间就会造成缓冲区溢出。SDS 被修改时会先根据 len 属性检查空间大小是否满足要求如果不满足则先扩展至所需大小再进行修改操作。获取字符串长度的复杂度较低C 语言中的字符串的长度通常是经过遍历计数来实现的时间复杂度为 O(n)。SDS 的长度获取直接读取 len 属性即可时间复杂度为 O(1)。减少内存分配次数为了避免修改增加/减少字符串时每次都需要重新分配内存C 语言的字符串是这样的SDS 实现了空间预分配和惰性空间释放两种优化策略。当 SDS 需要增加字符串时Redis 会为 SDS 分配好内存并且根据特定的算法分配多余的内存这样可以减少连续执行字符串增长操作所需的内存重分配次数。当 SDS 需要减少字符串时这部分内存不会立即被回收会被记录下来等待后续使用支持手动释放有对应的 API。二进制安全C 语言中的字符串以空字符 \0 作为字符串结束的标识这存在一些问题像一些二进制文件比如图片、视频、音频就可能包括空字符C 字符串无法正确保存。SDS 使用 len 属性判断字符串是否结束不存在这个问题。 多提一嘴很多文章里 SDS 的定义是下面这样的 struct sdshdr {unsigned int len;unsigned int free;char buf[]; };这个也没错Redis 3.2 之前就是这样定义的。后来由于这种方式的定义存在问题len 和 free 的定义用了 4 个字节造成了浪费。Redis 3.2 之后Redis 改进了 SDS 的定义将其划分为了现在的 5 种类型。 String 还是 Hash 存储对象数据更好呢 String 存储的是序列化后的对象数据存放的是整个对象。Hash 是对对象的每个字段单独存储可以获取部分字段的信息也可以修改或者添加部分字段节省网络流量。如果对象中某些字段需要经常变动或者经常需要单独查询对象中的个别字段信息Hash 就非常适合。String 存储相对来说更加节省内存缓存相同数量的对象数据String 消耗的内存约是 Hash 的一半。并且存储具有多层嵌套的对象时也方便很多。如果系统对性能和资源消耗非常敏感的话String 就非常适合。 在绝大部分情况我们建议使用 String 来存储对象数据即可 多级缓存的是怎么做的为什么还要再多加一层本地缓存呢 这个问题的答案摘自《Java 面试指北》高质量原创 Java 面试小册 我们这里只来简单聊聊 本地缓存 分布式缓存 的多级缓存方案这也是最常用的多级缓存实现方式。 这个时候估计有很多小伙伴就会问了既然用了分布式缓存为什么还要用本地缓存呢 。 本地缓存和分布式缓存虽然都属于缓存但本地缓存的访问速度要远大于分布式缓存这是因为访问本地缓存不存在额外的网络开销我们在上面也提到了。 不过一般情况下我们也是不建议使用多级缓存的这会增加维护负担比如你需要保证一级缓存和二级缓存的数据一致性。而且其实际带来的提升效果对于绝大部分业务场景来说其实并不是很大。 这里简单总结一下适合多级缓存的两种业务场景 缓存的数据不会频繁修改比较稳定数据访问量特别大比如秒杀场景。 多级缓存方案中第一级缓存L1使用本地内存比如 Caffeine)第二级缓存L2使用分布式缓存比如 Redis。 如果 L2 也没有此数据的话再去数据库查询数据查询成功后再将数据写入到 L1 和 L2 中。 J2Cache 就是一个基于本地内存和分布式缓存的两级 Java 缓存框架感兴趣的同学可以研究一下。 Redis 缓存穿透、缓存击穿、缓存雪崩区别和解决方案 内容较多单独写了一篇文章详细介绍Redis 缓存穿透、缓存击穿、缓存雪崩区别和解决方案 如何保证缓存和数据库数据的一致性 细说的话可以扯很多但是我觉得其实没太大必要小声 BB很多解决方案我也没太弄明白。我个人觉得引入缓存之后如果为了短时间的不一致性问题选择让系统设计变得更加复杂的话完全没必要。 下面单独对 Cache Aside Pattern旁路缓存模式 来聊聊。 Cache Aside Pattern 中遇到写请求是这样的更新 DB然后直接删除 cache 。 如果更新数据库成功而删除缓存这一步失败的情况的话简单说两个解决方案 缓存失效时间变短不推荐治标不治本我们让缓存数据的过期时间变短这样的话缓存就会从数据库中加载数据。另外这种解决办法对于先操作缓存后操作数据库的场景不适用。增加 cache 更新重试机制常用如果 cache 服务当前不可用导致缓存删除失败的话我们就隔一段时间进行重试重试次数可以自己定。如果多次重试还是失败的话我们可以把当前更新失败的 key 存入队列中等缓存服务可用之后再将缓存中对应的 key 删除即可。 详细介绍可以参考这篇文章缓存和数据库一致性问题看这篇就够了 - 水滴与银弹。
http://www.dnsts.com.cn/news/82201.html

相关文章:

  • 个性化定制网站有哪些云南公司网站开发
  • 购物网站建立网站建设套模板下载
  • 郑州市做网站的科技霸权
  • 外贸网站设计制作免费虚拟机下载手机版
  • 用织梦软件如何做网站网站域名怎么查询备案价格
  • 网站内页收录软件开发生命周期
  • 新乡市网站建设有哪些公司网站建设论文大全
  • 建设网站毕业设计适合新手的网站开发
  • 网站排行查询网站dns如何修改不了
  • 网络推广外包公司干什么的网站如何做seo推广方案
  • 重庆做网站开发的公司html代码大全及详解
  • 流程网站html5简单网页源代码
  • 展示型网站举例网站建设怎么
  • 常熟住房和城乡建设局网站属于网站建设过程规划
  • 站内推广的方法和工具做图书馆网站模板
  • 做网站如何添加视频代码演示插件wordpress
  • 哪个网站做logo设计师高端品牌灯具
  • 自己做的网站打开很慢网站建设尺寸大小
  • 江宁交通建设集团网站石碣东莞网站建设
  • 盐城市城乡建设局门户网站seo推广优化外包公司
  • 西安高科鱼化建设有限公司网站网站扩展性
  • 做一个公司网站需要多少钱wordpress自建站邮箱
  • 大连网站开发公司排名公司网络宣传方案
  • 滨州做网站推广免费制作论坛网站模板免费下载
  • 做网站的优惠广告做旅游网站有前途吗
  • 厂房装修东莞网站建设电话营销系统
  • 深圳市网站首页网站建设平台价格
  • 手机套 东莞网站建设军事新闻最新消息军事新闻
  • 风景区介绍网站建设市场分析wordpress 优化加速
  • 用电脑做兼职的网站比较好如何入侵网站服务器