总结了本次面试遇到的值得整理记录的面试题。
目录
变量赋值判断
$a = null;
$b = $a;
$c = false;
$d = $c;
var_dump(isset($a), empty($b), isset($c), empty($d));
结果:
bool(false) bool(true) bool(true) bool(true)
变量+=判断
$a = [0, 1, 2, 3];
$b = [1, 2, 3, 4];
$a += $b;
print_r($a);
结果:
Array ( [0] => 0 [1] => 1 [2] => 2 [3] => 3 )
Foreach使用
$arr = [1, 2, 3, 4];
foreach ($arr as &$V) {
}
foreach ($arr as $v) {
}
print_r($arr);
结果:
Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )
Mysql优化策略
合理的索引设计
使用索引:在常用的查询条件(如WHERE、JOIN、ORDER BY 和 GROUP BY)上创建索引。
复合索引:对多个列的查询条件创建复合索引,以减少索引的数量和提高查询性能。
避免过度索引:过多的索引会增加写入操作的负担,影响性能,因此要合理选择索引。
查询优化
使用 EXPLAIN:在运行查询之前,使用 EXPLAIN 了解查询的执行计划,帮助识别瓶颈。
避免 SELECT *:选择必要的列,减少返回的数据量,提高查询效率。
使用 WHERE 条件:尽量添加 WHERE 条件,减少读取的数据量。
小表驱动大表: 尽量使用小表去关联大表,减少关联数据
数据表结构设计
合理的数据类型:选择合适的数据类型,可以减少存储空间并提高性能。例如,尽量使用 INT 而不是 BIGINT,以及选择最合适的字符串类型。
规范化与反规范化:在设计表结构时根据实际情况考虑规范化(消除冗余)与反规范化(提高查询效率)。
分区表(Partitioning):对于大型表,使用分区可以提高查询效率和管理性能。
大表进行拆分:比如一个大表,里面有很多的text,MEDIUMTEXT,LONGTEXT,这种可以抽取关键字段,然后引用大表的主键ID,方便报表需求开发。
配置优化
调整内存设置:根据应用程序的需求,适当调整 MySQL 的内存配置,如 innodb_buffer_pool_size(对于 InnoDB 存储引擎)来提高性能。
设置查询缓存:对于频繁查询的结果,可以启用查询缓存(虽然在新的 MySQL 版本中,查询缓存已被弃用,需谨慎使用)。
监控和调优服务器参数:定期监控 MySQL 的性能指标,并根据需求调整配置,如连接数、线程数等。
合理使用事务
控制事务的范围:尽量将事务控制在最小的范围内,减少对资源的占用。
使用适当的事务隔离级别:根据需求选择合适的事务隔离级别,以平衡性能与一致性。
IO和HTTP不要在同一个事务之内:这么做是为了避免,事务在回滚的时候,某一操作不可逆。
定期维护数据库
分析和优化表:定期运行 ANALYZE TABLE 和 OPTIMIZE TABLE 来更新统计信息和整理表的碎片。
清理无用数据:定期删除或归档旧数据,保持数据库的有效性和性能。
使用缓存
应用层缓存:使用 Redis 或 Memcached 等缓存系统来减轻数据库的负担,提高响应速度。
数据缓存:将频繁访问的数据缓存在内存中,减少对数据库的直接查询。
监控与性能分析
使用监控工具:借助如 MySQL Enterprise Monitor、Percona Toolkit 等工具监控数据库性能。
慢查询日志:启用慢查询日志以识别性能瓶颈,并对相关查询进行优化。
Redis主从复制
介绍
Redis主从复制是一个多Redis实例进行数据同步的过程,其中一个实例是主实例(Master),其他实例是从实例(Slave)。主实例负责处理命令请求,而从实例则 periodically 地从主实例拉取数据副本。
配置
要配置Redis主从复制,需要在从实例的配置文件中设置 slaveof 指令,指向主实例的IP和端口。
例如,假设主实例运行在IP 192.168.1.100 的6379端口上,可以在从实例的配置文件中添加如下行:
slaveof 192.168.1.100 6379
或者,你也可以在从实例启动时通过命令行参数设置:
redis-server --slaveof 192.168.1.100 6379
当配置生效后,从实例会连接到主实例,并开始接收数据。如果主实例发生故障,从实例可以配置为自动进行故障转移,这需要设置 slave-serve-stale-data 为 yes 并启用 slave-read-only 选项。
示例
以下是一个简单的例子,展示如何在Redis配置文件中启用主从复制:
# 主实例的配置文件(无需更改)
# 从实例的配置文件
slaveof 192.168.1.100 6379
slave-serve-stale-data yes
slave-read-only yes
记得在修改配置后重启Redis实例以使配置生效。
Redis 数据类型及应用
string
常用命令
除了get、set、incr、decr mget等操作外,Redis还提供了下面一些操作:
获取字符串长度
往字符串append内容
设置和获取字符串的某一段内容
设置及获取字符串的某一位(bit)
批量设置一系列字符串的内容
应用场景
String是最常用的一种数据类型,普通的key/value存储都可以归为此类,value其实不仅是String,
也可以是数字:比如想知道什么时候封锁一个IP地址(访问超过几次)。INCRBY命令让这些变得很容易,通过原子递增保持计数。
hash
常用命令
hget,hset,hgetall 等。
应用场景
比如我们要存储一个用户信息对象数据,包含以下信息:
用户ID,为查找的key,
存储的value用户对象包含姓名name,年龄age,生日birthday 等信息
list
常用命令
lpush,rpush,lpop,rpop,lrange,BLPOP(阻塞版)等。
应用场景
可以轻松地实现最新消息排行等功能。
List的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。
set
常用命令
sadd,srem,spop,sdiff ,smembers,sunion 等。
应用场景
Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的。set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
比如在微博应用中,每个人的好友存在一个集合(set)中,这样求两个人的共同好友的操作,可能就只需要用求交集命令即可。
Redis还为集合提供了求交集、并集、差集等操作
zset
常用命令
zadd,zrange,zrem,zcard等
应用场景
以某个条件为权重,比如按顶的次数排序.
ZREVRANGE命令可以用来按照得分来获取前100名的用户,ZRANK可以用来获取用户排名,非常直接而且操作容易。
Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。
比如:全班同学成绩,value可以是同学的学号,而score就可以是其考试得分,这样数据插入集合的,就已经进行了天然的排序。
Mysql 事务级别
在 MySQL中事务的隔离级别有以下 4 种:
读未提交(READ UNCOMMITTED)
读已提交(READ COMMITTED)
可重复读(REPEATABLE READ)
序列化(SERIALIZABLE)
MySQL 默认的事务隔离级别是可重复读,这4种隔离级别的说明如下。
1.READ UNCOMMITTED
读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发生回滚,因此把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
2.READ COMMITTED
读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL查询中,可能会得到不同的结果,这种现象叫做不可重复读。
3.REPEATABLE READ
可重复读,是MySQL的默认事务隔离级别,它能确保同一事务多次查询的结果一致。但也会有新的问题,比如此级别的事务正在执行时,另一个事务成功的插入了某条数据,但因为它每次查询的结果都是一样的,所以会导致查询不到这条数据,自己重复插入时又失败(因为唯一约束的原因)。明明在事务中查询不到这条信息,但就是插入不进去,这就叫幻读 (Phantom Read)。
4.SERIALIZABLE
序列化,事务最高隔离级别,它会强制事务排序,使之不会发生冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。
大数据量统计
一亿条订单数据属于1000个商户,取出订单量最多的前10家商户
如果直接使用一条sql来查出来,
无疑是非常慢并且损耗系统性能的,
设置数据库可能会崩溃。
可以分为两步来执行。
下面介绍实现思路:
首先数据表增加字段
在订单表中增加一个是否处理的标识,
在商户表中增加一个订单量字段用来存储该商户的下单量;
第一步分批次的每次拿一定订单数据,统计后更新相应商户下单量,并标识订单已被处理;最后把所有现有订单处理统计完成。
第二步在下单时维护一个商户下单量字段,
这样最后统计商户订单量前10只需要:
select id,name, order_num from member order by order_num desc limit 10;
即可达到效果。
Mysql 锁
行级锁
表级锁
主键锁
主键锁是为了保护表中具体的一条记录,当对表中的一条记录进行更新(UPDATE)、删除(DELETE)或者插入(INSERT)操作时,MySQL会自动对这条记录加锁
间隙锁
当使用范围当条件进行修改时,会把范围中的符合条件的数据条数进行锁定,还会对一部分可能会修改的数据进行锁定,这部分就是间隙锁。