Month: 3月 2019

Redis 与 Pika scan 性能对比

Redis 是后端常用的键值数据库。Pika 是 360 出品的一款与 Redis 协议几乎兼容的数据库。与 Redis 不同的是,Pika 基于硬盘,使用 RocksDB 作为引擎,从容量上来说,比基于内存的 Redis 大了不少,而且在性能上也能满足一般需求。

我们知道,在 Redis 中,keys * 这个操作仅限于在本地调试使用,千万不能用于线上,因为这会遍历整个数据库,可能引起数据库长时间无响应,甚至崩溃。在线上服务器,如果想要查找某个模式的键,可以使用 scan 命令。比如说要查找 user: 前缀的所有键,可以使用 scan 0 user:* 命令。然而如果服务器上的键非常多的话,虽然不会卡死服务器了,但是这个过程依然会很漫长。

Redis 是使用 hash table 实现的,所以 scan 命令其实也是遍历所有键,拿到每个键再做过滤,而不能直接读取符合对应 pattern 的键。我们使用下面的代码来验证一下 redis scan 的性能。

from redis import Redis
from uuid import uuid4
import time

def gen(r):
    for i in range(10000000):
        r.set(str(uuid4()), 1)
    r.set("user:1", "bar")

def scan(r):
    start = time.time()
    for key in r.scan_iter("user:*"):
        print("user=%s" % r.get(key).decode())
        duration = time.time() - start
        print("duration for finding user is %.3f" % duration)
    duration = time.time() - start
    print("duration for full scan is %.3f" % duration)

if __name__ == "__main__":
    import sys
    port = int(sys.argv[1])
    r = Redis(port=port)
    gen(r)
    scan(r)

首先插入一千万个随机数据,然后从中查找我们的目标数据。结果如下:

-> % python3 rb.py 6379
user=bar
duration for finding user is 80.145
duration for full scan is 180.936

和我们的预期基本是相符的,也就是说 Redis 是首先遍历然后再做过滤的。

接下来我们对 Pika 做相同的实验,Pika 默认使用 9221 端口,我们只需要把端口换一下就好了:

-> % python3 rb.py 9221
user=bar
duration for finding user is 0.002
duration for full scan is 0.003

结果是令人震惊的!Pika 几乎在瞬间就完成了遍历。原因在于 Pika 使用了 RocksDB,而 RocksDB 支持 Range 操作。RocksDB 中的数据都是有序的,所以查找起来就不需要 O(n) 了,只需要二分查找,也就是 O(logN) 即可。

学习 React

当我第一次接触前端的时候,那时候流行的是后端 MVC 模式。过去写界面的方法是,把所有的结构 (html),动作 (js),样式 (css) 分开,好处是非侵入,离了谁都能工作,缺点是无法模块化。在 js 无足轻重,甚至有 noscript 这种插件的过去,显然 MVC 是最佳实践,但是到了 js 大行其道的今天,把 html/js/css 打包在一起的模块化又被提出来了。

react 中也没有模板中的 {% block xxx %} 这个概念,直接使用 props。

Hello World

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);

Jsx 中可以使用大括号插值。对于 html 中不能自闭合的标签,都可以自闭合。JSX 最终会被编译成 JavaScript.

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

// 相当于
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

一个函数就可以是一个组件。在 React 16 时代,就不要再用 class 了,统一用函数式组件就好了。

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 相当于
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

props 只有向下传递一种方式。所有的函数都必须是纯函数。使用函数作为组件的一个缺点是没有办法保存状态。

在 class 组件中,需要使用使用 setState 更新状态。不能使用 += 类似的操作符,setState 是异步的,因此要传递回调函数。

事件

react 的事件和 HTML 的不同。所有属性是 camelCase 的;不能通过 return false 来阻止事件,必须调用 e.preventDefault

<button onClick={activateLasers}>
  Activate Lasers
</button>

一般情况下,在 React 中是不需要调用 addEventListener

条件渲染

React 是 functional 的。所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。state 是组件内部的状态。

key 是一个很关键的概念,有点像是 html 中的 ID, 用来唯一标示一个元素,因为 react 会尽可能复用元素。但是 key 不需要是全局唯一的,只需要在兄弟元素之间唯一即可。

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];

ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

组合

在 React 中不要使用继承来组织组件,而要使用组合,这也是近几年来面向对象领域的趋势。在 React 中,可以通过读取 props.children 来获取传递进来的子组件。

function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}

function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}

如果需要对子组件布局的话,可以使用命名的方式:

function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}
      </div>
      <div className="SplitPane-right">
        {props.right}
      </div>
    </div>
  );
}

function App() {
  return (
    <SplitPane left={ <Contacts /> } right={ <Chat /> } />
  );
}

另一种方式是特化,也就是类似函数的 partial

Thinking in React

React 设计哲学:https://zh-hans.reactjs.org/docs/thinking-in-react.html


  • FilterableProductTable
    • SearchBar
    • ProductTable
      • ProductCategoryRow
      • ProductRow

已经过期的一些知识

由于 JS 的 this 的坑,需要使用 public class fields。如果要向回调函数中使用参数需要这样:onClick={(e) => this.deleteRow(id, e)}

class LoggingButton extends React.Component {
  // This syntax ensures `this` is bound within handleClick.
  // Warning: this is *experimental* syntax.
  handleClick = () => {
    console.log('this is:', this);
  }
render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

如果需要传递参数的话:

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>

参考资料

  1. https://medium.com/@Zwenza/functional-vs-class-components-in-react-231e3fbd7108
  2. https://segmentfault.com/a/1190000011474522
  3. https://stackoverflow.com/questions/22876978/loop-inside-react-jsx