0%

我们知道在 redis 中,有一个排他锁,set ... nx,但是这个锁有一个问题是,有可能造成所有请求阻塞在等待这个锁上。

如果是允许同时执行的,比如秒杀,是可以有多个请求成功的,那么可以尝试一下 redis 的乐观锁。

如何使用

  1. 利用 redis 的 watch 功能,监控这个 redis key 的状态值。
  2. 获取 redis key 的值
  3. 创建 redis 事务
  4. 修改这个 key 的值
  5. 执行这个事务,如果 key 的值被修改过则回滚,key 不修改。(如果没修改过,则执行成功)

示例

事务执行

1
2
3
4
5
6
7
8
9
10
➜  ~ redis-cli
127.0.0.1:6379> watch a
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set a 12
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
127.0.0.1:6379>

如果事务执行成功,exec 会返回 OK,如果执行失败,则会返回 nil。我们可以根据这个返回值来判断事务是否执行成功。

在使用 socket api 开发的时候,可能经常会遇到一个问题,停止 socket 服务器之后立即重启的时候,会提示端口被占用。

这是因为一般情况下,一个端口释放后得等待一会才能再被使用,而 SO_REUSEADDR 可以让端口释放后立即就可以被再次使用。

这样以来,调试的时候就不用担心停止 server 之后再启动就启动不了了。

  • 给排序字段加索引

需要注意:联合索引里面,一个字段的顺序和逆序是不一样的索引。比如

1
db.coll.createIndex({"a": 1, "b": 1})

1
db.coll.createIndex({"a": 1, "b": -1})

是两个不一样的索引

  • 使用 batchSize 选项批量获取数据

jenssegers mongodb 包默认情况下会获取 101 条记录,然后其他记录在底层通过 iterator_to_array 来获取,如果我们需要获取的数据量比较多,则会需要产生多次获取数据的操作。

所以,如果我们知道需要获取大批量数据的时候,可以指定一个比较大的 batchSize 从而减少网络往返次数。

  • 只获取需要的字段(针对大批量获取数据的情况)

主要目的:减少带宽占用,以及处理过程中可以减少内存占用。

  • 使用覆盖索引

覆盖索引的使用类似 MySQL,因为 MongoDB 的索引也是 B+ 树。如果我们在查询的时候只需要某一两个字段,可以在索引上跟查询条件组合在一起建立一个联合索引。

  • 使用 find 代替 aggregate

当我们尝试设置一个 null 变量的属性的时候,会报这个错:

1
2
$a = null;
$a->b = 1; // Creating default object from empty value

其实除了 null,值为 空字符串 或者 false 的变量,如果想设置其属性的时候也会报这个错:

1
2
$a = ''; // 或者 $a = false;
$a->b = 1; // Creating default object from empty value

这种情况只在通过 __get 获取对象属性的时候才会出现。之前也写过一篇类似的文章,但是那里阐述的报错原因没那么直观,本文指出了 laravel 里面出现这个报错的常见原因。

在 Laravel 中,有时候我们需要修改关联的某一个属性的值。这个时候我们一般会使用 $a->b->c = 1; 这种形式。

这种形式一般情况下是没有问题的,但是如果在关联的值是 null 的时候,也就是没有对应的关联的时候就会报错。

而这个报错往往让人摸不着头脑,事实上原因很简单,只是设置了 null 的属性。

报错信息

1
Indirect modification of overloaded property A::$b has no effect

测试源码(lumen)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php

use Illuminate\Database\Eloquent\Model;

require_once __DIR__ . '/bootstrap/app.php';

app()->boot();

/**
* Class A
* @property-read B b
*/
class A extends Model
{
protected $table = 'as';

public function b()
{
return $this->hasOne(B::class);
}
}

class B extends Model
{
protected $table = 'bs';
}

/** @var A $a */
$a = A::query()->first();

dump($a->b); // null
$a->b->id = 2; // Indirect modification of overloaded property A::$b has no effect

解决方法

1、如果我们允许这个值为 null,并且需要在其不为 null 的时候设置其值,则可以用 optional 方法:

1
optional($a->b)->id = 2;

2、如果业务上是不会出现 null 的情况的,直接让它异常就好了,早点发现。