“ 数据库 ” 与 ” 缓存 ” 的基本概念
数据库与缓存是服务端开发人员的必学知识点.
数据库
“ 数据库 ” 是一种信息记录、存取的虚拟标记地点的集合统称. 比如现实生活中, 我们经常会用到文件柜、书桌等等数据存取容器.
在对容器进行数据存取的时候, 我们会为每一层打上一个标签表示一种分类项. 而这种在 数据库
中划分子分类形成了 表
的概念. 这就是我们通常所说的 结构化数据库
.
由于通常数据表之间可能会存在依赖关系, 某一 (或者多) 层通常可能会用于同一种用途. 这种用途将一层划分为索引表, 二层划分为分类表, 三层划分为数据表.
实现这种功能与依赖关系的数据库, 我们称之为: 关系型数据库
. 它可以定义一套规范并且建立数据存取模型, 这样方便维护一整套结构化的数据信息.
每当我们需要对数据进行结构化操作 (查询、增加、删除、修改) 的时候, 需要在计算机中用一种通俗易懂的 语言表达方式
来进行助记. 这种结构化查询语言称之为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
的表, 如下所示:
# 创建 `cf_mall` 数据库
CREATE DATABASE IF NOT EXISTS `cf_mall` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
# 创建 `cf_users` 用户表
CREATE TABLE IF NOT EXISTS `cf_mall`.`cf_users` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户 ID',
`age` tinyint(3) unsigned NOT NULL COMMENT '用户年龄',
`name` varchar(255) NOT NULL COMMENT '用户名',
`username` varchar(255) NOT NULL COMMENT '用户账户',
`password` varchar(255) NOT NULL COMMENT '用户密码',
`email` varchar(255) NOT NULL DEFAULT ''COMMENT' 用户邮箱 ',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
以上数据库创建语句可以在一些 MySQL GUI 工具中直接运行.
测试 DB 库的 API 写入数据
现在, 让我们利用上面学到的 API 尝试将作者的信息写入进去. 同时为了避免密码原文显示, 我们需要使用 crypt 库的 base64 方法将密码进行编码.
local crypt = require "crypt"
local DB = require "DB"
local db = DB:new {
host = "localhost",
port = 3306,
username = 'root',
password = 123456789,
database = "cf_mall",
charset = "utf8"
}
db:connect()
db:query(string.format([[
INSERT INTO `cf_mall`.`cf_users`
(`name`, `age`, `username`, `password`, `email`)
VALUE
('%s', '%s', '%s', '%s', '%s')
]],
'水果糖的小铺子', '29', 'candymi', crypt.base64encode('123456789'), '869646063@qq.com')
)
local ret, err = db:query("SELECT * FROM `cf_mall`.`cf_users` WHERE `name` =' 水果糖的小铺子 '")
if not ret then
return print(ret, err)
end
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 中.
local Cache = require "Cache"
local cache = Cache:new {
host = 'localhost',
port = 6379,
}
cache:connect()
local ok, msg = cache:rpush("languages", "C", "C++", "Java", "Golang", "Ruby", "Python", "PHP", "Lua")
if not ok then
return print(ok, msg)
end
print("当前 language 的总数为:", msg)
问题排查
最后, 让我们用 Redis 自带的命令行工具查看是否真实写入了数据.
[candy@MacBookPro:~] $ redis-cli
127.0.0.1:6379> lrange languages 0 -1
1) "C"
2) "C++"
3) "Java"
4) "Golang"
5) "Ruby"
6) "Python"
7) "PHP"
8) "Lua"
127.0.0.1:6379>
可以看到数据已经写入进去. 如果发送参数错误与语法发生错误, msg 将会是您排查错误的有效信息.
更多
更多 API 详情请参考 MySQL、Redis 的使用文档并且在实际开发中进行体验.
继续学习
下一章节我们将继续学习如何利用 httpc 库请求第三方接口