Lua Web快速开发指南(6) - Cache、DB介绍

"数据库"与"缓存"的基本概念

数据库与缓存是服务端开发人员的必学知识点.

数据库

"数据库"是一种信息记录、存取的虚拟标记地点的集合统称. 比如现实生活中, 我们经常会用到文件柜、书桌等等数据存取容器.

在对容器进行数据存取的时候, 我们会为每一层打上一个标签表示一种分类项. 而这种在数据库中划分子分类形成了的概念. 这就是我们通常所说的结构化数据库.

由于通常数据表之间可能会存在依赖关系, 某一(或者多)层通常可能会用于同一种用途. 这种用途将一层划分为索引表, 二层划分为分类表, 三层划分为数据表.

实现这种功能与依赖关系的数据库, 我们称之为: 关系型数据库. 它可以定义一套规范并且建立数据存取模型, 这样方便维护一整套结构化的数据信息.

每当我们需要对数据进行结构化操作(查询、增加、删除、修改)的时候, 需要在计算机中用一种通俗易懂的语言表达方式来进行助记. 这种结构化查询语言称之为SQL.

缓存

我们通常将数据存储完毕后, 能通过指定或特定的一(多)种方式对数据进行操作. 在项目开发的初期, 这并没有太大的问题.

但是随着数据量的不断增大, 在数据库的内存中已经放不下这么多数据. 我们的数据逐渐无法被加载到内存中: 只会在使用的时候才会进行(随机)读取. 而这会加大磁盘I/O.

我们知道通常磁盘的读写速度基本上会比内存读写慢几个数量级(即使是SSD), 大量请求可能瞬间将磁盘IO占满并出现数据库的CPU利用率低、内存频繁进行修改/置换等问题.

为了解决这些问题, 出现了很多解决方案: 读、写分离、分表分库等等. 虽然有了这些方案, 但是也同样回引来新的问题: 主从同步、分布式事务等问题.

"缓存"则是近十年兴起的概念, 它的本质是一份数据结构化存储在内存中的副本. 高级的缓存我们也可以将其称之为内存数据库NOSQL(非关系型)数据库.

"缓存"也是一种"另类"解决数据库问题点一种手段! 它通过丰富的数据结构扩展了数据模型的组合能力, 通过简单的使用方法与高效的连接方式提供更好数据操作方式.

"缓存"将查询、更新较为频繁的数据组成一个集合加载进内存中, 较少使用的数据序列化到磁盘内部. 高效利用内存的同时, 根据变化的情况合理更新、删除缓存.

这样的方式配合数据库的读、写分离与数据分区将数据合理的从一个数据集副本分散到多个数据集副本, 有效的减少性能问题点产生并且提升了整个业务系统的横向扩展能.

DB库

DB库是cf框架封装自MySQL 4.1协议实现的客户端连接库, 提供MySQL断线重连、SQL重试、连接池等高级特性.

Cache

Cache库是cf封装自Redis 2.0协议实现的客户端连接库, 提供Redis断线重连、命令重试、连接池等高级特性.

API学习

1. DB API

在使用下面的API之前, 请先确保已经导入库: local DB = require "DB".

1.1 DB:new(opts)

opts表的参数决定如何连接到MySQL, 表属性如下:

host - MySQL主机名或IP地址(string类型).

port - MySQL端口号(int类型).

charset - MySQL字符集设置.

database - MySQL库的名称.

username - MySQL用户账户(string类型).

password - MySQL用户密码(string类型).

max - MySQL的最大连接池大小(int类型).

这个方法返回一个新创建db对象

1.2 DB:connect()

开始连接MySQL. 连接成功返回True, 否则将会持续进行连接并且输出连接失败原因的日志.

1.3 DB:query(SQL)

数据库查询语句调用方法, SQL为string类型的的一个标准SQL语句.

返回值为ret与err. 查询成功ret为一个结果集数组, 在发生错误时未nil, err为错误信息.

2. Cache API

在使用下面的API之前, 请先确保已经导入库: local Cache = require "Cache".

2.1 Cache:new(opts)

opts表的参数决定如何连接到MySQL, 表属性如下:

host - Redis主机名或IP地址(string类型).

port - Redis主机端口号(int类型).

auth - Redis主机设置的密码, 默认为:nil.

db - Redis的数据库设置.

max = 最大连接池大小(int类型).

此方法返回一个新创建的Cache对象.

2.2 Cache:connect()

开始连接Redis. 连接成功返回True, 否则将会持续进行连接并且输出连接失败原因的日志.

2.3 Cache:API(…)

Cache支持大部分的redis API, 目前测试过多API在script/test_Cache.lua文件内部都有展示.

DB库的操作流程

DB库的操作与使用流程非常简单, 其目标是简化开发人员在业务编写过程中的使用难点.

创建数据库与数据表

启动一个MySQL实例并且初始化完毕, 具体安装与初始化方法根据平台不同而不同, 这不再本文讲解范围内.

然后我们使用默认的root用户并且root账户、密码设置完毕. 这里为了演示方便, 我们将root密码设置为: 123456789.

创建一个叫cf_mall的数据库字符集编码都设为utf-8. 并且在cf_mall数据库中创建一个叫做cf_users的表, 如下所示:

  1. # 创建`cf_mall`数据库
  2. CREATE DATABASE IF NOT EXISTS `cf_mall` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
  3. # 创建`cf_users`用户表
  4. CREATE TABLE IF NOT EXISTS `cf_mall`.`cf_users` (
  5. `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  6. `age` tinyint(3) unsigned NOT NULL COMMENT '用户年龄',
  7. `name` varchar(255) NOT NULL COMMENT '用户名',
  8. `username` varchar(255) NOT NULL COMMENT '用户账户',
  9. `password` varchar(255) NOT NULL COMMENT '用户密码',
  10. `email` varchar(255) NOT NULL DEFAULT '' COMMENT '用户邮箱',
  11. PRIMARY KEY (`id`)
  12. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

以上数据库创建语句可以在一些MySQL GUI工具中直接运行.

测试DB库的API写入数据

现在, 让我们利用上面学到的API尝试将作者的信息写入进去. 同时为了避免密码原文显示, 我们需要使用crypt库的base64方法将密码进行编码.

  1. local crypt = require "crypt"
  2. local DB = require "DB"
  3. local db = DB:new {
  4. host = "localhost",
  5. port = 3306,
  6. username = 'root',
  7. password = 123456789,
  8. database = "cf_mall",
  9. charset = "utf8"
  10. }
  11. db:connect()
  12. db:query(string.format([[
  13. INSERT INTO `cf_mall`.`cf_users`
  14. (`name`, `age`, `username`, `password`, `email`)
  15. VALUE
  16. ('%s', '%s', '%s', '%s', '%s')
  17. ]],
  18. '水果糖的小铺子', '29', 'candymi', crypt.base64encode('123456789'), '869646063@qq.com')
  19. )
  20. local ret, err = db:query("SELECT * FROM `cf_mall`.`cf_users` WHERE `name` = '水果糖的小铺子'")
  21. if not ret then
  22. return print(ret, err)
  23. end
  24. return print('name:', ret[1].name, 'password:', ret[1].password, 'email:', ret[1].email)

检查问题

最后我们检查数据库内是否已经写入了我们需要存储的数据. 如果使用的SQL有语法错误导致写入失败, 请使用print检查db:query操作否出现错误.

同时我们在console控制台上可以检查是否输出了我们刚才写入到cf_users的信息.

Cache库的操作流程

Cache库拥有非常简单的使用方法, 几乎能运行所有Redis已提供的命令. 并且协议是二进制安全的(binary safe).

启动一个Redis实例

启动并运行一个redis实例. 具体安装与运行方法根据平台不同而不同, 这不再本文讲解范围内.

并且这里为了演示示例简单, 我们将不设置任何auth并且使用默认的db.

将数据加载到Redis

我们将列出目前已知的一些知名大众化语言, 然后将其写入到Redis中.

  1. local Cache = require "Cache"
  2. local cache = Cache:new {
  3. host = 'localhost',
  4. port = 6379,
  5. }
  6. cache:connect()
  7. local ok, msg = cache:rpush("languages", "C", "C++", "Java", "Golang", "Ruby", "Python", "PHP", "Lua")
  8. if not ok then
  9. return print(ok, msg)
  10. end
  11. print("当前language的总数为:", msg)

问题排查

最后, 让我们用Redis自带的命令行工具查看是否真实写入了数据.

  1. [candy@MacBookPro:~] $ redis-cli
  2. 127.0.0.1:6379> lrange languages 0 -1
  3. 1) "C"
  4. 2) "C++"
  5. 3) "Java"
  6. 4) "Golang"
  7. 5) "Ruby"
  8. 6) "Python"
  9. 7) "PHP"
  10. 8) "Lua"
  11. 127.0.0.1:6379>

可以看到数据已经写入进去. 如果发送参数错误与语法发生错误, msg将会是您排查错误的有效信息.

更多

更多API详情请参考MySQL、Redis的使用文档并且在实际开发中进行体验.

继续学习

下一章节我们将继续学习如何利用httpc库请求第三方接口