For the first iteration of Vector search, we will implement HNSW algorithm.
The implementation will only support Euclidean distance initially.
Basic plan:
Graph construction will be done according to HNSW paper.
Storage wise, we'll store the graph as part of a subtable (MDEV-33404).
The table's definition will be something along these lines:
CREATETABLE i (
levelint unsigned notnull,
src varbinary(255) notnull,
dst varbinary(255) notnull,
index (level,src),
index (level,dst));
For each link in the graph, there will be a corresponding entry in the table.
src and dst will store handler::position, a quick link to the actual vector blob in the main table.
The index (level,src) will allow for quick jumping between nodes.
To go deeper in search, one just needs to decrement the level and search using the same "src" value.
If src is found on level n, then it is also found on level n - 1 and so on. Level 0 is the base level with all the nodes.
Performance considerations:
Storing the vector in the subtable might be required. Looking up the blob value in the base table might be too costly.
Got it, thanks! So I built bb-11.6-MDEV-32887-vector, and the build process seemed to go fine. In the log I can see at startup:
2024-07-03 15:45:53 0 [Note] Starting MariaDB 11.6.0-MariaDB source revision 77a016686ec2a2617dd6489a756b1f9f11a78d9f as process 27924
Which seems to be the latest commit on that branch as far as I can tell, so it looks like I've gotten the proper source. But when I run "ALTER TABLE data ADD COLUMN embedding VECTOR(100);", I get "SQL Error (4161): Unknown data type: 'VECTOR'". Is there something else I need to enable to test?
BJ Quinn
added a comment - Got it, thanks! So I built bb-11.6- MDEV-32887 -vector, and the build process seemed to go fine. In the log I can see at startup:
2024-07-03 15:45:53 0 [Note] Starting MariaDB 11.6.0-MariaDB source revision 77a016686ec2a2617dd6489a756b1f9f11a78d9f as process 27924
Which seems to be the latest commit on that branch as far as I can tell, so it looks like I've gotten the proper source. But when I run "ALTER TABLE data ADD COLUMN embedding VECTOR(100);", I get "SQL Error (4161): Unknown data type: 'VECTOR'". Is there something else I need to enable to test?
No, nothing. VECTOR data type is MDEV-33410, and it's open no work done on it yet.
We're going to implement it, of course, but it's not the first priority — it's a convenience feature that helps to avoid mistakes, but an application does not really need it, one can store and search embedding without a dedicated data type. We're prioritizing features that an application cannot work without. Functions VEC_FromText() and VEC_AsText() are also not a priority.
See the test file mysql-test/main/vector.test — that's how one uses it now, store in blob, insert as binary.
In python I do it like
Sergei Golubchik
added a comment - No, nothing. VECTOR data type is MDEV-33410 , and it's open no work done on it yet.
We're going to implement it, of course, but it's not the first priority — it's a convenience feature that helps to avoid mistakes, but an application does not really need it, one can store and search embedding without a dedicated data type. We're prioritizing features that an application cannot work without. Functions VEC_FromText() and VEC_AsText() are also not a priority.
See the test file mysql-test/main/vector.test — that's how one uses it now, store in blob, insert as binary.
In python I do it like
c.execute( 'INSERT kb (emb) VALUES (?)' , array.array( 'f' ,resp.data.embedding).tobytes())
Benchmarks indicate that using _Float16 instead of floats results in a 40-60% reduction in insertion speed and a 15-20% reduction in search speed. There is also a minor decrease in recall of less than 1%.
There are two issues with this solution (more research needed):
Converting 4-byte floats to 2-byte floats results in precision loss and a reduced range. Proportional scaling is necessary, but there is no simple method to define a proportion that works for all cases. Scaling must be done during transformation, and the best approach depends on the specific dataset and range of values.
_Float16 range is -65504 ~ 65504
If original floats and distance squares are all below this value the direct transform from float to _Float16 will work perfectly. e.g. [1, 2, 222], [0, -1, 0]
However if original floats or distance squares are all out of the range, then scaling must be done during transformation. Otherwise the distance makes no sense at all as they are out of range. e.g. [6789, 1234], [-6789, -1234]
for dataset of mnist-784-euclidean, without scaling, the distance are bigger than FLT16_MAX and recall is 0. If divided the float by 1000 during transformation, then the recall becomes 0.978.
One possible solution could be to allow for configuring a "proportion" parameter when they choose scalar quantization, which would enable the user to specify the appropriate scaling factor for their specific use case.
Another possible solution might be define the corresponding data type (half-vector) and let the users to do the scaling before inserting the data.
_Float16 requires CPU instruction set support, otherwise it will revert to float and not utilize SIMD, leading to performance issues. In the commit I’m using -mf16c but looks it could be improved further.
Hugo Wen
added a comment - - edited Hi serg , I summarize some findings regarding the scalar quantization using _Float16 that we discussed during our meeting today.
Draft commit to test _Float16 (2-byte float) in HNSW index: https://github.com/HugoWenTD/server/commit/9656b6c0d
Benchmarks indicate that using _Float16 instead of floats results in a 40-60% reduction in insertion speed and a 15-20% reduction in search speed. There is also a minor decrease in recall of less than 1%.
There are two issues with this solution (more research needed):
Converting 4-byte floats to 2-byte floats results in precision loss and a reduced range. Proportional scaling is necessary, but there is no simple method to define a proportion that works for all cases. Scaling must be done during transformation, and the best approach depends on the specific dataset and range of values.
_Float16 range is -65504 ~ 65504
If original floats and distance squares are all below this value the direct transform from float to _Float16 will work perfectly. e.g. [1, 2, 222], [0, -1, 0]
However if original floats or distance squares are all out of the range, then scaling must be done during transformation. Otherwise the distance makes no sense at all as they are out of range. e.g. [6789, 1234], [-6789, -1234]
for dataset of mnist-784-euclidean, without scaling, the distance are bigger than FLT16_MAX and recall is 0. If divided the float by 1000 during transformation, then the recall becomes 0.978.
One possible solution could be to allow for configuring a "proportion" parameter when they choose scalar quantization, which would enable the user to specify the appropriate scaling factor for their specific use case.
Another possible solution might be define the corresponding data type (half-vector) and let the users to do the scaling before inserting the data.
_Float16 requires CPU instruction set support, otherwise it will revert to float and not utilize SIMD, leading to performance issues. In the commit I’m using -mf16c but looks it could be improved further.
People
Vicențiu Ciorbaru
Sergei Golubchik
Votes:
3Vote for this issue
Watchers:
12Start watching this issue
Dates
Created:
Updated:
Resolved:
Git Integration
Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.
{"report":{"fcp":935.1999998092651,"ttfb":263.90000009536743,"pageVisibility":"visible","entityId":127813,"key":"jira.project.issue.view-issue","isInitial":true,"threshold":1000,"elementTimings":{},"userDeviceMemory":8,"userDeviceProcessors":64,"apdex":0.5,"journeyId":"2cc4d329-94a6-4e10-809f-ad81a3284acc","navigationType":0,"readyForUser":1049.2999997138977,"redirectCount":0,"resourceLoadedEnd":1084.1999998092651,"resourceLoadedStart":271.19999980926514,"resourceTiming":[{"duration":93.09999990463257,"initiatorType":"link","name":"https://jira.mariadb.org/s/2c21342762a6a02add1c328bed317ffd-CDN/lu2cib/820016/12ta74/0a8bac35585be7fc6c9cc5a0464cd4cf/_/download/contextbatch/css/_super/batch.css","startTime":271.19999980926514,"connectEnd":0,"connectStart":0,"domainLookupEnd":0,"domainLookupStart":0,"fetchStart":271.19999980926514,"redirectEnd":0,"redirectStart":0,"requestStart":0,"responseEnd":364.2999997138977,"responseStart":0,"secureConnectionStart":0},{"duration":93.89999961853027,"initiatorType":"link","name":"https://jira.mariadb.org/s/7ebd35e77e471bc30ff0eba799ebc151-CDN/lu2cib/820016/12ta74/494e4c556ecbb29f90a3d3b4f09cb99c/_/download/contextbatch/css/jira.browse.project,project.issue.navigator,jira.view.issue,jira.general,jira.global,atl.general,-_super/batch.css?agile_global_admin_condition=true&jag=true&jira.create.linked.issue=true&slack-enabled=true&whisper-enabled=true","startTime":271.40000009536743,"connectEnd":0,"connectStart":0,"domainLookupEnd":0,"domainLookupStart":0,"fetchStart":271.40000009536743,"redirectEnd":0,"redirectStart":0,"requestStart":0,"responseEnd":365.2999997138977,"responseStart":0,"secureConnectionStart":0},{"duration":234.59999990463257,"initiatorType":"script","name":"https://jira.mariadb.org/s/0917945aaa57108d00c5076fea35e069-CDN/lu2cib/820016/12ta74/0a8bac35585be7fc6c9cc5a0464cd4cf/_/download/contextbatch/js/_super/batch.js?locale=en","startTime":271.59999990463257,"connectEnd":271.59999990463257,"connectStart":271.59999990463257,"domainLookupEnd":271.59999990463257,"domainLookupStart":271.59999990463257,"fetchStart":271.59999990463257,"redirectEnd":0,"redirectStart":0,"requestStart":367.2999997138977,"responseEnd":506.19999980926514,"responseStart":388.19999980926514,"secureConnectionStart":271.59999990463257},{"duration":382,"initiatorType":"script","name":"https://jira.mariadb.org/s/2d8175ec2fa4c816e8023260bd8c1786-CDN/lu2cib/820016/12ta74/494e4c556ecbb29f90a3d3b4f09cb99c/_/download/contextbatch/js/jira.browse.project,project.issue.navigator,jira.view.issue,jira.general,jira.global,atl.general,-_super/batch.js?agile_global_admin_condition=true&jag=true&jira.create.linked.issue=true&locale=en&slack-enabled=true&whisper-enabled=true","startTime":271.7999997138977,"connectEnd":271.7999997138977,"connectStart":271.7999997138977,"domainLookupEnd":271.7999997138977,"domainLookupStart":271.7999997138977,"fetchStart":271.7999997138977,"redirectEnd":0,"redirectStart":0,"requestStart":368.2999997138977,"responseEnd":653.7999997138977,"responseStart":406.90000009536743,"secureConnectionStart":271.7999997138977},{"duration":139,"initiatorType":"script","name":"https://jira.mariadb.org/s/a9324d6758d385eb45c462685ad88f1d-CDN/lu2cib/820016/12ta74/c92c0caa9a024ae85b0ebdbed7fb4bd7/_/download/contextbatch/js/atl.global,-_super/batch.js?locale=en","startTime":272,"connectEnd":272,"connectStart":272,"domainLookupEnd":272,"domainLookupStart":272,"fetchStart":272,"redirectEnd":0,"redirectStart":0,"requestStart":370.69999980926514,"responseEnd":411,"responseStart":409.2999997138977,"secureConnectionStart":272},{"duration":138.69999980926514,"initiatorType":"script","name":"https://jira.mariadb.org/s/d41d8cd98f00b204e9800998ecf8427e-CDN/lu2cib/820016/12ta74/1.0/_/download/batch/jira.webresources:calendar-en/jira.webresources:calendar-en.js","startTime":272.09999990463257,"connectEnd":272.09999990463257,"connectStart":272.09999990463257,"domainLookupEnd":272.09999990463257,"domainLookupStart":272.09999990463257,"fetchStart":272.09999990463257,"redirectEnd":0,"redirectStart":0,"requestStart":370.7999997138977,"responseEnd":410.7999997138977,"responseStart":408.59999990463257,"secureConnectionStart":272.09999990463257},{"duration":122.90000009536743,"initiatorType":"script","name":"https://jira.mariadb.org/s/d41d8cd98f00b204e9800998ecf8427e-CDN/lu2cib/820016/12ta74/1.0/_/download/batch/jira.webresources:calendar-localisation-moment/jira.webresources:calendar-localisation-moment.js","startTime":272.2999997138977,"connectEnd":272.2999997138977,"connectStart":272.2999997138977,"domainLookupEnd":272.2999997138977,"domainLookupStart":272.2999997138977,"fetchStart":272.2999997138977,"redirectEnd":0,"redirectStart":0,"requestStart":373.7999997138977,"responseEnd":395.19999980926514,"responseStart":394.09999990463257,"secureConnectionStart":272.2999997138977},{"duration":97.7999997138977,"initiatorType":"link","name":"https://jira.mariadb.org/s/b04b06a02d1959df322d9cded3aeecc1-CDN/lu2cib/820016/12ta74/a2ff6aa845ffc9a1d22fe23d9ee791fc/_/download/contextbatch/css/jira.global.look-and-feel,-_super/batch.css","startTime":272.5,"connectEnd":0,"connectStart":0,"domainLookupEnd":0,"domainLookupStart":0,"fetchStart":272.5,"redirectEnd":0,"redirectStart":0,"requestStart":0,"responseEnd":370.2999997138977,"responseStart":0,"secureConnectionStart":0},{"duration":140.90000009536743,"initiatorType":"script","name":"https://jira.mariadb.org/rest/api/1.0/shortcuts/820016/47140b6e0a9bc2e4913da06536125810/shortcuts.js?context=issuenavigation&context=issueaction","startTime":272.69999980926514,"connectEnd":272.69999980926514,"connectStart":272.69999980926514,"domainLookupEnd":272.69999980926514,"domainLookupStart":272.69999980926514,"fetchStart":272.69999980926514,"redirectEnd":0,"redirectStart":0,"requestStart":380.7999997138977,"responseEnd":413.59999990463257,"responseStart":412.19999980926514,"secureConnectionStart":272.69999980926514},{"duration":104.5,"initiatorType":"link","name":"https://jira.mariadb.org/s/3ac36323ba5e4eb0af2aa7ac7211b4bb-CDN/lu2cib/820016/12ta74/d176f0986478cc64f24226b3d20c140d/_/download/contextbatch/css/com.atlassian.jira.projects.sidebar.init,-_super,-project.issue.navigator,-jira.view.issue/batch.css?jira.create.linked.issue=true","startTime":272.7999997138977,"connectEnd":0,"connectStart":0,"domainLookupEnd":0,"domainLookupStart":0,"fetchStart":272.7999997138977,"redirectEnd":0,"redirectStart":0,"requestStart":0,"responseEnd":377.2999997138977,"responseStart":0,"secureConnectionStart":0},{"duration":140.2999997138977,"initiatorType":"script","name":"https://jira.mariadb.org/s/5d5e8fe91fbc506585e83ea3b62ccc4b-CDN/lu2cib/820016/12ta74/d176f0986478cc64f24226b3d20c140d/_/download/contextbatch/js/com.atlassian.jira.projects.sidebar.init,-_super,-project.issue.navigator,-jira.view.issue/batch.js?jira.create.linked.issue=true&locale=en","startTime":273,"connectEnd":273,"connectStart":273,"domainLookupEnd":273,"domainLookupStart":273,"fetchStart":273,"redirectEnd":0,"redirectStart":0,"requestStart":381.09999990463257,"responseEnd":413.2999997138977,"responseStart":411.09999990463257,"secureConnectionStart":273},{"duration":430.59999990463257,"initiatorType":"script","name":"https://jira.mariadb.org/s/d41d8cd98f00b204e9800998ecf8427e-CDN/lu2cib/820016/12ta74/1.0/_/download/batch/jira.webresources:bigpipe-js/jira.webresources:bigpipe-js.js","startTime":281.40000009536743,"connectEnd":281.40000009536743,"connectStart":281.40000009536743,"domainLookupEnd":281.40000009536743,"domainLookupStart":281.40000009536743,"fetchStart":281.40000009536743,"redirectEnd":0,"redirectStart":0,"requestStart":700.1999998092651,"responseEnd":712,"responseStart":711.1999998092651,"secureConnectionStart":281.40000009536743},{"duration":801.1999998092651,"initiatorType":"script","name":"https://jira.mariadb.org/s/d41d8cd98f00b204e9800998ecf8427e-CDN/lu2cib/820016/12ta74/1.0/_/download/batch/jira.webresources:bigpipe-init/jira.webresources:bigpipe-init.js","startTime":283,"connectEnd":283,"connectStart":283,"domainLookupEnd":283,"domainLookupStart":283,"fetchStart":283,"redirectEnd":0,"redirectStart":0,"requestStart":1071.6999998092651,"responseEnd":1084.1999998092651,"responseStart":1083.2999997138977,"secureConnectionStart":283},{"duration":261.2999997138977,"initiatorType":"xmlhttprequest","name":"https://jira.mariadb.org/rest/webResources/1.0/resources","startTime":638.9000000953674,"connectEnd":638.9000000953674,"connectStart":638.9000000953674,"domainLookupEnd":638.9000000953674,"domainLookupStart":638.9000000953674,"fetchStart":638.9000000953674,"redirectEnd":0,"redirectStart":0,"requestStart":869.0999999046326,"responseEnd":900.1999998092651,"responseStart":899.6999998092651,"secureConnectionStart":638.9000000953674},{"duration":316.2000002861023,"initiatorType":"script","name":"https://www.google-analytics.com/analytics.js","startTime":928.1999998092651,"connectEnd":0,"connectStart":0,"domainLookupEnd":0,"domainLookupStart":0,"fetchStart":928.1999998092651,"redirectEnd":0,"redirectStart":0,"requestStart":0,"responseEnd":1244.4000000953674,"responseStart":0,"secureConnectionStart":0}],"fetchStart":0,"domainLookupStart":0,"domainLookupEnd":0,"connectStart":0,"connectEnd":0,"requestStart":63,"responseStart":264,"responseEnd":283,"domLoading":267,"domInteractive":1186,"domContentLoadedEventStart":1186,"domContentLoadedEventEnd":1237,"domComplete":2117,"loadEventStart":2117,"loadEventEnd":2118,"userAgent":"Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)","marks":[{"name":"bigPipe.sidebar-id.start","time":1158.7999997138977},{"name":"bigPipe.sidebar-id.end","time":1159.7999997138977},{"name":"bigPipe.activity-panel-pipe-id.start","time":1160.7999997138977},{"name":"bigPipe.activity-panel-pipe-id.end","time":1163.5},{"name":"activityTabFullyLoaded","time":1256.9000000953674}],"measures":[],"correlationId":"a2a54eb183d8de","effectiveType":"4g","downlink":10,"rtt":0,"serverDuration":134,"dbReadsTimeInMs":22,"dbConnsTimeInMs":32,"applicationHash":"9d11dbea5f4be3d4cc21f03a88dd11d8c8687422","experiments":[]}}
Got it, thanks! So I built bb-11.6-MDEV-32887-vector, and the build process seemed to go fine. In the log I can see at startup:
2024-07-03 15:45:53 0 [Note] Starting MariaDB 11.6.0-MariaDB source revision 77a016686ec2a2617dd6489a756b1f9f11a78d9f as process 27924
Which seems to be the latest commit on that branch as far as I can tell, so it looks like I've gotten the proper source. But when I run "ALTER TABLE data ADD COLUMN embedding VECTOR(100);", I get "SQL Error (4161): Unknown data type: 'VECTOR'". Is there something else I need to enable to test?