官网:https://github.com/openresty/lua-resty-mysql
mysql:new:创建mysql连接对象
语法格式:db, err = mysql:new()
Creates a MySQL connection object. In case of failures,
returns nil and a string describing the error
* 创建mysql连接对象
* 如果创建失败,返回nil、错误信息err
connect(options):连接数据库
语法格式:ok, err, errcode, sqlstate = db:connect(options)
# options可选值
* host:mysql地址
* port:mysql端口
* path:he path of the unix socket file listened by the MySQL server
* database:数据库名称
* user:用户名
* password:密码
* charset:编码集
* max_packet_size:mysql最大返回的数据大小,默认1M
* ssl:是否使用ssl加密,默认false
* pool:数据库连接池,如果不设置,默认为:user:database:host:port、user:database:path
* pool_size:数据库连接池大小(针对每个worker,不是整个nginx服务器)
如果不设置backlog,总的数据库连接不会有限制
如果不设置,并且backlog没有设置,则不会创建连接池
如果不设置,但是设置了backlog,poolsize默认为lua_socket_pool_size
当连接不够时,超过存活时间的连接会断开,断开后可以建立新的连接
* backlog:设置后可以限制总的数据库连接
If specified, this module will limit the total number of opened
connections for this pool. No more connections than pool_size can
be opened for this pool at any time. If the connection pool is full,
subsequent connect operations will be queued into a queue equal to
this option's value (the "backlog" queue). If the number of queued
connect operations is equal to backlog, subsequent connect operations
will fail and return nil plus the error string "too many waiting
connect operations"
* 如果设置了,可以限制连接池总的连接数
* 连接数满了后,不能创建新的连接,会进入队列排队
* 队列数满了,会直接报错,返回nil,错误信息:too many waiting connect operations
* compact_arrays:true、false(默认)
when this option is set to true, then the query and read_result
methods will return the array-of-arrays structure for the resultset,
rather than the default array-of-hashes structure
* 如果设置为true,返回的数据结构:数组嵌套数组
* 默认返回hash数组结构
set_timeout:设置超时时间
语法格式:db:set_timeout(time)
Sets the timeout (in ms) protection for subsequent
operations, including the connect method
* 超时时间,单位毫秒
set_keepalive:设置空闲连接存活时间
语法格式:ok, err = db:set_keepalive(max_idle_timeout, pool_size)
Puts the current MySQL connection immediately into the ngx_lua
cosocket connection pool.
* 将当前连接放到连接池
You can specify the max idle timeout (in ms) when the connection
is in the pool and the maximal size of the pool every nginx worker process.
* 设置最大空闲时间、连接池大小(针对每个nginx worker)
In case of success, returns 1. In case of errors, returns nil with a string describing the error
* 设置成功,返回1
* 设置出错,返回nil,错误信息err
get_reused_times:判断当前连接重复使用次数
语法格式:times, err = db:get_reused_times()
This method returns the (successfully) reused times for the current connection.
In case of error, it returns nil and a string describing the error.
* 获取当前连接的重复使用次数
* 如果出错,返回nil,错误信息err
If the current connection does not come from the built-in connection pool, then
this method always returns 0, that is, the connection has never been reused (yet).
If the connection comes from the connection pool, then the return value is always
non-zero. So this method can also be used to determine if the current connection
comes from the pool
* 如果当前连接不来自于连接池,返回0
* 如果当前连接来自连接池,返回一个非0值
* 这方法可用来判断当前连接是不是来自于连接池
close:关闭连接
语法格式:ok, err = db:close()
Closes the current mysql connection and returns the status.
* 关闭数据库连接,返回状态
In case of success, returns 1. In case of errors, returns
nil with a string describing the error
* 关闭成功,返回1
* 关闭失败,返回nil、错误信息err
send_query:发送查询语句,不需要等待响应结果
语法格式:bytes, err = db:send_query(query)
Sends the query to the remote MySQL server without waiting for its replies.
* 向服务端发送查询语句,不需要等待响应结果
Returns the bytes successfully sent out in success and otherwise returns
nil and a string describing the error.
* 如果发送成功,返回发送成功的响应数据
* 如果发送失败,返回nil、错误信息err
You should use the read_result method to read the MySQL replies afterwards
* 使用read_result读取响应结果
read_result:读取响应结果
# 语法格式
res, err, errcode, sqlstate = db:read_result()
res, err, errcode, sqlstate = db:read_result(nrows)
* 读取服务端第一个返回的结果(多语句查询会返回多个结果)
# 结果示例
* 查询语句
{
{ name = "Bob", age = 32, phone = ngx.null },
{ name = "Marry", age = 18, phone = "10666372"}
}
* 插入语句
{
insert_id = 0,
server_status = 2,
warning_count = 1,
affected_rows = 32,
message = nil
}
if more results are following the current result, a second err return
value will be given the string again. One should always check this
(second) return value and if it is again, then she should call this
method again to retrieve more results. This usually happens when the
original query contains multiple statements (separated by semicolon
in the same query string) or calling a MySQL procedure. See also
Multi-Resultset Support.
* 如果返回多个结果,err字符串为again,可调用read_result继续读取结果
* 多结果查询:Multi-Resultset
In case of errors, this method returns at most 4 values: nil, err,
errcode, and sqlstate. The err return value contains a string describing
the error, the errcode return value holds the MySQL error code (a
numerical value), and finally, the sqlstate return value contains the
standard SQL error code that consists of 5 characters. Note that, the
errcode and sqlstate might be nil if MySQL does not return them.
* 如果出错了,方法最多返回4个结果:nil, err, errcode, and sqlstate
* err:错误信息
* errcode:错误信息状态码
* sqlstate:sql错误码
* 如果mysql不返回errcode、sqlstate,errcode、sqlstate为nil
The optional argument nrows can be used to specify an approximate
number of rows for the result set. This value can be used to
pre-allocate space in the resulting Lua table for the result set.
By default, it takes the value 4
* nrows:限制结果集的数量,默认为4
query:send_query、read_result的简化
# 语法格式
res, err, errcode, sqlstate = db:query(query)
res, err, errcode, sqlstate = db:query(query, nrows)
This is a shortcut for combining the send_query call and the
first read_result call.
* send_query、read_result的简化
You should always check if the err return value is again in case
of success because this method will only call read_result only
once for you. See also Multi-Resultset Support.
* 检查err的值是否是again
* read_result只会读取服务端第一次返回的结果
server_ver:服务端版本
语法格式:str = db:server_ver()
Returns the MySQL server version string, like "5.1.64".
* 返回服务端的版本
You should only call this method after successfully
connecting to a MySQL server, otherwise nil will be returned
* 需要在连接上服务端后调用,否则会返回nil
set_compact_arrays:压缩数据集
语法格式:db:set_compact_arrays(boolean)
Sets whether to use the "compact-arrays" structure for
the resultsets returned by subsequent queries. See the
compact_arrays option for the connect method for more details
* 是否压缩返回的数据结构
* 可参照connect的参数:compact_arrays
防止sql注入
local name = ngx.unescape_uri(ngx.var.arg_name)
local quoted_name = ngx.quote_sql_str(name)
local sql = "select * from users where name = " .. quoted_name
多结果操作
local cjson = require "cjson"
local mysql = require "resty.mysql"
local db = mysql:new()
local ok, err, errcode, sqlstate = db:connect({
host = "127.0.0.1",
port = 3306,
database = "world",
user = "monty",
password = "pass"})
if not ok then
ngx.log(ngx.ERR, "failed to connect: ", err, ": ", errcode, " ", sqlstate)
return ngx.exit(500)
end
res, err, errcode, sqlstate = db:query("select 1; select 2; select 3;")
if not res then
ngx.log(ngx.ERR, "bad result #1: ", err, ": ", errcode, ": ", sqlstate, ".")
return ngx.exit(500)
end
ngx.say("result #1: ", cjson.encode(res))
local i = 2
while err == "again" do
res, err, errcode, sqlstate = db:read_result()
if not res then
ngx.log(ngx.ERR, "bad result #", i, ": ", err, ": ", errcode, ": ", sqlstate, ".")
return ngx.exit(500)
end
ngx.say("result #", i, ": ", cjson.encode(res))
i = i + 1
end
local ok, err = db:set_keepalive(10000, 50)
if not ok then
ngx.log(ngx.ERR, "failed to set keepalive: ", err)
ngx.exit(500)
end
# 返回结果
result #1: [{"1":"1"}]
result #2: [{"2":"2"}]
result #3: [{"3":"3"}]
结果调试:将返回结果编码为字符串后输出
local cjson = require "cjson"
...
local res, err, errcode, sqlstate = db:query("select * from cats")
if res then
print("res: ", cjson.encode(res))
end
错误日志
By default the underlying ngx_lua module does error logging when
socket errors happen. If you are already doing proper error handling
in your own Lua code, then you are recommended to disable this automatic
error logging by turning off ngx_lua's lua_socket_log_errors directive
* 默认情况下,ngx_lua会记录错误日志
* 如果代码中已经有了错误处理,可关闭ngx_lua错误日志记录
# 关闭ngx_lua错误日志记录
lua_socket_log_errors off;
lua-resty-mysql使用限制
This library cannot be used in code contexts like
init_by_lua*, set_by_lua*, log_by_lua*, and header_filter_by_lua*
where the ngx_lua cosocket API is not available
* 不能在init_by_lua*, set_by_lua*, log_by_lua*, and header_filter_by_lua*使用mysql
* 在这些模块中,cosocket API不能使用
The resty.mysql object instance cannot be stored in a Lua variable
at the Lua module level, because it will then be shared by all the
concurrent requests handled by the same nginx worker process
* resty.mysql对象不能在lua模块级别的变量中存储
* 会被nginx worker中的所有请求共享
创建mysql
docker run -it -d --net fixed --ip 172.18.0.61 -p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 --name mysql4 mysql
数据库操作
# 修改认证方式
mysql> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
Query OK, 0 rows affected (0.01 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
# 创建数据库
mysql> create database lihu;
Query OK, 1 row affected (0.01 sec)
default.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/local/openresty/nginx/html;
index index.html index.htm;
}
location /test {
content_by_lua_block {
local mysql = require 'resty.mysql';
local db, err = mysql:new();
if not db then
ngx.say("mysql创建失败", err);
end
db:set_timeout(1000);
local res, err, errcode, sqlstate = db:connect({
host = "172.18.0.61", port = 3306, database = "lihu",
user = "root", password = "123456"
});
if not res then
ngx.say("连接出错", err, errcode, sqlstate);
end
ngx.say("创建表: test")
res, err, errcode, sqlstate = db:query("create table test("
.."id int primary key auto_increment, "
.."name varchar(20)"
..")");
if not res then
ngx.say("表创建失败", err);
end
ngx.say("\n数据插入: ", "insert into test(name) values('gtlx'),('gzw')");
res, err, errcode, sqlstate = db:query(
"insert into test(name) values('gtlx'),('hzw')"
);
if not res then
ngx.say("数据插入失败", err);
end
ngx.say("\n数据查询: ", "select * from test");
res, err, errcode, sqlstate = db:query(
"select * from test"
);
if not res then
ngx.say("数据查询失败", err);
end
local cjson = require 'cjson';
ngx.say("返回的数据类型:", type(res));
ngx.say("查询的数据为:", cjson.encode(res));
ngx.say("\n数据更新: ","update test set name = 'gtls' where id = 2");
res, err, errcode, sqlstate = db:query(
"update test set name = 'gtls' where id = 2"
);
if not res then
ngx.say("数据更新失败", err);
end
res, err, errcode, sqlstate = db:query(
"select * from test"
);
if not res then
ngx.say("数据查询失败", err);
end
ngx.say("更新后查询的数据为:", cjson.encode(res));
ngx.say("\n数据删除: ", "delete from test where id = 2");
res, err, errcode, sqlstate = db:query(
"delete from test where id = 2"
);
if not res then
ngx.say("数据删除失败", err);
end
res, err, errcode, sqlstate = db:query(
"select * from test"
);
if not res then
ngx.say("数据查询失败", err);
end
ngx.say("删除后查询的数据为:", cjson.encode(res));
}
}
location /insert {
content_by_lua_block {
local mysql = require 'resty.mysql';
local db, err = mysql:new();
if not db then
ngx.say("mysql创建失败", err);
end
db:set_timeout(1000);
local res, err, errcode, sqlstate = db:connect({
host = "172.18.0.61", port = 3306, database = "lihu",
user = "root", password = "123456"
});
if not res then
ngx.say("连接出错", err, errcode, sqlstate);
end
for i=1,9 do
db:query("insert into test(name) values('gtlx')");
end
}
}
location /test2 {
content_by_lua_block {
local mysql = require 'resty.mysql';
local db, err = mysql:new();
if not db then
ngx.say("mysql创建失败", err);
end
db:set_timeout(1000);
local res, err, errcode, sqlstate = db:connect({
host = "172.18.0.61", port = 3306, database = "lihu",
user = "root", password = "123456"
});
if not res then
ngx.say("连接出错", err, errcode, sqlstate);
end
ngx.say("数据查询: ","select * from test limit 1");
res, err, errcode, sqlstate = db:query(
"select * from test limit 1"
);
if not res then
ngx.say("数据查询失败", err);
end
local cjson = require 'cjson';
ngx.say("查询的数据为:", cjson.encode(res));
ngx.say("\n数据查询: ", "select * from test", " nrows = 1" );
res, err, errcode, sqlstate = db:query(
"select * from test", 1
);
if not res then
ngx.say("数据查询失败", err);
end
ngx.say("查询的数据为:", cjson.encode(res));
--for i=1,9 do
-- db:query("insert into test(name) values('gtlx')")
--end
ngx.say("\n多结果数据查询: ", "select * from test where id = 1; select * from test where id = 3");
res, err, errcode, sqlstate = db:query(
"select * from test where id = 1; select * from test where id = 3"
);
if not res then
ngx.say("数据查询失败", err);
end
ngx.say("查询的数据为: result[1] ==> ", cjson.encode(res));
local index = 2;
while err == 'again' do
res, err, errcode, sqlstate = db:read_result();
if not res then
ngx.say("数据查询失败", err);
end
ngx.say("查询的数据为: result[",index,"] ==> ", cjson.encode(res));
end
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/local/openresty/nginx/html;
}
}
创建容器
docker run -it -d --net fixed --ip 172.18.0.62 -p 5002:80 \
-v /Users/huli/lua/openresty/mysql/default.conf:/etc/nginx/conf.d/default.conf \
--name resty-mysql lihu12344/openresty
使用测试
huli@hudeMacBook-Pro mysql % curl localhost:5002/test
创建表: test
数据插入: insert into test(name) values('gtlx'),('gzw')
数据查询: select * from test
返回的数据类型:table
查询的数据为:[{"id":1,"name":"gtlx"},{"id":2,"name":"hzw"}]
数据更新: update test set name = 'gtls' where id = 2
更新后查询的数据为:[{"id":1,"name":"gtlx"},{"id":2,"name":"gtls"}]
数据删除: delete from test where id = 2
删除后查询的数据为:[{"id":1,"name":"gtlx"}]
# 插入数据
huli@hudeMacBook-Pro mysql % curl localhost:5002/insert
# 数据查询测试
huli@hudeMacBook-Pro mysql % curl localhost:5002/test2
数据查询: select * from test limit 1
查询的数据为:[{"id":1,"name":"gtlx"}]
# nrows不能限制查询的条数
数据查询: select * from test nrows = 1
查询的数据为:[{"id":1,"name":"gtlx"},{"id":3,"name":"gtlx"},{"id":4,"name":"gtlx"},{"id":5,"name":"gtlx"},{"id":6,"name":"gtlx"},{"id":7,"name":"gtlx"},{"id":8,"name":"gtlx"},{"id":9,"name":"gtlx"},{"id":10,"name":"gtlx"},{"id":11,"name":"gtlx"}]
多结果数据查询: select * from test where id = 1; select * from test where id = 3
查询的数据为: result[1] ==> [{"id":1,"name":"gtlx"}]
查询的数据为: result[2] ==> [{"id":3,"name":"gtlx"}]